【学习笔记】AGC055

A - ABC Identity

如果只有 A A A, B B B两种字符的话,我们发现要寻找 p ∈ [ 1 , n ] p\in [1,n] p[1,n],使得 [ 1 : p ] [1:p] [1:p] A A A的数目与 [ p + 1 : n ] [p+1:n] [p+1:n] B B B的数目相同。

如果有 A , B , C A,B,C A,B,C三种字符,我们可以先将 A , B A,B A,B分离出来,再将 B , C B,C B,C分离出来,最后把 A , C A,C A,C分离出来,这样最后会生成 8 8 8个子序列 然后无法通过

神谕告诉我们, A , B , C A,B,C A,B,C三种字符一共只有 6 6 6种本质不同的排列,因此我们可以考虑把原序列分成长度为 n n n 3 3 3段,从每一段中分别选取一个字符构成 A , B , C A,B,C A,B,C的排列,最后把相同的排列放在一起即可。猜一下结论,这显然是有解的。

这种题还是要多尝试

#include<bits/stdc++.h> #define pb push_back using namespace std; vector<int>v[3][3]; string s; int n,res[600005]; int has(int x,int y,int z){ return x*2+(y-(x<y))*1; } int main(){ cin>>n>>s; for(int i=0;i<3*n;i++){ v[i/n][s[i]-'A'].pb(i); } for(int i=1;i<=n;i++){ for(int j=0;j<3;j++){ for(int k=0;k<3;k++){ for(int l=0;l<3;l++){ if(v[0][j].size()&&v[1][k].size()&&v[2][l].size()&&j!=k&&k!=l&&j!=l){ res[v[0][j].back()]=has(j,k,l); res[v[1][k].back()]=has(j,k,l); res[v[2][l].back()]=has(j,k,l); v[0][j].pop_back(); v[1][k].pop_back(); v[2][l].pop_back(); } } } } }for(int i=0;i<3*n;i++)cout<<res[i]+1; }

B - ABC Supremacy

如果只考虑 S S S怎么生成 T T T的话,怎么做都是 O ( n 2 ) O(n^2) O(n2)的。数据删除

上面那种思路可能不太好处理 但是操作是可逆的,因此判断 S , T S,T S,T同构的一个充分条件是它们都能到达一个相同的状态 P P P。所以我们只要求出 S , T S,T S,T的最小表示即可,这样一个字符串生成的表示是唯一的,就不用担心上述问题了。

剩下的就是怎么去寻找最小串。比较烦恼就先咕了

显然我们要凑出尽量多的 A B C ABC ABC串(这里指轮换),并且每次操作相当于将 A B C ABC ABC串这个整体挪到前面,然后把 A A A放在最前面。那么 B C BC BC是固定的吗?如果 B C BC BC不是固定的,这个问题也比较烦恼,可以先咕着

开始慌张 不过幸运的是之前的结论还是正确的

我们可以把最小表示的定义换成 得到最多的 A B C ABC ABC轮换组,那么 B C BC BC就肯定是固定的了。

复杂度 O ( n ) O(n) O(n)

#include<bits/stdc++.h> #define pb push_back using namespace std; int n; string s,t; vector<char>solve(string s){ vector<char>v; for(int i=0;i<s.size();i++){ v.pb(s[i]); if(v.size()>=3&&(v[v.size()-3]=='A'&&v[v.size()-2]=='B'&&v[v.size()-1]=='C'||v[v.size()-3]=='B'&&v[v.size()-2]=='C'&&v[v.size()-1]=='A'||v[v.size()-3]=='C'&&v[v.size()-2]=='A'&&v[v.size()-1]=='B')){ v.pop_back(); v.pop_back(); v.pop_back(); } }return v; } int main(){ cin>>n>>s>>t; cout<<(solve(s)==solve(t)?"YES":"NO"); }

C - Weird LIS

如果我们能思考清楚 { A i } \{A_i\} {Ai}合法的充要条件,那么这道题也就解决了。

或者说能建立双射然后计数也行

想不太清楚所以先咕了

