2024年6月杂题乱写
6.5
P3214 [HNOI2011] 卡农
设
- 这
个集合已经合法,最后一个集合为空集。 - 最后要选的集合在前面
个集合出现过。
答案就是总数减去这两种情况,第一种情况显然为
直接递推即可。
点击查看代码
#include<bits/stdc++.h> #define int long long inline int read(){ char ch=getchar();int x=0,f=1; for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48); return x*f; } const int N=1e6+10,mod=1e8+7; inline int mo(int x){return x<0?(x%mod+mod)%mod:(x>=mod?x%mod:x);} inline int qpow(int a,int b){ int res=1; for(;b;b>>=1,a=mo(a*a))if(b&1)res=mo(res*a); return res; } int n,m,sum,inv[N],C,f[N]; signed main(){ // freopen("in.in","r",stdin),freopen("out.out","w",stdout); std::ios::sync_with_stdio(false);std::cin.tie(0),std::cout.tie(0); n=read(),m=read();sum=mo(qpow(2,n)-1); if(m>sum){std::cout<<0<<'\n';exit(0);} inv[1]=1,f[1]=f[2]=0;f[0]=1; for(int i=2;i<=m;++i)inv[i]=mo((mod-mod/i)*inv[mod%i]); C=mo(mo(sum*(sum-1))*inv[2]); for(int i=3;i<=m;++i){ f[i]=mo(mo((C-f[i-1]-mo(f[i-2]*mo(sum-i+2))))*inv[i]); C=mo(mo(C*(sum-i+1))*inv[i]); } std::cout<<f[m]<<'\n'; }
6.9
困困困,头疼疼疼疼疼,给大家来场 ABC 速通。
- A 直接扫
- B 直接扫
- C 递归周围,然后中间填白色
- D 设
为数字 十进制下的长度, ,后面直接等比数列求和 。 - E 就是求有向图每个点能到达的点(包括自己)的和,反向建边后直接缩点拓扑排序跑 DP 即可。
- F 一眼线段树,赛时傻逼没推下式子锅了,
,记两个 tag 即可。 - G 高级东西,不会。
6.15 ABC358 速通
D 直接二分找,然后删。
E 就是求多重集排列数,直接设
F 随便找,然后比较傻逼,没意思。
G 直接设状态
6.17
感觉这种博客还是要写下去,
CF1808D Petya, Petya, Petr, and Palindromes
比较水的题。首先直接不太好想,正难则反,考虑将所有不需要改变的点对处理出来,拿总数一减就是答案。
发现合法数就是两个位置的数相同,所以就变成了,对于每一个数,查询指定区间内与它相等的数的个数。
因为需要满足回文的对称性,所以偶数位置只会和偶数位置匹配,奇数位置只会和奇数位置匹配,此时其实可以直接无脑上数据结构了,时间复杂度
不考虑数据结构,这里有两种处理方法。
- 开个桶记录一下每个数的出现次数,类似莫队的思想,两个指针记录一下桶的范围,每次查到
时,求出与 的对应位置范围,然后移动指针,如果位置 与 奇偶性相同,就产生 的贡献,奇数偶数各查一次。 - 开桶记录每个数出现的奇数位置和偶数位置,对于每个数,双指针不断向右扩展,直到不合法时记录一下两个指针之间的数的个数,也是奇数偶数都查一次。
这两种做法的时间复杂度都是
#include<bits/stdc++.h> #define int long long typedef long long ll; typedef unsigned long long ull; inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;} const int N=2e5+10; int a[N],n,k,q[N],mid,l1,r1,ans; inline void work(int pd){ l1=1,r1=0; memset(q,0,sizeof(q)); for(int i=mid+2+pd;i<=n;i+=2){ int L=std::max(1ll,i-k+1),R=std::min(n,i+mid-1); int l=(L+L+k-1)-i,r=(R+(R-k+1))-i; while(r1<r){if((r1&1)!=(i&1))q[a[++r1]]++;else ++r1;} while(r1>r){if((r1&1)==(i&1))q[a[r1--]]--;else --r1;} while(l1<l){if((l1&1)==(i&1))q[a[l1++]]--;else ++l1;} while(l1>l){if((l1&1)!=(i&1))q[a[--l1]]++;else --l1;} ans+=q[a[i]]; } } signed main(){ // freopen("in.in","r",stdin);freopen("out.out","w",stdout); std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0); n=read();k=read(); for(int i=1;i<=n;++i)a[i]=read(); mid=k/2;int pd=mid&1; work(pd),work(pd^1); std::cout<<(n-k+1)*(k/2)-ans<<'\n'; }
CF1526D Kill Anton
感觉是比较神的构造,直接构造答案,比较困难,思考
有引理:一定存在所有同类字母相邻的最优答案。
感性理解:因为字母之间只会不同类交换,所以考虑把不同类的字母尽量放前面,同类在一起。
证明:
- 在最优策略下,同类字母不会发生交换,所以对于任意排列,同类字母之间的标号都是递增的。
- 对于同类字母
与 之间的所有相邻异类字母 到 来说,如果 这种字母中 个字母与前面的 这种字母共构成了 个逆序对, 个与后面的 这种字母共构成了 个逆序对,有 。如果将所有的 交换到前面,此时新增了 个逆序对,如果将他们交换到后面,此时新增了 个逆序对。 - 当
,即 时,有 ,则 ,即 。同理,如果 ,则 。 - 所以对于任何一种情况,我们总能使得同类字母相邻且逆序对数量单调不减,即一定存在所有同类字母相邻的最优答案。
由于不确定字母之间的顺序,我们可以直接枚举排列,然后每次用树状数组查询逆序对数量(当然也可以
#include<bits/stdc++.h> #define int long long typedef long long ll; typedef unsigned long long ull; inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;} const int N=1e5+10; int n,a[N],ans[N],_ans,t[N]; char s[N]; bool vis[N]; std::vector<int> v[5]; inline void add(int x){ for(;x<=n;x+=x&-x)t[x]+=1; } inline int query(int x){ int res=0; for(int i=n;i;i-=i&-i)res+=t[i]; for(int i=x;i;i-=i&-i)res-=t[i]; return res; } inline void solve(){ for(int i=1;i<=n;++i)t[i]=0; int res=0; for(int i=1;i<=4;++i){ for(int x:v[a[i]]){ res+=query(x); add(x); } } if(_ans<=res){ for(int i=1;i<=4;++i)ans[i]=a[i]; _ans=res; } } inline void dfs(int len,int x){ if(len)a[len]=x; if(len==4){ solve(); return; } for(int i=1;i<=4;++i){ if(!vis[i]){ vis[i]=1; dfs(len+1,i); vis[i]=0; } } } signed main(){ // freopen("in.in","r",stdin);freopen("out.out","w",stdout); std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0); int t;std::cin>>t; while(t--){ _ans=0; for(int i=1;i<=4;++i)v[i].clear(); std::cin>>s+1; n=strlen(s+1); for(int i=1;i<=n;++i){ if(s[i]=='A')v[1].push_back(i); if(s[i]=='N')v[2].push_back(i); if(s[i]=='T')v[3].push_back(i); if(s[i]=='O')v[4].push_back(i); } dfs(0,0); for(int i=1;i<=4;++i){ for(int j=1;j<=v[ans[i]].size();++j){ if(ans[i]==1)std::cout<<'A'; if(ans[i]==2)std::cout<<'N'; if(ans[i]==3)std::cout<<'T'; if(ans[i]==4)std::cout<<'O'; } } std::cout<<'\n'; } }
6.19
CF1978D Elections
感觉 C 比 D 难,为啥这场能掉分,为啥 C 对不出脑电波,感觉赛时死磕很不好。
有一点比较显然,如果一个人不能直接获胜的话,那么他前面的人都应该被撤下,这样才会改变这个人的票数,才有可能使他获胜。
接下来有两种情况:
- 撤完前面的人后,他的票数大于等于剩下的人的最多票数的话,那么此时直接获胜。
- 撤完前面的人后,他的票数小于剩下的人的最多票数的话,此时再把票数最多的人撤下,这样他就成了票数最多的人,获胜。
直接做前缀和和后缀最大值就行,时间复杂度
#include<bits/stdc++.h> #define int long long typedef long long ll; typedef unsigned long long ull; inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;} const int N=2e5+10; int a[N],sum[N],max[N]; signed main(){ // freopen("in.in","r",stdin);freopen("out.out","w",stdout); std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0); int T=read(); while(T--){ int n=read(),c=read(); for(int i=1;i<=n;++i)a[i]=read();a[1]+=c; for(int i=1;i<=n;++i)sum[i]=sum[i-1]+a[i]; max[n+1]=0; for(int i=n;i;--i)max[i]=std::max(a[i],max[i+1]); int st=0; for(int i=1;i<=n;++i){ if(a[i]==max[1]&&st<max[1]){std::cout<<0<<' ';} else{ int ans=i-1; if(sum[i]<max[i+1]){ans++;} std::cout<<ans<<' '; } st=std::max(st,a[i]); } std::cout<<'\n'; } }
6.20
CF1736D Equal Binary Subsequences
感觉是对脑电波题,能对上黄,对不上紫。
首先对于
经过一些数据的模拟,我们好像并不能找出完全不合法的情况,这时可以大胆猜测如果数量不是奇数的话一定有解。
考虑什么样的序列一定是合法的,不难发现,两位两位的进行分组,每组都一样的序列一定是合法的,如 1111001100
或 0000111100
,对于答案
事实上,对于任意一个可能合法的序列,都能构造出一种方案,使得他与上面序列同构。
具体来说,我们扫描每一组,如果这两个数不一样,选出一个和上一个选择不一样的那一位(如果上次没选过就随便选),扫描完毕后,将选出的子序列右移一位。此时每组的两个数都相同,合法。
考虑为什么第一组的数也一定相同:因为
#include<bits/stdc++.h> typedef long long ll; typedef unsigned long long ull; inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;} const int N=2e5+10; char s[N]; signed main(){ // freopen("in.in","r",stdin);freopen("out.out","w",stdout); std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0); int T;std::cin>>T; while(T--){ int n;std::cin>>n;n*=2; std::cin>>s+1; std::vector<int> v; int _1=0,_0=0; for(int i=1;i<=n;++i)_1+=s[i]=='1',_0+=s[i]=='0'; for(int i=1;i<=n;i+=2){ int x=i,y=i+1; if(s[x]!=s[y]){ if(v.size()){ if(s[x]!=s[v[v.size()-1]])v.push_back(x); else v.push_back(y); } else v.push_back(x); } } if(_1&1){std::cout<<-1<<'\n';continue;} std::cout<<v.size()<<' '; for(int x:v)std::cout<<x<<' '; std::cout<<'\n'; for(int i=1;i<=n;i+=2)std::cout<<i<<' ';std::cout<<'\n'; } }
这题真的从头到尾都想假了,如果想的总有hack,及时换思路。
6.21
P9510 『STA - R3』高维立方体
更多关于斐波那契数列的知识
设
引理一:
证明:显然
即
简单整理下所求式子,得
对于
故答案可以整理为
直接矩阵快速幂即可。
另外,这题也可以数形结合,用立方体的体积表示乘积,发现所有的
#include<bits/stdc++.h> #define int long long #define ll long long #define int long long int mod; using namespace std; inline int read(){ int x=0,f=1;char ch=getchar(); for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<1)+(x<<3)+(ch^48); return x*f; } inline int mo(int x){return x>=1e9?x%mod:x;} struct mt{ ll a[2][2]; mt(){memset(a,0,sizeof a);} mt operator*(const mt&b)const{ mt res; for(int i=0;i<=1;++i) for(int k=0;k<=1;++k){ int zc=a[i][k]; for(int j=0;j<=1;++j) res.a[i][j]=mo(res.a[i][j]+zc*b.a[k][j]); } return res; } }ans,base; inline int qpow(int b){ b--; ans.a[0][0]=0,ans.a[0][1]=1; base.a[0][1]=base.a[1][0]=base.a[1][1]=1; base.a[0][0]=0; while(b){ if(b&1)ans=ans*base; base=base*base;b>>=1; } return ans.a[0][1]; } signed main(){ // freopen("in.in","r",stdin);freopen("out.out","w",stdout); int T=read(); while(T--){ int n=read();mod=read(); int a=qpow(n),b=ans.a[0][0]+a; std::cout<<(a*a%mod*b%mod+b*a%mod)%mod<<'\n'; } }
6.23
AT_abc359_d [ABC359D] Avoid K Palindrome
看到 A
代表 B
代表 ?
都可以代表,设
显然有转移方程
#include<bits/stdc++.h> typedef long long ll; typedef unsigned long long ull; inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;} const int N=2e3+50,mod=998244353; char st[N]; int n,k,f[N][N]; bool vis[N]; inline int mo(int x){return x<0?(x%mod+mod)%mod:(x>=mod?x%mod:x);} inline bool check(int l,int r,int s){ int x=s; for(int i=r;i>=l;--i){ int zc=s&1;s>>=1; if(st[i]=='?')continue; if(st[i]-'A'!=zc)return false; } return (!vis[x]); } signed main(){ // freopen("in.in","r",stdin);freopen("out.out","w",stdout); std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0); std::cin>>n>>k>>(st+1); if(k==1){std::cout<<0<<'\n';exit(0);} vis[0]=1; for(int i=0;i<(1<<k)-1;++i){ int a[11],b[11]; for(int i=1;i<=k;++i)a[i]=b[i]=0; int len=0,x=i; while(x)len++,a[len]=b[len]=(x&1),x>>=1; len=k; std::reverse(b+1,b+len+1); int tot=0; for(int j=1;j<=len;++j)tot+=a[j]==b[j]; if(tot==len)vis[i]=1; f[k][i]=check(1,k,i); } int ans=0; for(int i=k+1;i<=n;++i) for(int s=0;s<(1<<k)-1;++s) if(check(i-k+1,i,s)){ int x=s>>1; f[i][s]=mo(f[i][s]+f[i-1][x+(1<<k-1)]+f[i-1][x]); } for(int s=0;s<(1<<k)-1;++s)ans=mo(ans+f[n][s]); std::cout<<ans<<'\n'; }
AT_abc359_e [ABC359E] Water Tank
如果一个水桶即将有水,那么之前的所有低于他的桶一定都装满了水,否则水不会过来。
所以需要维护一个高度单调递减的栈就行,就是单调栈板子题,时间复杂度
#include<bits/stdc++.h> #define int long long typedef long long ll; typedef unsigned long long ull; inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;} const int N=2e5+10; int n,h[N],f[N],head,tail,q[N]; signed main(){ // freopen("in.in","r",stdin);freopen("out.out","w",stdout); std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0); n=read();int ans=0; h[0]=2e18; for(int i=1;i<=n;++i){ h[i]=read(); int pos=0; while(head<=tail&&h[i]>h[q[tail]]){--tail;} pos=q[tail],q[++tail]=i; f[i]=f[pos]+(i-pos)*h[i]; std::cout<<f[i]+1<<' '; } }
AT_abc359_f [ABC359F] Tree Degree Optimization
对于任何一个度数合法的方案,它所对应的树一定是存在的,所以只需要考虑度数的分配即可。
为了保证连通,每个点至少有一个度,答案先记上这一点。然后剩下
#include<bits/stdc++.h> #define int long long typedef long long ll; typedef unsigned long long ull; inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;} const int N=2e5+10; int n,a[N]; struct QQ { int val,cnt,id; friend bool operator <(QQ a,QQ b){return a.val>b.val;} }; std::priority_queue<QQ> q; signed main(){ // freopen("in.in","r",stdin);freopen("out.out","w",stdout); std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0); n=read();int ans=0; for(int i=1;i<=n;++i){ int x=read(); ans+=x; q.push({x*3,1,x}); } for(int i=1;i<=n-2;++i){ auto it=q.top();q.pop(); ans+=it.val; int x=it.cnt+1; q.push({it.id*(x*2+1),x,it.id}); } std::cout<<ans<<'\n'; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