cf 1562(div2)

比赛连接: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\n",1,n-1,2,n);
        else
        {
            if(pos<=n/2)printf("%d %d %d %d\n",pos,n,pos+1,n);
            else printf("%d %d %d %d\n",1,pos,1,pos-1);
        }
    }
    return 0;
}
View Code

 

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\n%d ",L),L++;
            int x,y;
            if((x=getb(L,R,L))==0){printf("%d\n",L); continue;}
            if((y=getb(L,R,R))==0){printf("%d\n",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\n",mid); break;}
                if((val>0&&f==1)||(val<0&&f==-1))l=mid+1;
                else r=mid-1;
            }
        }
    }
    return 0;
}
View Code

(这场比赛题目大多是关注简单情况、单个位置、特殊(端)点;很机智的思路。)

posted @ 2021-08-27 17:01  Zinn  阅读(43)  评论(0编辑  收藏  举报