241116 noip 模拟赛

省流:\(100 + 100 + 100 + 5\)

T1

题意:给一个括号序列 \(s\),求出长度最小的 \(s\) 的子序列 \(t\),满足 \(t\) 是合法括号序列且删掉 \(t\)\(s\) 是一个特殊的序列。定义特殊的序列为长度 \(2n\),前 \(n\) 个都是 (,后 \(n\) 个都是 )

\(n \leq 3 \times 10^6\)

可以枚举特殊序列左右括号的分界点,有个比较显然的贪心就是往左一个一个加入左括号看合不合法,往右一个一个加入右括号看合不合法。这具有单调性,所以可以双指针。

代码:

#include<bits/stdc++.h> using namespace std; const int N=3e6+5; string s; int a[N],l[N],r[N]; bool check() { int sum=0; for(int i=0; i<s.size(); i++) { sum+=(s[i]=='('); sum-=(s[i]==')'); if(sum<0) return false; } return sum==0; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr),cout.tie(nullptr); cin>>s;int n=s.size(); if(!check()) return cout<<-1,0; int tot=0,pos=1,sum1=0,sum2=0; for(int i=0; i<n-1; i++) { if(s[i]=='(') a[++tot]=0; else a[tot]++,sum2++; while(pos<=tot&&sum1<sum2) { sum1+=(a[pos]==0); sum2-=a[pos]; pos++; } l[i]=tot-pos+1; } tot=sum1=sum2=0;pos=1; for(int i=n-1; i>0; i--) { if(s[i]==')') a[++tot]=0; else a[tot]++,sum2++; while(pos<=tot&&sum1<sum2) { sum1+=(a[pos]==0); sum2-=a[pos]; pos++; } r[i-1]=tot-pos+1; } int ans=0; for(int i=0; i<n-1; i++) ans=max(ans,min(l[i],r[i])); cout<<n-2*ans; return 0; }

T2

原题:AT_cf17_final_g。

最优策略:每次选取第⼀个 \(a_i = i\) 的位置,然后操作,这样不会破坏已经可以操作的位置的性质,同时可以尽可能多地操作。

分析发现,每次操作会把整个序列的总和 \(-1\)。所以可以考虑计算出所有不同初始值下 \(-i\) 的方案数总和。本质上就是计算出了所有情况下减去的总和。再用所有方案数的总和减去这个方案数就得到最终所有情况的答案。

而每个点只会被后⾯的点操作,于是我们倒着 dp,记 \(dp_{i,j}\) 表示到了第 \(i\) 位,它后面的位置总共对它造成了 \(j\)\(+1\) 的操作”的方案数,然后我们枚举第 \(i\) 位的初始值 \(x\),如果可以操作那么⼀定可以在 \(i\) 处操作了 \(\lfloor \frac{x + j}{i} \rfloor\) 次操作,可以操作代表的是初始值 \(\leq i\)。那么最后怎么统计答案呢?发现 \(dp_{1,i}\),也就是 \(1\)\(+1\) 的次数为i时,总体是⼀共做了 \(i\) 次总和 \(-1\),所以所有减去的 \(1\) 的总和就是 \(\sum dp_{1,i} \times i\)

代码:

#include<bits/stdc++.h> #define int long long using namespace std; const int N=105,p=1e9+7; int n,k,dp[N][N*N],ans=0; int qpow(int a,int b) { int ans=1; while(b) { if(b&1) ans=ans*a%p; a=a*a%p; b>>=1; } return ans; } signed main() { cin>>n>>k; dp[k][0]=k,dp[k][1]=1; for(int i=k; i>1; i--) { for(int j=0; j<=k*k; j++) { for(int l=0; l<i; l++) if(j+(l+j)/(i-1)<=k*k) (dp[i-1][j+(l+j)/(i-1)]+=dp[i][j])%=p; (dp[i-1][j]+=dp[i][j]*(k-i+1)%p)%=p; } } for(int i=1; i<=n; i++) (ans+=k*(k+1)/2*qpow(k+1,n-1)%p)%=p; for(int i=0; i<=k*k; i++) (ans+=-dp[1][i]*i%p*qpow(k+1,n-k)%p+p)%=p; cout<<ans; return 0; }

T3

原题:CF177G2。

对于 \(n \leq 28\),直接暴力做就行。接下来讨论 \(n > 28\) 的情况。

