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