Simple Final Test 题解
往届学长的一场比赛.
总结:三道水题 haha!
Task 1 省选 jloi.cpp/in/out
给你一个字符串,你每次只能删除一个回文串,问你最少需要几步将字符串删完,或输出无解.
题解:显然,答案最多为 2,而如果这个串本身就不是回文串答案就是 1,所以难点就是判断无解的情况.
手画几组发现无解有两种情况:s[1...len/2] 与 s[n-len/2+1....n] 回文(这里是向下取整),和 abababababa 这种两个字母交替的形式.
判一下就好了.
总结:很快想到了答案只有 3 种情况,但是开始的时候没有讨论好无解的情况. 正式考试中的话肯定会打一个表肉眼找找规律吧,属于简单题.
#include <bits/stdc++.h> #define N 100009 #define ll long long #define setIO(s) freopen(s".in","r",stdin) , freopen(s".out","w",stdout) using namespace std; int n; char str[N]; int check1() { int len=n/2,flag=0; for(int i=1;i<=len;++i) if(str[i]!=str[n-i+1]) flag=1; return flag; } int check3() { int len=n/2,f1=1,f2=1; // 中间相等 for(int i=2;i<=len;++i) if(str[i]!=str[i-1]) f1=0; if(str[len]!=str[n-len+1]) f1=0; for(int i=n-len+2;i<=n;++i) if(str[i]!=str[i-1]) f1=0; // printf("%d\n",flag); // 奇偶对称 if(n&1) { for(int i=3;i<=n;i+=2) if(str[i]!=str[i-2]) f2=0; for(int i=4;i<n;i+=2) if(str[i]!=str[i-2]) f2=0; } else f2=0; return f1||f2; } void solve() { scanf("%d%s",&n,str+1); if(check1()) printf("1\n"); else if(check3()) printf("-1\n"); else printf("2\n"); } int main() { // setIO("jloi"); int T; scanf("%d",&T); for(int i=1;i<=T;++i) { solve(); } // while(T--) solve(); return 0; }
Task 2 祝 good.cpp/in/out
给定一张有向图,每次可以选择若干个没有先后继关系的点进行删除.
求:最少要几步才能将所有点删没.
题解:刚开始看到这道题的时候没什么思路,然后就想着能否从入度为 0 的点开始删.
显然,如果选择这张图初始状态下所有入度为 0 的点进行删除的话其他所有点都是不可以选的.
然后思考了一下感觉这样做貌似十分有道理,就写了一个程序,然后就过了.
总结:正统的做法应该马上想到答案不小于于最长链长度,然后发现每次删除入度为 0 的点可以达到这个下界.
难度不大,但是有些代码细节需要注意.
#include <cstdio> #include <ctime> #include <cstring> #include <algorithm> #include <map> #include <stack> #include <queue> #include <vector> #define N 1000009 #define ll long long #define setIO(s) freopen(s".in","r",stdin) , freopen(s".out","w",stdout) using namespace std; int n,m; stack<int>S; queue<int>q; map<int,int>vis[N]; vector<int>G[N]; int low[N],dfn[N],idx[N],size[N],tim,scc; int hd[N],to[N],nex[N],deg[N],f[N],edges; void add(int u,int v) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; } void tarjan(int x) { S.push(x),dfn[x]=low[x]=++tim; for(int i=hd[x];i;i=nex[i]) { int y=to[i]; if(!dfn[y]) { tarjan(y); low[x]=min(low[x],low[y]); } else if(!idx[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]) { ++scc; for(;;) { int u=S.top(); S.pop(); idx[u]=scc; ++size[scc]; if(u==x) break; } } } char *p1,*p2,buf[100000]; #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++) int rd() { int x=0; char c; do { c=nc(); } while(c<48); while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc(); return x; } int main() { // setIO("good"); // int T=clock(); int x,y,z; n=rd(),m=rd(); for(int i=1;i<=m;++i) x=rd(),y=rd(),add(x,y); for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;++i) { int u=idx[i]; for(int j=hd[i];j;j=nex[j]) { y=to[j]; int v=idx[y]; if(u==v) continue; if(!vis[u][v]) { vis[u][v]=1; G[u].push_back(v); ++deg[v]; } } } for(int i=1;i<=scc;++i) { if(!deg[i]) { f[i]=size[i]; q.push(i); } } while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0;i<G[u].size();++i) { int v=G[u][i]; --deg[v]; f[v]=max(f[v],f[u]+size[v]); if(!deg[v]) q.push(v); } } int ans=0; for(int i=1;i<=scc;++i) ans=max(ans,f[i]); printf("%d\n",ans); // printf("%d\n",clock()-T); return 0; }
Task 3 顺利 luck.cpp/in/out
题意:给定一棵有根树,每个点都有种颜色,有两个操作:
1. 修改一个点的颜色
2. 查询一个点子树中不同颜色种类数.
直接用带修改莫队+树状数组维护就好了.
时间复杂度为 $O(n^{\frac{5}{3}} \log n ).$
但是这是极限复杂度,而且树状数组的 log 非常小,所以跑的飞快.
总结:算是本场考试稍微有点难度的题,但是一眼就能看出是带修改莫队+树状数组维护,难度其实也不大.
code:
#include <cstdio> #include <ctime> #include <cstring> #include <algorithm> #include <map> #include <stack> #include <queue> #include <vector> #define N 1000009 #define ll long long #define setIO(s) freopen(s".in","r",stdin) , freopen(s".out","w",stdout) using namespace std; int n,m; stack<int>S; queue<int>q; map<int,int>vis[N]; vector<int>G[N]; int low[N],dfn[N],idx[N],size[N],tim,scc; int hd[N],to[N],nex[N],deg[N],f[N],edges; void add(int u,int v) { nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; } void tarjan(int x) { S.push(x),dfn[x]=low[x]=++tim; for(int i=hd[x];i;i=nex[i]) { int y=to[i]; if(!dfn[y]) { tarjan(y); low[x]=min(low[x],low[y]); } else if(!idx[y]) low[x]=min(low[x],dfn[y]); } if(dfn[x]==low[x]) { ++scc; for(;;) { int u=S.top(); S.pop(); idx[u]=scc; ++size[scc]; if(u==x) break; } } } char *p1,*p2,buf[100000]; #define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++) int rd() { int x=0; char c; do { c=nc(); } while(c<48); while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc(); return x; } int main() { // setIO("good"); int x,y,z; n=rd(),m=rd(); for(int i=1;i<=m;++i) x=rd(),y=rd(),add(x,y); for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;++i) { int u=idx[i]; for(int j=hd[i];j;j=nex[j]) { y=to[j]; int v=idx[y]; if(u==v) continue; if(!vis[u][v]) { vis[u][v]=1; G[u].push_back(v); ++deg[v]; } } } for(int i=1;i<=scc;++i) { if(!deg[i]) { f[i]=size[i]; q.push(i); } } while(!q.empty()) { int u=q.front(); q.pop(); for(int i=0;i<G[u].size();++i) { int v=G[u][i]; --deg[v]; f[v]=max(f[v],f[u]+size[v]); if(!deg[v]) q.push(v); } } int ans=0; for(int i=1;i<=scc;++i) ans=max(ans,f[i]); printf("%d\n",ans); return 0; }
总结:虽说题的难度不大,但是自己 AK 了还是蛮开心的.