2024牛客多校第一场

A

简单的组合数学。考虑枚举为1的个数的长度为x,则其他数除了最后一位的0外都可以乱填。

对于末尾为1的数,显然每一位都是独立的,单独考虑每一位。

显然只要该位上有一个0即可,经典容斥:减去全为1的这一种情况。

复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N=5005;
int f[N][N];
int qpow(int a,int b,int mod){
    int ret=1;
    while(b){
        //TODO
        if(b&1) ret=ret*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ret;
}
signed main(){
    int n,m,p;cin>>n>>m>>p;
    
    f[0][0]=1;
    for(int i=1;i<=n;i++){
        f[i][0]=1;
        for(int j=1;j<=i;j++){
            f[i][j]=(f[i-1][j-1]+f[i-1][j])%p;
        }
    }
        
    int ans=0;
    for(int x=1;x<=n;x++){
        int tmp=0;
        
        int a1=(qpow(2,x,p)-1+p)%p;
        a1=qpow(a1,m-1,p);
        
        int a2=qpow(2,m-1,p);
        a2=qpow(a2,n-x,p);
        
        int a3=f[n][x];
        tmp=a1*a2%p*a3%p;
        
        ans=(ans+tmp)%p;
    }
    cout<<ans<<"\n";
}
复制代码

 

B

A的plus版,但也不难。多了一个限制是:能选出2个不同的子序列使得其AND和为1。

2个不同的情况很多种不好计算,反向考虑什么时候选不出来?当且仅当每个末尾为1的数都要选。

问题化简成有n个数要覆盖m个位,每个数都至少有一个自己唯一覆盖的位置。

dp[i][j]=(dp[i-1][j-1]+dp[i][j-1])*i

复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=5005;
typedef long long ll;
#define int long long
int f[N][N],n,m,p;
int qpow(ll a,int b){
    a%=p;
    ll ret=1;
    while(b){
        //TODO
        if(b&1) ret=ret*a%p;
        a=a*a%p;
        b>>=1;
    }
    return ret;
}
int dp[N][N];
signed main(){
    cin>>n>>m>>p;
    f[0][0]=1;
    for(int i=1;i<=5002;i++){
        f[i][0]=1;
        for(int j=1;j<=i;j++){
            f[i][j]=(f[i-1][j-1]+f[i-1][j])%p;
        }
    }
    
    ll ans=0;
    for(int x=1;x<=n;x++){
        ll a1=(qpow(2,x)-1+p)%p;a1=qpow(a1,m-1);
        int a2=qpow(2,m-1);a2=qpow(a2,n-x);
        int a3=f[n][x];
        ans=(ans+a1*a2%p*a3%p)%p;
    }
//    cout<<"ans: "<<ans<<"\n";
    
    dp[0][0]=1;
    for(int i=1;i<=n;i++){
        for(int j=i;j<=m;j++){
            dp[i][j]=1ll*((dp[i-1][j-1]+dp[i][j-1])%p)*i%p;
            //  cout<<i<<" "<<j<<" :"<<dp[i][j]<<"\n";
        }
    }
    
    ll tot=0;
    for(int k=2;k<=n;k++){
        int x=qpow(2,(m-1)*(n-k));
        int y=(qpow(2,k)-k-1+p)%p;
        ll tmp=1;
        for(int t=m-1;t>=k;t--){
            tot+=tmp*f[m-1][t]%p*dp[k][t]%p*f[n][k]%p*x%p;
            //    cout<<k<<" "<<t<<" : "<<f[m-1][t]*dp[k][t]%p*f[n][k]%p*qpow(2,(m-1)*(n-k)%p)%p*qpow((qpow(2,k)-k-1+p)%p,m-1-t)<<"\n";
            tot%=p;
            tmp=tmp*y%p;
        }
    }
    tot+=(ll)qpow(2,(m-1)*(n-1))*n%p;
    tot%=p;
    ans=(ans-tot+p)%p;
    cout<<ans<<"\n";
}
复制代码

C

签到

复制代码
#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int mod=1e9+7;
signed main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    
    int q;
    cin>>q;
    int sum=0;// a1+a2+a3
    int tot=0;// s1+s2+s3
    int len=0;
    vector<int>a;
    while(q--){
        int t,v;
        cin>>t>>v;
        for(int i=1;i<=t;i++){
            sum=(sum-a.back()+mod)%mod;
            tot=(tot-a.back()*len%mod+mod)%mod;
            len--;
            a.pop_back();
        }
        a.push_back(v);
    //    cout<<tot<<" "<<len<<" "<<sum<<"\n";
        tot+=(len*v%mod+v)%mod;
        tot%=mod;
        len++;
        sum+=v;
        sum%=mod;
        cout<<tot<<"\n";
    }
}
复制代码

