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]
#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";
	}
}

posted @ 2024-07-18 11:04  liyishui  阅读(93)  评论(0编辑  收藏  举报