对于一个字符串,设它在 \(f_i\) 的出现次数为 \(g_i\),那么有 \(g_i = g_{i - 1} + g_{i - 2} + x\)\(x\) 表示这个字符串在 \(f_i\) 出现次数减 \(f_{i - 1}\) 出现次数减 \(f_{i - 2}\) 出现次数,即跨越 \(f_{i - 1}\)\(f_{i - 2}\) 的出现次数。观察一下,发现 \(i\) 为奇数时,\(f_i\) 可以被表示为 \(\cdots + f_{26} + f_{26} + \cdots\),最中间的即为 \(f_{i - 1}\)\(f_{i - 2}\) 的分界点;当 \(i\) 为偶数时,\(f_i\) 可以被表示为 \(\cdots + f_{27} + f_{26} + \cdots\),分界点同理。由于 \(f_{26}\) 已经大于 \(10^5\) 了,所以 \(x\) 只分奇数和偶数两种取值,算出这两种取值按照上面的递推式矩阵快速幂优化即可。

暴力和算 \(x\) 我用了 ACAM 解决。好像可以用其他的,应该是我赛时唐了。

代码:

#include<bits/stdc++.h> #define int long long using namespace std; const int N=1e5+5,p=1e9+7; string s[N]; int n,q,tot=0,ch[N][2],fl[N],cnt[N],id[N],head[N],ecnt=0; struct node {int to,nxt;}e[N]; void addedge(int u,int v) {e[++ecnt]=(node){v,head[u]};head[u]=ecnt;} struct matrix { int c[6][6]; matrix operator*(const matrix &o) const { matrix x; memset(x.c,0,sizeof(x.c)); for(int i=1; i<=5; i++) for(int j=1; j<=5; j++) for(int k=1; k<=5; k++) x.c[i][j]=(x.c[i][j]+c[i][k]*o.c[k][j]%p)%p; return x; } }a,ans[N]; matrix qpow(matrix a,int b) { matrix ans; memset(ans.c,0,sizeof(ans.c)); for(int i=1; i<=5; i++) ans.c[i][i]=1; while(b) { if(b&1) ans=ans*a; a=a*a; b>>=1; } return ans; } void add(string s,int ii) { int cur=0; for(int i=0; i<s.size(); i++) { int b=s[i]-'0'; if(!ch[cur][b]) ch[cur][b]=++tot; cur=ch[cur][b]; } id[ii]=cur; } void build_fail() { queue<int> q; for(int i=0; i<2; i++) if(ch[0][i]) q.push(ch[0][i]); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0; i<2; i++) { if(ch[u][i]) fl[ch[u][i]]=ch[fl[u]][i],q.push(ch[u][i]); else ch[u][i]=ch[fl[u]][i]; } } } void query(string t) { int cur=0; for(int i=0; i<t.size(); i++) { int b=t[i]-'0'; cur=ch[cur][b]; cnt[cur]++; } } void dfs(int u) { for(int i=head[u]; i; i=e[i].nxt) { int v=e[i].to; dfs(v);cnt[u]+=cnt[v]; } } string make(int lim) { string s1="0",s2="1",s3; for(int i=3; i<=lim; i++) { s3=s2+s1; s1=s2,s2=s3; } return s3; } void init1() { a.c[1][1]=0,a.c[1][2]=0,a.c[1][3]=0,a.c[1][4]=0,a.c[1][5]=0; a.c[2][1]=0,a.c[2][2]=1,a.c[2][3]=1,a.c[2][4]=0,a.c[2][5]=0; a.c[3][1]=1,a.c[3][2]=1,a.c[3][3]=2,a.c[3][4]=0,a.c[3][5]=0; a.c[4][1]=0,a.c[4][2]=1,a.c[4][3]=1,a.c[4][4]=1,a.c[4][5]=0; a.c[5][1]=0,a.c[5][2]=0,a.c[5][3]=1,a.c[5][4]=0,a.c[5][5]=1; } void init2(string t,int ii) { query(t);dfs(0); for(int i=1; i<=q; i++) ans[i].c[1][ii]+=cnt[id[i]]; memset(cnt,0,sizeof(cnt)); } signed main() { freopen("fib.in","r",stdin); freopen("fib.out","w",stdout); ios::sync_with_stdio(false); cin.tie(nullptr),cout.tie(nullptr); cin>>n>>q; for(int i=1; i<=q; i++) cin>>s[i],add(s[i],i); build_fail(); for(int i=1; i<=tot; i++) addedge(fl[i],i); if(n<=28) { string t=make(n); query(t);dfs(0); for(int i=1; i<=q; i++) cout<<cnt[id[i]]<<'\n'; } else { string t1=make(26),t2=make(27),t3=make(28); init1(),init2(t1,1),init2(t2,2),init2(t3,3),init2(t1+t1,4),init2(t2+t1,5); for(int i=1; i<=q; i++) ans[i].c[1][4]-=2*ans[i].c[1][1],ans[i].c[1][5]-=ans[i].c[1][1]+ans[i].c[1][2]; matrix tmp=qpow(a,(n-27)/2); for(int i=1; i<=q; i++) { ans[i]=ans[i]*tmp; if(n&1) cout<<ans[i].c[1][2]<<'\n'; else cout<<ans[i].c[1][3]<<'\n'; } } return 0; }

