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> |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需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