思路其实并不困难,不过可能需要猜几个结论。

1.1 1.1 1.1 如果 A i A_i Ai全部等于 K K K,猜测 K ≤ ⌊ n 2 ⌋ K\le \lfloor\frac{n}{2}\rfloor K2n,这还是比较容易看出来。
1.2 1.2 1.2 如果 K K K K − 1 K-1 K1同时存在,那么 A i = K − 1 A_i=K-1 Ai=K1的那些点是固定的,我们要在所有 A i = K A_i=K Ai=K的连续段中挑选一段接在固定的数之间,那么根据 1.1 1.1 1.1的推论,这一段的长度不能超过 ⌊ l 2 ⌋ \lfloor\frac{l}{2}\rfloor 2l l l l表示连续段长度),我们猜测对于更小的情况也是取得到的,因此 ∑ ⌊ l i 2 ⌋ + c n t K − 1 ≥ K \sum{\lfloor\frac{l_i}{2}\rfloor}+cnt_{K-1}\ge K 2li+cntK1K,并且 c n t K − 1 ≤ K cnt_{K-1}\le K cntK1K

这个向下取整好像不太妙,先咕了

计数这个地方可能要多尝试

复杂度 O ( n 2 ) O(n^2) O(n2)。不过要注意特判 A i = n − 1 A_i=n-1 Ai=n1的情况。

#include<bits/stdc++.h> #define ll long long #define pb push_back using namespace std; const int N=5005; int n,m,mod; ll fac[N],inv[N],res; ll pw(ll x,ll y=mod-2){ ll z(1); for(;y;y>>=1){ if(y&1)z=z*x%mod; x=x*x%mod; }return z; } void init(int n){ fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod; inv[n]=pw(fac[n]);for(int i=n;i>=1;i--)inv[i-1]=inv[i]*i%mod; } ll binom(int x,int y){ if(x<0||y<0||x<y)return 0; return fac[x]*inv[y]%mod*inv[x-y]%mod; } int main(){ cin>>n>>m>>mod,init(n+1); for(int x=1;x<n;x++){ for(int y=0;y<=(n-x)/2;y++){ int L=max(3,x),R=min(m,x+y); if(L<=R)res=(res+binom(x+y,x)*binom(x+1,n-x-2*y)%mod*(R-L+1))%mod; } } for(int i=2;i<=min(m,n/2);i++)res++; if(m==n-1)res++; cout<<res%mod; }

D - ABC Ultimatum

先考虑怎么判断给定串合法。

好像没什么思路先咕了

不过这题还是有学习的价值的,我们可以照着结论来翻译一下

S a ( i ) , S b ( i ) , S c ( i ) S_a(i),S_b(i),S_c(i) Sa(i),Sb(i),Sc(i)表示 1 ∼ i 1\sim i 1i a , b , c a,b,c a,b,c的个数, M a = max ⁡ ( S b ( i ) − S a ( i ) ) , M b = max ⁡ ( S c ( i ) − S b ( i ) ) , M c = max ⁡ ( S a ( i ) − S c ( i ) ) M_a=\max (S_b(i)-S_a(i)),M_b=\max(S_c(i)-S_b(i)),M_c=\max(S_a(i)-S_c(i)) Ma=max(Sb(i)Sa(i)),Mb=max(Sc(i)Sb(i)),Mc=max(Sa(i)Sc(i)),则 S S S是好的当且仅当 M a + M b + M c ≤ n M_a+M_b+M_c\le n Ma+Mb+Mcn

必要性应该很显然,可以猜一个结论,或者打表证明这是充要的。

可能有时间会补一下证明

然后暴力复杂度 O ( n 7 ) O(n^7) O(n7)。但是很显然可以省去一维状态,因此就可以在 O ( n 6 ) O(n^6) O(n6)时间内通过了。

