【WC2015模拟2.6】Circle

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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值