赞
踩
题目链接:https://codeforces.com/contest/1478
贪心
题目大意为给数列中的数字上色,使得相同颜色的数字组成的序列位严格的递增序列,求最少需要的颜色数。
类似于导弹拦截问题,但本题给出数据已经排序好,降低了难度。考虑到数值相同的数字一定分配在不同的序列中,因此一定是不同的颜色,所以计算出出现次数最多的数字次数即可。
#include<bits/stdc++.h> #define ll long long #define next next_ using namespace std; int _,n,num[110],ans; int main(){ scanf("%d",&_); while(_--){ ans=0; scanf("%d",&n); for(int i=1;i<=n;i++) num[i]=0; for(int i=1,x;i<=n;i++){ scanf("%d",&x); num[x]++; } for(int i=1;i<=n;i++) ans=max(ans,num[i]); printf("%d\n",ans); } return 0; }
数学
题意要求判断给出的数字能否分解为若干个数字之和且这几个数字的数位上存在数字d。
对于给出的数字d,若要求判断的数字大于等于10d,则可将原数字减去10d,10d+1,10d+2…10d+9的其中一个,使得减去得到的结果的各位为d,所以若要求判断的数字大于等于10d,则一定成立,若要求判断的数字小于10d,则分解后的各个数字d只能存在个位上,判断原数字分别减去10,20…后能否被d整除即可。
#include<bits/stdc++.h> #define ll long long #define next next_ using namespace std; int _,n,d; int main(){ scanf("%d",&_); while(_--){ scanf("%d%d",&n,&d); for(int i=1,x;i<=n;i++){ scanf("%d",&x); if(x>=10*d) printf("YES\n"); else{ bool ok=false; for(int i=0;10*i<=x;i++) if((x-10*i)%d==0&&(x-10*i)) ok=true; if(ok) printf("YES\n"); else printf("NO\n"); } } } return 0; }
数学
题意要求根据给出的数列di还原原数列ai,di为ai到数列中每个数字的距离之和。
根据原数列的对称性,可以得出数列di中的数字一定是每个出现恰好2次。假设将原数列a中的数字从小到大排列,考虑数列中最大的数字,它到数列中每个数字的距离之和一定为数列中最大值与最小值之差的n/2倍,n为元素个数,可以利用对称性画图进行验证,即dn=2nan。an的相反数同理。计算出an后,将an的值剔除,余下的数字是一个相似的子问题,继续处理直到所有ai被计算出再判断ai是否满足条件即可。
#include<bits/stdc++.h> #define ll long long #define next next_ using namespace std; ll _,n,d[200010],cnt,b[200010],sum; bool ok; int main(){ scanf("%lld",&_); while(_--){ ok=true; cnt=sum=0; map<ll,ll> app; scanf("%lld",&n); for(ll i=1;i<=2*n;i++){ scanf("%lld",&d[i]); app[d[i]]++; } for(auto it=app.rbegin();it!=app.rend();it++){ if((*it).second!=2||(*it).first&1){ ok=false; break; } else{ cnt++; if(((*it).first-sum)%(2*n-2*(cnt-1))){ ok=false; break; } b[cnt]=((*it).first-sum)/(2*n-2*(cnt-1)); sum+=2*b[cnt]; } } if(!ok) printf("NO\n"); else{ for(ll i=2;i<=cnt;i++){ if(b[i]==b[i-1]){ ok=false; break; } if(b[i]<=0){ ok=false; break; } } if(ok) printf("YES\n"); else printf("NO\n"); } } return 0; }
数论GCD
题意为给出n个数,一次操作可以任选2个数x,y,并将2x-y添加到数列中,判断最后能否得到数字k。
对于任意三个数x,y,z,以x为中心,得到x+(y-x),x+2(y-x),…,x+(z-x),x+2(z-x),…,记y-x与z-x为d1,d2,即操作得到的数为x+k1d1与x+k1d2。
取k1=k2=1,对于x+d1与x+d2,我们假定d2大于d1,
选择x+d1与x+d2进行若干次次操作,得到x+(d2-kd1),选取合适的k使得结果为x+(d2%d1),记d2%d1为d3,
选择x+d3与x+d1进行一次操作,得到x+(d1%d3),记d1%d3为d4,
选择x+d4与x+d3进行一次操作…
整个过程类似辗转相除法,最后得到的结果为x+kdn,dn为d1与d2的最大公约数,即最后整个数列中的数减去x都可以被gcd(d1,d2)整除。
对于题目给定的数列,我们以a1,a2,a3为起点,计算出gcd(|a2-a1|,|a3-a2|)记为g,用a1+g替换a2与a3,再引入a4,以a1,a1+g,a4为起点继续往下推,最后得出数列中所有的数都可以表示为a1+kgcd(|a2-a1|,|a3-a2|,…,|an-an-1|),计算出gcd(|a2-a1|,|a3-a2|,…,|an-an-1|)再判断给定数减去a1能否被gcd(|a2-a1|,|a3-a2|,…,|an-an-1|)整除即可。
#include<bits/stdc++.h> #define ll long long #define next next_ using namespace std; ll _,n,k,g,a[200010]; ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } int main(){ scanf("%lld",&_); while(_--){ scanf("%lld%lld",&n,&k); for(ll i=1;i<=n;i++) scanf("%lld",&a[i]); sort(a+1,a+1+n); g=a[2]-a[1]; for(int i=3;i<=n;i++) g=gcd(g,a[i]-a[1]); if(abs(k-a[1])%g==0) printf("YES\n"); else printf("NO\n"); } return 0; }
线段树
题意为为给定初始序列与结果序列,并给出q个查询区间,每次查询后可以将相应的区间内小于一半数量的数进行更改,问是否存在相应的操作满足每次查询到的区间只包含0或1并且最终更改后的初始序列与结果序列相同。
按时间顺序,若要满足每次查询到的区间只含有0或1,则每次查询完成后开始更改序列之前区间内一定只包含0或1,若将时间逆序考虑,则每次修改区间的操作将区间内的数字全部改为0或1,从结果序列倒序出发,每次将给定区间内的数全部修改为数量较大的一方,若区间内0和1各占一半,则也不满足,最后判断得到的序列与初始序列是否相同。
#include<bits/stdc++.h> #define ll long long #define next next_ #define set set_ using namespace std; int _,n,q,a[200010],b[200010],l[200010],r[200010]; bool ok; struct tree{ int l; int r; int sum; int lz; }t[800010]; void build(int i,int l,int r){ t[i].l=l; t[i].r=r; t[i].lz=-1; if(l==r){ t[i].sum=b[l]; return; } int mid=l+r>>1; build(i<<1,l,mid); build(i<<1|1,mid+1,r); t[i].sum=t[i<<1].sum+t[i<<1|1].sum; return; } void pushdown(int i){ if(t[i].lz!=-1){ t[i<<1].sum=t[i].lz*(t[i<<1].r-t[i<<1].l+1); t[i<<1|1].sum=t[i].lz*(t[i<<1|1].r-t[i<<1|1].l+1); t[i<<1].lz=t[i].lz; t[i<<1|1].lz=t[i].lz; t[i].lz=-1; } return; } int search(int i,int l,int r){ if(t[i].l>=l&&t[i].r<=r) return t[i].sum; pushdown(i); int sum=0; if(t[i<<1].r>=l) sum+=search(i<<1,l,r); if(t[i<<1|1].l<=r) sum+=search(i<<1|1,l,r); return sum; } void set(int i,int l,int r,int k){ if(t[i].l>=l&&t[i].r<=r){ t[i].sum=k*(t[i].r-t[i].l+1); t[i].lz=k; return; } pushdown(i); if(t[i<<1].r>=l) set(i<<1,l,r,k); if(t[i<<1|1].l<=r) set(i<<1|1,l,r,k); t[i].sum=t[i<<1].sum+t[i<<1|1].sum; return; } void change(int i){ if(t[i].l==t[i].r){ b[t[i].l]=t[i].sum; return; } pushdown(i); change(i<<1); change(i<<1|1); return; } int main(){ scanf("%d",&_); while(_--){ ok=true; scanf("%d%d",&n,&q); for(int i=1;i<=n;i++) scanf("%1d",&a[i]); for(int i=1;i<=n;i++) scanf("%1d",&b[i]); build(1,1,n); for(int i=1;i<=q;i++) scanf("%d%d",&l[i],&r[i]); for(int i=q;i>=1;i--){ int sum=search(1,l[i],r[i]); if(2*sum==r[i]-l[i]+1){ ok=false; break; } else{ if(2*sum>r[i]-l[i]+1) set(1,l[i],r[i],1); else set(1,l[i],r[i],0); } } change(1); for(int i=1;i<=n;i++){ if(a[i]!=b[i]){ ok=false; break; } } if(ok) printf("YES\n"); else printf("NO\n"); } return 0; }
贪心,几何
题意为求出n个点的排列顺序,使得其中任意连续3个点所形成的角都为锐角。
从任意点出发,依次将点压入结果序列中,并判断最后三个点所形成的角是否为锐角,若是,则直接进行下一步,若不是,则交换最后两个点的顺序,考虑任意三个点所形成的一个三角形中至少存在两个锐角,交换后的最后三个点一定满足条件,由于交换后会影响到之前的序列,对之前的序列也要重新进行一次判断。将所有点压入结果序列并判断满足即可。
#include<bits/stdc++.h> #define ll long long #define next next_ #define set set_ using namespace std; ll n,x[5010],y[5010],a[5010],cnt; ll mul(int a,int b,int c){ return (x[a]-x[b])*(x[c]-x[b])+(y[a]-y[b])*(y[c]-y[b]); } int main(){ scanf("%lld",&n); for(ll i=1;i<=n;i++){ scanf("%lld%lld",&x[i],&y[i]); } for(ll i=1;i<=n;i++){ a[++cnt]=i; for(ll j=cnt;j>=3;j--){ if(mul(a[j],a[j-1],a[j-2])<=0) swap(a[j],a[j-1]); else break; } } for(ll i=1;i<=cnt;i++) printf("%lld ",a[i]); return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。