#include<bits/stdc++.h> #define ll long long #define pb push_back using namespace std; const int mod=998244353; int n,dp[17][17][17][17][17][17],res; string s; void add(int &x,int y){ if((x+=y)>=mod)x-=mod; } int main(){ cin>>n>>s;dp[0][0][0][0][0][0]=1; for(int i=0;i<3*n;i++){ for(int a=0;a<=n;a++){ for(int b=0;b<=n;b++){ int c=i-a-b;if(c>n||c<0)continue; for(int j=0;j<=n;j++){ for(int k=0;k<=n;k++){ for(int l=0;l<=n;l++){ int tmp=dp[a][b][c][j][k][l]; if(s[i]=='A'||s[i]=='?'){ add(dp[a+1][b][c][j][k][max(l,a+1-c)],tmp); }if(s[i]=='B'||s[i]=='?'){ add(dp[a][b+1][c][max(b+1-a,j)][k][l],tmp); }if(s[i]=='C'||s[i]=='?'){ add(dp[a][b][c+1][j][max(c+1-b,k)][l],tmp); } } } } } } } for(int i=0;i<=n;i++){ for(int j=0;j<=n;j++){ for(int k=0;k<=n;k++){ if(i+j+k<=n)add(res,dp[n][n][n][i][j][k]); } } }cout<<res; }

E - Set Merging

场上无一人AC

这种给你规定输入的构造题就很烦,那么我们就要去分析一些性质,看它在不同情况下是否成立。

某个人曾经说过,第一个做出这种题的人一定是具有非凡的人类智慧的

想了一下,发现自己人类智慧不足,于是滚去看题解

大概思路是,构造一个排列 { p i } \{p_i\} {pi},初始 p i = i p_i=i pi=i,合并 S i , S i + 1 S_i,S_{i+1} Si,Si+1就等价于交换 p i , p i + 1 p_i,p_{i+1} pi,pi+1。那么相当于要求一个排列 p p p,使其能导出 { l i } , { r i } \{l_i\},\{r_i\} {li},{ri}并且逆序对最小。

然后把该确定的位置确定,剩下的猜一个结论让逆序对最小,可能需要一些观察。

我们只需要发现,对于不能确定的位置,一定满足 l i = l i + 1 , r i − 1 = r i l_{i}=l_{i+1},r_{i-1}=r_i li=li+1,ri1=ri,换句话说 p i ∈ [ l i , r i ] p_i\in [l_i,r_i] pi[li,ri],又因为 l i , r i l_i,r_i li,ri都是单增的,因此我们不难猜出结论:只要填区间 [ l i , r i ] [l_i,r_i] [li,ri]中最小的并且没有被选择的数即可,这显然也满足了逆序对数目最小。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include<bits/stdc++.h> #define ll long long #define pb push_back #define fi first #define se second using namespace std; const int N=5e5+5; int n,L[N],R[N],p[N],bit[N]; ll res; set<int>S; void add(int x){ for(;x<=n;x+=x&-x)bit[x]++; } int ask(int x){ int res(0); for(;x;x-=x&-x){ res+=bit[x]; } return res; } void ins(int x,int y){ if(!p[x]){ if(!S.count(y)){ cout<<-1; exit(0); } p[x]=y; S.erase(y); } else if(p[x]!=y){ cout<<-1; exit(0); } } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n;for(int i=1;i<=n;i++)cin>>L[i]>>R[i]; for(int i=1;i<=n;i++)S.insert(i); ins(n,L[n]); for(int i=n-1;i>=1;i--){ if(L[i]>L[i+1]){ cout<<-1; return 0; } if(L[i]<L[i+1]){ ins(i,L[i]); } } ins(1,R[1]); for(int i=2;i<=n;i++){ if(R[i]<R[i-1]){ cout<<-1; return 0; } if(R[i]>R[i-1]){ ins(i,R[i]); } } for(int i=1;i<=n;i++){ if(!p[i]){ auto it=S.lower_bound(L[i]); if(it==S.end()||*it>R[i]){ cout<<-1; return 0; } ins(i,*it); } } for(int i=1;i<=n;i++){ add(p[i]); res+=i-ask(p[i]); }cout<<res; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530024.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(8)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-02-24 【题解】「BZOJ4504」K个串
点击右上角即可分享
微信分享提示