省队集训Round3 DAY6

T1

这里写图片描述

题解

这道题应该是可以通过组合数直接计算的,但是我不会数学证明,所以就用了一种简单粗暴的方式。
ans=2|iC(n,i)C(n,ni)
ans=2|iC(n,i)2
1018 肯定不能直接枚举,如果要计算的话也需要用到lucas定理。观察发现这题的模数比较小,所以从模数入手。
考虑Lucas定理。
n=a[0]p0+a[1]p1+a[2]p2+...
m=b[0]p0+b[1]p1+b[2]p2+...
对n,m进行p进制分解,那 C(n,m)%p=C(a[0],b[0])C(a[1],b[1])....%p
如果某一位 a[i]<b[i] ,那么答案在模p意义下一定是0,也就是没有贡献,所以我们现在考虑计算出所有有贡献的答案。
确定了a之后,我们就可以通过枚举计算每一位的选择。因为i必须是偶数,所以我们可以进行数位dp.
f[i][0/1] 表示确定了第i位后,m是偶数/奇数的总贡献。
转移过程直接看代码吧。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define p 1000003
#define LL long long 
using namespace std;
LL jc[2000000],inv[2000000],f[10][10],mi[10];
LL n;
int a[10],cnt;
LL quickpow(LL num,int x)
{
    LL ans=1,base=num%p;
    while (x) {
        if (x&1) ans=ans*base%p;
        x>>=1;
        base=base*base%p;
    }
    return ans;
}
LL calc(int n,int m)
{
    return jc[n]*inv[m]%p*inv[n-m]%p;
}
LL pow(LL x){
    return x*x%p;
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    cin>>n;
    jc[0]=1;
    for (int i=1;i<=p;i++) jc[i]=(LL)jc[i-1]*i%p;
    for (int i=0;i<=p;i++) inv[i]=quickpow(jc[i],p-2);
    LL x=n; 
    while (x) {
        a[++cnt]=(LL)x%p;
        x/=p;
    }
    mi[0]=1;
    for (int i=1;i<=cnt;i++) mi[i]=mi[i-1]*mi[i-1]%p;
    for (int i=0;i<=a[1];i++)
     (f[1][i*mi[0]%2]+=pow(calc(a[1],i)))%=p;
    for (int i=2;i<=cnt;i++)
     for (int j=0;j<=a[i];j++){
        LL t=j*mi[i-1]%2;
        (f[i][t]+=f[i-1][0]*pow(calc(a[i],j))%p)%=p;
        (f[i][(t+1)%2]+=f[i-1][1]*pow(calc(a[i],j))%p)%=p;
     }
    cout<<f[cnt][0]<<endl;
}

T3

这里写图片描述
这里写图片描述

题解

可持久化trie
首先我们考虑如果只有异或操作该怎么做。因为是对全局取xor,所以我们可以把要异或的值记录下来,每次查询的时候考虑上异或值即可,不会对trie的形态造成影响。
然后考虑and,对于每一位分开考虑,如果是and 1的话没有影响。关键就是and 0,会使所有数当前位的取值变成0。
or的话,如果是or 1,会使所有数当前位变成1。我们不妨对于每一位开一个标记数组,表示当前位是否全部相同以及值是多少。如果某一位在操作的过程中由值不全相同变成相同,那么我们就重构可持久化trie,如果某一位全部相同,我们就把所有数同一放到左儿子,方便查询。
因为每一位只有一次由不同变成相同的机会,所以重构的次数不会太多,最多只有30次。
注意异或的时候别忘了修改每一位的标记。

代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define N 100003
using namespace std;
int xornum,n,m,a[N],ch[N*20][2],size[N*20],pd[N],opt[N],root[N],sz;
void insert(int i,int x)
{
    int pre=root[i-1];
    root[i]=++sz;
    int now=root[i];
    for (int i=30;i>=0;i--){
        int t=(x>>i)&1;
        if (pd[i]) t=0;
        size[now]=size[pre]+1;
        ch[now][t^1]=ch[pre][t^1];
        ch[now][t]=++sz;
        now=ch[now][t]; pre=ch[pre][t];
    }
    size[now]=size[pre]+1;
}
int query(int i,int j,int x)
{
    int ans=0;
    int R=i; int L=j;
    for (int t=30;t>=0;t--) {
        if (pd[t]) ans+=(1<<t)*opt[t],R=ch[R][0],L=ch[L][0];
        else {
            int son=(xornum>>t)&1;
            int k=size[ch[R][son]]-size[ch[L][son]];
            if (k>=x) {
                ans+=(1<<t)*0;
                R=ch[R][son],L=ch[L][son];
            }
            else {
                x-=k;
                ans+=(1<<t)*1;
                R=ch[R][son^1],L=ch[L][son^1];
            }
        }
    } 
    return ans;
}
int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    insert(1,0);
    for (int i=1;i<=n;i++) 
     insert(i+1,a[i]);
    for (int i=1;i<=m;i++) {
        char s[10]; scanf("%s",s+1);
        int l,r,x;
        if (s[1]=='X') {
            scanf("%d",&x);
            xornum^=x;
            for (int j=0;j<=30;j++)
             if (pd[j]&&((x>>j)&1)) opt[j]^=1;
        }
        if (s[1]=='O'){
            scanf("%d",&x); bool flag=false;
            for (int j=0;j<=30;j++)
             if ((x>>j)&1) {
                if (pd[j]) opt[j]=1;
                else{
                    flag=true;
                    pd[j]=opt[j]=1;
                }
             }
             if (flag) {
                sz=0;
                memset(size,0,sizeof(size));
                memset(ch,0,sizeof(ch));
                memset(root,0,sizeof(root));
                insert(1,0);
                for (int j=1;j<=n;j++) insert(j+1,a[j]);
             }
        }
        if (s[2]=='n'){
            scanf("%d",&x); bool flag=false;
            for (int j=0;j<=30;j++)
             if (((x>>j)&1)==0){
                if (pd[j]) opt[j]=0;
                else{
                    flag=true;
                    pd[j]=1;opt[j]=0;
                }
             }
            if (flag){
                sz=0;
                memset(size,0,sizeof(size));
                memset(ch,0,sizeof(ch));
                memset(root,0,sizeof(root));
                insert(1,0);
                for (int j=1;j<=n;j++) insert(j+1,a[j]);
            }
        }
        if (s[2]=='s') {
            scanf("%d%d%d",&l,&r,&x);
            printf("%d\n",query(root[r+1],root[l],x));
        }
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值