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; }
这里看到其实比赛也不一定是什么难得知识点,只不过考验你对知识点的运用是否熟练与深刻...