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 了还是蛮开心的.

浙公网安备 33010602011771号