11 11

今天的T1确实好,又学到了没有的知识....

这里先说一些前缀知识,关于^的一些东西...

^是取反,即相同取0,相反取1,知道这可不远不够,我们还要知道^是满足前缀和的,即如果我们知道sum[x]于sum[y]那(x-y)的异或和即为sum[y]^sum[x-1]...

至于不进位加法,就是某位园神告诉我的,怎么说呢,我们这样想,末尾两个1,^后不就是0吗,这不就是加法吗,只不过没有进位,而这里就体现了^和奇偶的联系...

见题:

考场上被这道题打爆...

现在我们仔细思考一下,发现其实很简单...

简化题意:题目要求我们找一个最长的连续序列,使得各个位上的数相加都为奇数或偶数...O(n^2)的暴力就不多说了...

我们考虑题目要奇偶也就是末位数都是1或0,那我们就可以转换成^,因为^的本质不就是不进位加法吗?这样我们就把判奇偶转换成我们熟悉且可操作的运算..

之后我们考虑区间查询常用的手段:前缀和,我们要(x-y)的异或和,只需有处理出,sum[y]和sum[x-1]即可,这很好想,当我们将前缀和当做一个元素之后,我们就对题目再转换:

对于每一个数i,找到在它之前离他最远的一个数j,使得i^j==(1<<k)-1或i^j==0...为什么呢?全为奇数不就是各个位上都是1吗?全为偶数不就是全为0...

这样我们就好办了,枚举i时,检验sum[i]是否已存在,若存在就更新答案,否则记录它存在...

#include<bits/stdc++.h>
#define max(a,b) (a>b?a:b)
#define ll long long
using namespace std;
map<ll,int>mp;
char str[50];
ll n,k,now,ans;
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
int main()
{
    freopen("music.in","r",stdin);
    freopen("music.out","w",stdout);
    n=read();k=read();
    for(register int i=1;i<=n;++i)
    {
        scanf("%s",str+1);
        ll s=0;//s当前状态的二进制. 
        for(register int j=1;j<=k;++j) 
        {
            if(str[j]=='A') s=(s<<1)|1;
            else            s=s<<1;
        }
        now=now^s;//将当前的前缀和的二进制搞出来. 
        if(!mp[now]) 
        {
            mp[now]=i;//赋值. 
            mp[now^((1<<k)-1)]=i;
        }
        else ans=max(ans,i-mp[now]);//更新答案. 
    }
    printf("%d",ans);
    return 0;
} 

这里看到其实比赛也不一定是什么难得知识点,只不过考验你对知识点的运用是否熟练与深刻...

posted @ 2019-11-11 15:08  逆天峰  阅读(209)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//