比赛连接:Dashboard - Codeforces Round #741 (Div. 2) - Codeforces
做了A和B,B还WA了一次。被C卡住。大掉分。
C
分析:
二进制和倍数放一起不好考虑,那只考虑0,1,2倍就好了。
1倍前面可以增加任意个0。
2倍就是二进制左移一位。左移完右边多了个0。
综上,如果字符串里没有0,随便取长度相同的;如果有0,取一个0找到它的位置(k)——如果在左边,取(k)到(n)和(k+1)到(n),表示这个0是前导0;如果在右边,取(1)到(k)和(1)到(k-1),表示这个0是左移完增加的0。
代码如下:
#include<iostream> using namespace std; int const N=2e4+5; int T,n; char st[N]; int main() { scanf("%d",&T); while(T--) { scanf("%d%s",&n,st+1); int pos=-1; for(int i=1;i<=n;i++) if(st[i]=='0'){pos=i; break;} if(pos==-1)printf("%d %d %d %d ",1,n-1,2,n); else { if(pos<=n/2)printf("%d %d %d %d ",pos,n,pos+1,n); else printf("%d %d %d %d ",1,pos,1,pos-1); } } return 0; }
D
分析:
首先,序列的每个值在计算时不是1就是-1。所以要想和为0,序列长度必须是偶数。也就是说,当询问的序列长度是奇数时,就至少要去掉一个数;当长度是偶数时,如果整个序列和不为0,那么至少要去掉两个数,可以随便去掉一个端点,然后按奇数长度考虑。
下面我们考虑一个奇数长度序列的情况:
设(b[i])表示去掉(i)后序列的和。可以想到去掉一个数以后,它后面的序列和要乘一个-1(因为奇偶性变了)。
根据这个,再结合每个位置计算时不是1就是-1,想想可以发现(b[i])和(b[i+1])的差值要么是0,要么是2。
关注两个端点(b[1])和(b[n]):
如果(b[1])和(b[n])中有0,那么对应的那个端点就是答案;
如果二者都不是0,那么肯定是一正一负。因为(b[1]=-s pm 1),(b[n]=s pm 1)(此处(s)是整个序列的和),而(s)是一个奇数,所以它们不能同号。
又因为所有(b[i])都是偶数(去掉(i)位置后序列长度为偶数),差值是0或2,所以中间必定有一个值为0的(b[i])。
到这里就可以做D1了:对于一个询问,如果已经和为0,输出0;否则如果长度是奇数,输出1,否则输出2。
进一步考虑D2:需要找到删除的位置,也就是找到(b[i]=0)的(i)。
同样,我们把和不为0的偶数长度序列直接去掉一个端点,变成奇数长度序列处理。
对于一个奇数长度序列,如果答案不在端点,那么答案在序列中的某个(b[i]=0)的位置;(b)都是偶数,差值为0或2,说白了就是连续变化;而端点的(b)异号。
所以我们可以二分找到(b[i]=0)的位置(i)。时间复杂度(O(qlogn))。
代码如下:
#include<iostream> using namespace std; int const N=3e5+5; int T,n,q,s[N]; char st[N]; int getb(int L,int R,int p) { int f=(L&1)?1:-1, ret=f*(s[p-1]-s[L-1]); f=-f; ret+=f*(s[R]-s[p]); return ret; } int main() { scanf("%d",&T); while(T--) { scanf("%d%d%s",&n,&q,st+1); for(int i=1,x,f=1;i<=n;i++,f=-f) { x=(st[i]=='+')?1:-1; s[i]=s[i-1]+f*x; } for(int i=1,L,R,f;i<=q;i++) { scanf("%d%d",&L,&R); if(s[R]-s[L-1]==0){puts("0"); continue;} if((R-L+1)&1)puts("1"); else printf("2 %d ",L),L++; int x,y; if((x=getb(L,R,L))==0){printf("%d ",L); continue;} if((y=getb(L,R,R))==0){printf("%d ",R); continue;} if(x>0)f=1; else f=-1; int l=L,r=R; while(l<=r) { int mid=((l+r)>>1),val=getb(L,R,mid); if(val==0){printf("%d ",mid); break;} if((val>0&&f==1)||(val<0&&f==-1))l=mid+1; else r=mid-1; } } } return 0; }
(这场比赛题目大多是关注简单情况、单个位置、特殊(端)点;很机智的思路。)