您的位置:首页 > 其它

【WC2015模拟2.6】Circle

2016-12-27 22:01 393 查看

Description

一开始有个n个在[0,2^m)区间内的数。

每一秒每一个数将会+1,然后对2^m取模。

求在[1,T]秒内,有多少个时间,使得这n个数的异或值为S。

n<=10^5,m<=50,T<=10^16

Solution

很显然的与位运算有关的题目。

首先我们就相当于求给出一个上界up,[1,up]中有多少个数x使得所有的(ai+x)%2^m的异或值为S。

考虑Dp,发现我们只需要二进制的后m位的异或值与S相同,这样就不用考虑%的问题。

但是发现加法会产生进位,比较难处理。

考虑设状态Fi,j,0\1表示后i-1位已经确定了,第i-1位向第i位产生了j个进位,0\1表示加的数和上界的关系

我们可以发现,因为加的数一样,这j个进位一定是后i-1位最大的那j个数所产生的。

那么我们可以预处理对于每个i,j,产生进位的是哪些数。

我们只需要对这n个值排序就好了,排m次。

排序的考虑类似基数排序,从上一次排序的结果O(N)推出这一次的排序结果。

然后转移分情况讨论就好了,转移比较复杂,详细见代码。

一道好题,(⊙v⊙)嗯。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int N=1e5+5,M=55;
int n,m,d[M];
ll t,s,cnt,a
,mi[M],f[M]
[2],c[M]
;
bool cmp(ll x,ll y) {return x>y;}
ll dp(ll upper) {
memset(f,0,sizeof(f));f[0][0][0]=1;
fo(i,0,m-1) fo(up,0,1) {
int ad=d[i],k=0,x;
fo(j,0,n) {
if (f[i][j][up]) {
if ((s&mi[i])&&(ad&1)||!(s&mi[i])&&!(ad&1)) {
if (!(upper&mi[i])&&up) x=1;else x=0;
f[i+1][k][x]+=f[i][j][up];
}
if ((s&mi[i])&&((n-ad)&1)||!(s&mi[i])&&!((n-ad)&1)) {
if ((upper&mi[i])&&!up) x=0;else x=1;
f[i+1][k+ad][x]+=f[i][j][up];
}
}
if (i&&j<n) {
if (a[c[i-1][j+1]]&mi[i]) k++,ad--;
else ad++;
}
}
}
ll ans=0;
fo(i,0,n) ans+=f[m][i][0];
return ans;
}
int main() {
scanf("%d%d%lld%lld",&n,&m,&s,&t);cnt=s;
fo(i,1,n) scanf("%lld",&a[i]),cnt^=a[i];
mi[0]=1;fo(i,1,m+1) mi[i]=mi[i-1]*2;
fo(j,0,m-1) {
fo(i,1,n) d[j]+=(a[i]&mi[j])>0;
if (j) {
int tot=0;
fo(i,1,n) if (a[c[j-1][i]]&mi[j]) c[j][++tot]=c[j-1][i];
fo(i,1,n) if (!(a[c[j-1][i]]&mi[j])) c[j][++tot]=c[j-1][i];
} else {
int tot=0;
fo(i,1,n) if (a[i]&1) c[j][++tot]=i;
fo(i,1,n) if (!(a[i]&1)) c[j][++tot]=i;
}
}
printf("%lld\n",t/mi[m]*dp(mi[m]-1)+dp(t%mi[m])-(cnt==0));
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: