【WC2015模拟2.6】Circle

来源:互联网 发布:mac系统软件卸载 编辑:程序博客网 时间:2024/05/16 07:35

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[N],mi[M],f[M][N][2],c[M][N];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));}
0 0
原创粉丝点击