D

没场切,当队友说出这个模数很小,先算出答案后再取模而我没有反对意见时,这道题就已经完蛋了。。

切入点在于模数,没有很大,且是2^21,启发我们在位运算上做文章。

显然先拆位,设当前在第 k 位,拆完后询问转化为有多少后缀和在第 k 位上为1。

后缀和不好处理,如果直接从后缀和入手,每次操作对整个数列都会产生影响。

考虑转化成前缀和,则后缀和 s[i]=sum[n]-sum[i-1],求有多少个 s[i] 在第k位上为1。

这里直接算肯定也不行,”在第k位上为1“可以转化成模意义下的加法运算:(sum[n]-sum[i-1]) % (2^(k+1)) >= 2^k

令x=-sum[i-1] % (2^(k+1))   =>  (sum[n]+x)  % (2^(k+1)) >= 2^k

 又 (sum[n]+x)  % (2^(k+1)) <= 2^(k+1)
所以  2^(k+1) >= (sum[n]+x)  % (2^(k+1)) >= 2^k
每次询问相当于固定sum[n],求有多少x满足条件。满足条件的x是一段(或者两段连续的值)
R = 2^(k+1)-sum[n]  >= x >= 2^k -sum[n] = L
若 L <= R,直接询问[L,R]有多少个数即可,用权值树状数组维护
若 R<L ,说明在mod意义下R溢出了,跑到了前面去,就是两段:[0,R]  和  [L,2^(k+1)-1]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include<bits/stdc++.h>
using namespace std;
const int N = 2097152+5;
int lowbit(int x) {
    return x&(-x);
}
struct ft{
    int a[N],n;
    void init(int len){
        n=len;
        for(int i=1;i<=n;i++) a[i]=0;
    }
    void add(int pos,int v){
        pos++;
        for(int i=pos;i<=n;i+=lowbit(i))
            a[i]+=v;
    }
    int sum(int pos){
        if(pos<0) return 0;
        pos++;
        int ret=0;
//      cout<<pos<<" LEN: "<<n<<endl;
        for(int i=pos;i;i-=lowbit(i))
            ret+=a[i];
        return ret;
    }
    int query(int l,int r){
        if(l>r) return 0;
        return sum(r)-sum(l-1);
    }
}BIT[21];
int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
     
    for(int i=0;i<=20;i++) BIT[i].init((1<<(i+1)));
    vector<int>a;
    int q;cin>>q;
     
    int sum=0;
    for(int k=0;k<=20;k++){
        int tot,p=1<<(k+1);
        tot=(p-sum%p)%p;
        BIT[k].add(tot,-1);
    }
     
     
    while(q--){
        //TODO
        int t,v;cin>>t>>v;
        for(int j=1;j<=t;j++){
            int x=a.back();
            for(int k=0;k<=20;k++){
                int tot,p=1<<(k+1);
                tot=(p-sum%p)%p;
                BIT[k].add(tot,-1);
            }
            sum-=x;
            a.pop_back();
        }
 
        sum+=v;
        a.push_back(v);
        for(int k=0;k<=20;k++){
            int tot,p=1<<(k+1);  
            tot=(p-sum%p)%p;
        //  cout<<k<<" add "<<tot<<"\n";
            BIT[k].add(tot,1);
        }
         
        int ans=0;
        for(int k=0;k<=20;k++){
            int p=1<<(k+1),p1=1<<k;
            int l=(p1-sum%p+p)%p,r=(p-1-sum%p+p)%p,cnt=0;
        //  cout<<"["<<l<<","<<r<<"]"<<"\n";
        //  cout<<BIT[k].query(l,r)<<" "<<BIT[k].query(0,r)<<" "<<BIT[k].query(l,p-1)<<"\n";
            if(l<=r) cnt=BIT[k].query(l,r);
            else cnt=cnt+BIT[k].query(0,r)+BIT[k].query(l,p-1);
            if(cnt&1) {
            //  cout<<l<<" "<<r<<"\n";
                ans|=(1<<k);
            }
        }
        cout<<ans<<"\n";
    }
}<br><br>
posted @   liyishui  阅读(95)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
历史上的今天:
2022-07-18 codeforces D. Array Division
2022-07-18 codeforces E - Selling Souvenirs
点击右上角即可分享
微信分享提示