19.11.16ACM集训补题
题意是找到一个1~N的排列B,使得排列A和排列B的每个数的差的绝对值大于等于N
原本想的是将原排列逆序输出应该可以满足要求
然后就WA在第六个点。后来想想132这个排列的逆序231,dif只有2<3
然后第二个思路就是将较大的数和较小的数依次交换,最后过了
#include<iostream> #include<string.h> #include<stdio.h> using namespace std; const int maxn=1e5+7; int a[maxn],n,m,b[maxn]; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&m); a[m]=i; } for(int i=1;i<=n;i++) b[a[n+1-i]]=i; for(int i=1;i<n;i++) printf("%d ",b[i]); printf("%d\n",b[n]); return 0; }
题意是给出R,C两个数组,组成得到NxN的二维数组,问从起点到终点有没有每个点都是偶数的路径
先通过R,C数组得到二维数组,然后每次判断就将起点周围的偶数区域染色,判断终点在不在这个区域,这样就可以解了
但是N最大1e5,二维数组就超内存了,结果也没有想到好的办法,只有看题解
让我们把点(i,j)标为0或1,分别代表这个点是偶数或奇数,同理R,C数组
当我们看一个点从(i,j)到(i,j+1),R(i)的值不变,说明C(j)和C(j+1)同1或同0
同理,从(i,j)到(i+1,j)说明R(i)和R(i+1)同1或同0
而如果路径不是笔直的,例如左-下-左,我们也可以判断
C(j)和C(j+1)同1或同0,C(j+1)和C(j+2)同1或同0,则C(j)和C(j+2)同1或同0
所以若从(a,b)到(A,B),R(a,A)和C(b,B)两个区间都同1或同0
当然这是有路径的必要条件
由于起点必是偶数,则两个区间只能同1或同0,否则认为起点是奇数
所以仅用判断起点终点是偶数,以及两个区间同1或同0,,即可证有路径
这里用前缀和判断区间是否同0或同1
#include<iostream> #include<string.h> #include<stdio.h> using namespace std; const int maxn=1e5+7; int n,q,c[maxn],r[maxn],csum[maxn],rsum[maxn]; int ra,ca,rb,cb; int main(){ scanf("%d%d",&n,&q); for(int i=1;i<=n;i++) scanf("%d",&r[i]); for(int i=1;i<=n;i++) scanf("%d",&c[i]); for(int i=1;i<=n;i++) if(r[i]%2==r[i-1]%2)rsum[i]=rsum[i-1]; else rsum[i]=rsum[i-1]+1; for(int i=1;i<=n;i++) if(c[i]%2==c[i-1]%2)csum[i]=csum[i-1]; else csum[i]=csum[i-1]+1; while(q--){ scanf("%d%d%d%d",&ra,&ca,&rb,&cb); if((r[ra]+c[ca])%2||(r[rb]+c[cb])%2){ printf("NO\n"); continue; } if(ra<rb)swap(ra,rb); if(ca<cb)swap(ca,cb); if(rsum[ra]-rsum[rb]==0&&csum[ca]-csum[cb]==0){ printf("YES\n"); } else printf("NO\n"); } return 0; }
题意是给出N个矩形,找最大面积
一种找法是矩形面积的一半
一种找法是找到两个矩形,都满足长度大于等于l,宽度大于等于d,最大面积是max l*max d
看到这道题,我第一反应是二分,确定一个面积后,找这个面积能不能得到,当然浮点数也可以二分
但是这样依然想不出怎么完成第二种找法,最后。。。看题解
第一种找法简单,在输入的时候就可以先找出来
然后第二种找法,分别用排序和求最大值满足了l,d的条件
众所周知,一次遍历的复杂度是O(n)(一维)
如果能事先处理好,然后一次遍历得到答案,我觉得这个程序是写得很漂亮的
我们想让这些矩阵由宽度d从大到小排序,后面的矩阵之前一定有宽度比他打的矩阵
再用一个最大值记录到第i个矩阵时,他之前矩阵的最大长度,如果他长度比maxl大,那么他和maxl对应的矩阵长度都大于等于maxl,反之他和maxl对应的矩阵长度都大于等于他的长度
这样就既满足了l和d的条件
到这里其实我还有一个疑问,就是看答案代码时,发现在排序前将矩阵较长的一面作为排序的宽度,但不知道为什么
一种想法是将每个输入,长宽颠倒作为两个矩阵读入,但是这样可能会出现满足条件的两个矩阵是同一个,所以不行
后来看了下案例,不处理的话,就相当于把两个矩阵十字重叠起来,最后重叠的面积一定偏小,所以还是要处理
还有就是精度问题??说是double存不下longlong的内容
#include<iostream> #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int maxn=1e5+7; typedef long long LL; struct Node{ LL l,d; bool operator<(const Node &a){ return d>a.d; } }node[maxn]; int n; LL ans,l,d; int main(){ scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%lld%lld",&l,&d); if(d<l)swap(d,l); node[i].d=d,node[i].l=l; ans=max(ans,l*d); } sort(node,node+n); LL maxl=node[0].l; for(int i=1;i<n;i++){ ans=max(ans,node[i].d*min(maxl,node[i].l)*2); maxl=max(maxl,node[i].l); } if(ans%2)printf("%lld.5\n",ans/2); else printf("%lld.0\n",ans/2); return 0; }
这道题很好想,题意是给出一串字符,和一堆操作
1操作是反转l到r的字符,2是通过字符串得到f函数的值
只要你按照题目说的做,很容易就能写出来,然后TLE
然后我考虑把操作1写作前缀和,然后还是TLE
问题只有可能出在f函数上了,但是。。想不出来
要是思维灵敏一点,你就会发现A=A+B和B=A+B的操作可以分别用两种矩阵实现
但是我们毕竟不能有几个‘A’就乘几个A=A+B的矩阵,毕竟字符串是有顺序的,‘AB’和‘BA’最后的结果是不一样的
所以,画风一转,使用线段树来同时满足修改区间和得到有序的累积的区间值的操作
具体一点,就是通过线段树就可以得到l到r的区间里所有两种矩阵的累乘
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; typedef long long LL; const int maxn=1e5+7; const LL mod=1e9+7; struct mat{ LL m[3][3]; mat(){ memset(m,0,sizeof(m)); } }A,B,st[maxn<<2][2]; int lazy[maxn<<2],n,q,l,r,op; LL a,b; char s[maxn]; mat operator*(mat a,mat b){ mat ans; for(int i=1;i<=2;i++) for(int j=1;j<=2;j++) for(int k=1;k<=2;k++){ ans.m[i][j]=(ans.m[i][j]+a.m[i][k]*b.m[k][j]%mod)%mod; // ans.m[i][j]%=mod; } return ans; } void pushdown(int l,int r,int rt){ if(lazy[rt]){ swap(st[rt<<1][0],st[rt<<1][1]); swap(st[rt<<1|1][0],st[rt<<1|1][1]); lazy[rt<<1]^=1; lazy[rt<<1|1]^=1; lazy[rt]=0; } } void pushup(int rt){ st[rt][0]=st[rt<<1][0]*st[rt<<1|1][0]; st[rt][1]=st[rt<<1][1]*st[rt<<1|1][1]; } void build(int l,int r,int rt){ //lazy[rt]=0; if(l==r){ if(s[l]=='A') st[rt][0]=A,st[rt][1]=B; else st[rt][0]=B,st[rt][1]=A; return; } int mid=(l+r)>>1; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); pushup(rt); } void change(int l,int r,int rt,int ll,int rr){ if(ll<=l&&r<=rr){ swap(st[rt][0],st[rt][1]); lazy[rt]^=1; return; } int mid=(l+r)>>1; pushdown(l,r,rt); if(ll<=mid)change(l,mid,rt<<1,ll,rr); if(rr>mid)change(mid+1,r,rt<<1|1,ll,rr); pushup(rt); } mat query(int l,int r,int rt,int ll,int rr){ if(ll<=l&&r<=rr) return st[rt][0]; int mid=(l+r)>>1; mat ans; ans.m[1][1]=ans.m[2][2]=1; pushdown(l,r,rt); if(ll<=mid) ans=ans*query(l,mid,rt<<1,ll,rr); if(rr>mid) ans=ans*query(mid+1,r,rt<<1|1,ll,rr); return ans; } int main(){ scanf("%d%d%s",&n,&q,s+1); A.m[1][1]=1,A.m[1][2]=0; A.m[2][1]=1,A.m[2][2]=1; B.m[1][1]=1,B.m[1][2]=1; B.m[2][1]=0,B.m[2][2]=1; // scanf("%s",s+1); build(1,n,1); // show(A*B); while(q--){ scanf("%d%d%d",&op,&l,&r); if(op==1){ change(1,n,1,l,r); } else { scanf("%lld%lld",&a,&b); mat kk=query(1,n,1,l,r); mat ans; ans.m[1][1]=a,ans.m[1][2]=b; ans=ans*kk; printf("%lld %lld\n",ans.m[1][1],ans.m[1][2]); } } return 0; }
做得多了我慢慢也开始明白了,当你一点思路都没有又是跟数有关的大多都是数论。
这道题文字太长,一开始题意都没明白
主要讲的就是在无穷的点集里,只要两点相差i值,B集合又有i值,就两点相连
问你删除B集合哪些点后,连接的图是二分图
这道题保证二分图就是要保证无奇环
我们看一下两个互质的数,比如3,4
从起点0开始,依次加3,变成0,3,6,9,12
依次加4,变成0,4,8,12。在0-12的环里一共有7个数,是奇环
上半圈有4条线,下半圈有3条,这是因为3,4互质,3要乘4变成12,4要乘3变成12
所以第一个结论,若是互质的数,相加为奇数则有奇环
若是奇偶互质,那必是奇环。所以可能的解只有是奇奇互质,比如3,5
第二个结论,两个数同乘相同倍数,环线数不变
比如3,5能组成8线环,12,20也能组成8线环,不同的是一条线增加的值变多了
最后一个结论,所以我们把两个数除到互质,最后判断是不是两个奇数就行了
但其实奇数除奇数还是奇数,所以看两个数除以相同倍数的2,最后看是不是两个奇数也行
#include<iostream> #include<stdio.h> #include<string.h> using namespace std; const int maxn=2e5+7; typedef long long LL; LL n,a[maxn],cnt[maxn],num[maxn]; int main(){ scanf("%lld",&n); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); for(int i=1;i<=n;i++){ LL t=a[i]; while(t%2==0){ cnt[i]++; t/=2; } num[cnt[i]]++; } int ans=0,maxcnt; for(int i=0;i<63;i++) if(num[i]>ans){ ans=num[i]; maxcnt=i; } printf("%lld\n",n-ans); for(int i=1;i<=n;i++) if(cnt[i]!=maxcnt)printf("%lld ",a[i]); cout<<endl; return 0; }
CodeForces 1225D