T4

原题:P6305。

洛谷题解讲的挺好,不写了,其实是懒得写

代码:

#include<bits/stdc++.h> #define endl '\n' using namespace std; const int N=4e5+5; vector<int> ve; int n,s,a[N],b[N],c[N],vis[N],vise[N],nxt[N],head[N],ecnt=0; struct node {int to,nxt;}e[N]; void add(int u,int v) {e[++ecnt]=(node){v,head[u]};head[u]=ecnt;} void dfs(int u) { for(int& i=head[u]; i; i=e[i].nxt) { int v=e[i].to; if(vise[i]) continue; vise[i]=true,dfs(v); } if(u<=n) ve.push_back(u),vis[u]=true; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr),cout.tie(nullptr); cin>>n>>s; for(int i=1; i<=n; i++) cin>>a[i],b[i]=a[i]; sort(b+1,b+n+1); int len=unique(b+1,b+n+1)-b-1; for(int i=1; i<=n; i++) a[i]=lower_bound(b+1,b+len+1,a[i])-b,c[i]=a[i]; sort(c+1,c+n+1); for(int i=1; i<=n; i++) { if(a[i]==c[i]) continue; add(i,n+a[i]);add(n+c[i],i); } vector<vector<int>> tmp; for(int i=1; i<=n; i++) { if(vis[i]||a[i]==c[i]) continue; ve.clear();dfs(i); ve.pop_back();s-=ve.size(); reverse(ve.begin(),ve.end()); tmp.push_back(ve); } if(s<0) return cout<<-1,0; if(!tmp.size()) return cout<<0,0; if(tmp.size()==1) { cout<<1<<endl<<tmp[0].size()<<endl; for(int i=0; i<tmp[0].size(); i++) cout<<tmp[0][i]<<" "; return 0; } if(s>=tmp.size()) { cout<<2<<endl; cout<<tmp.size()<<endl; for(int i=0; i<tmp.size(); i++) cout<<tmp[i][0]<<" "; cout<<endl; int sum=0; for(int i=0; i<tmp.size(); i++) sum+=tmp[i].size(); cout<<sum<<endl; for(int i=0; i<tmp.size(); i++) { for(int j=1; j<tmp[i].size(); j++) nxt[tmp[i][j]]=tmp[i][(j+1)%tmp[i].size()]; nxt[tmp[i][0]]=tmp[(i-1+tmp.size())%tmp.size()][1]; } int cur=tmp[0][0]; do cout<<cur<<" ",cur=nxt[cur]; while(cur!=tmp[0][0]); } else { cout<<tmp.size()-max(0,s-1)+(s>=2)<<endl; if(s>=2) { cout<<s<<endl; for(int i=0; i<s; i++) cout<<tmp[i][0]<<" "; cout<<endl; int sum=0; for(int i=0; i<s; i++) sum+=tmp[i].size(); cout<<sum<<endl; for(int i=0; i<s; i++) { for(int j=1; j<tmp[i].size(); j++) nxt[tmp[i][j]]=tmp[i][(j+1)%tmp[i].size()]; nxt[tmp[i][0]]=tmp[(i-1+s)%s][1]; } int cur=tmp[0][0]; do cout<<cur<<" ",cur=nxt[cur]; while(cur!=tmp[0][0]); cout<<endl; } for(int i=(s==1?0:s); i<tmp.size(); i++) { cout<<tmp[i].size()<<endl; for(int j=0; j<tmp[i].size(); j++) cout<<tmp[i][j]<<" "; cout<<endl; } } return 0; }

__EOF__

本文作者System_Error
本文链接https://www.cnblogs.com/System-Error/p/18549508.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   System_Error  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示