点此进入比赛

\(A\):Ivan the Fool and the Probability Theory(点此看题面

大致题意: 给一个\(n\times m\)的矩阵\(01\)染色,使得不存在某个同色连通块大小超过\(2\)。

这道题看似很神仙,实际上仔细想一想、推一推性质,还是比较简单的。

先考虑第一行,这是一个\(1\times m\)的矩阵,可以设\(f_{i,0/1}\)为第\(i\)个格子所填与上个格子不同/相同的方案数,则:\(f_{i,0}=f_{i-1,0}+f_{i-1,1},f_{i,1}=f_{i-1,0}\)。

由于第一个位置可填\(0\)可填\(1\),所以初始化\(f_{1,0}=2,f_{1,1}=0\)。

接下来,我们考虑从第一行向下扩展,分两种情况讨论:

  • 如果第一行不存在连续两个格子所填相同,则每一行填的数只能与上一行完全相反或是完全相同,且不能有连续超过\(2\)行完全相同。此时第一行只有\(0101...01\)或\(1010...10\)两种情况,而若我们把\(0101...01\)看作\(0\),\(1010...10\)看作\(1\),那么就相当于对\(n\times 1\)的矩阵\(01\)染色,所以方案数为\(f_{n,0}+f_{n,1}\)。
  • 如果第一行存在连续的两个格子所填相同,则每一行填的数只能与上一行完全相反。此时第一行有\(f_{m,0}+f_{m,1}-2\)种填法(除去第一行不存在连续两个格子所填相同的两种填法),每种第一行填法对应唯一一种全局填法,所以方案数为\(f_{m,0}+f_{m,1}-2\)。

综上所述,总方案数为\(f_{n,0}+f_{n,1}+f_{m,0}+f_{m,1}-2\)。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define X 1000000007
using namespace std;
int n,m,f[N+5][2];
I int Qpow(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
int main()
{
RI i;scanf("%d%d",&n,&m);
for(f[1][0]=2,i=2;i<=max(n,m);++i) f[i][0]=(f[i-1][0]+f[i-1][1])%X,f[i][1]=f[i-1][0];
return printf("%d",(1LL*f[n][0]+f[n][1]+f[m][0]+f[m][1]-2+X)%X),0;
}

\(B\):The World Is Just a Programming Task (Hard Version)(点此看题面

大致题意: 定义一个括号序列\(S\)的美丽值为有多少个\(p\)使能得\(S_{p,n}S_{1,p-1}\)为合法括号序列。现在你可以交换一个括号序列中两个括号,求所能得到的最大的美丽值及交换的位置。

细节巨多的题目,写了一个下午......

首先,如果\(S\)的左右括号数量不同,直接输出\(0\)和\(1\ 1\)走人。

然后,我们考虑,对于一个括号序列,它的美丽值的意义。然后不难推导出,如果我们把(看作\(1\),)看作\(-1\),求出前缀和\(s\)数组后,美丽值就是\(s\)中的最小值个数

假设我们交换括号\(x,y(x<y)\),\(s\)中原本的最小值为\(Mn\)。

我的做法是,先对于\(x\)是左括号还是右括号进行讨论,然后再分别继续处理。

  • 对于\(x\)是左括号的情况。此时,就相当于\(s_{x\sim y-1}\)减去\(2\)。

    • 如果\(s_{x\sim y-1}\)中的最小值是\(Mn\),则操作后\(s\)中的最小值是\(Mn-2\)。显然答案不会更优。
    • 如果\(s_{x\sim y-1}\)中的最小值是\(Mn+1\),则操作后\(s\)中的最小值是\(Mn-1\)。答案就是\(s_{x\sim y-1}\)中\(Mn+1\)的个数。
    • 如果\(s_{x\sim y-1}\)中的最小值是\(Mn+2\),则操作后\(s\)中的最小值依然是\(Mn-2\)。答案就是\(s_{x\sim y-1}\)中\(Mn+2\)的个数加上整个\(s\)中\(Mn\)的个数。
  • 对于\(x\)是右括号的情况。此时,就相当于\(s_{x\sim y-1}\)加上\(2\),也就是\(s_{1\sim x-1},s_{y\sim n}\)减去\(2\),随后的讨论与上面类似。

通过上面的讨论,不难发现,在最小值不变的情况下,变化区间肯定是范围越大越好,因此我们用两个双指针来维护出最小值为\(Mn+1,Mn+2\)的两个答案区间即可。

具体实现可能有些细节要注意,可详见代码。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con cosnt
#define CI Con int&
#define I inline
#define W while
#define N 300000
using namespace std;
int n,a[N+5],p[N+5];char s[N+5];
int main()
{
RI i,Mn=n,res=0,ans=0,tx=1,ty=1;for(scanf("%d%s",&n,s+1),i=1;i<=n;++i)//读入+转化
a[i]=s[i]=='('?1:-1,p[i]=p[i-1]+a[i],Mn>p[i]?(Mn=p[i],res=1):Mn==p[i]&&++res;
if(p[n]) return puts("0\n 1 1"),0;ans=res;RI t=0,g=0;//如果左右括号数量不同,直接输出
RI nxt1=n,nxt2=n;for(i=n-1;i;--i)//对于左边是左括号
p[i]==Mn?(t=0,nxt1=i):(t+=p[i]==Mn+1),//维护最小值是Mn+1的答案区间
(p[i]==Mn||p[i]==Mn+1)?(g=0,nxt2=i):(g+=p[i]==Mn+2),//维护最小值是Mn+2的答案区间
~a[i]&&(ans<t&&(ans=t,tx=i,ty=nxt1),ans<res+g&&(ans=res+g,tx=i,ty=nxt2));//更新答案
RI v=0,w=0,k=0;for(t=g=0,i=1;i<=n;++i) p[i]==Mn+1&&++t,p[i]==Mn+2&&++g;//初始化
RI lim=t;for(nxt1=nxt2=i=1;i<=n;++i)//对于左边是右括号
{
W(v^res) p[nxt1]==Mn&&++v,p[nxt1]==Mn+1&&--t,nxt1=nxt1%n+1;//维护最小值是Mn+1的答案区间
W(w^lim||k^res) p[nxt2]==Mn+1&&++w,p[nxt2]==Mn&&++k,p[nxt2]==Mn+2&&--g,nxt2=nxt2%n+1;//维护最小值是Mn+2的答案区间
!~a[i]&&ans<t&&(ans=t,tx=i,ty=nxt1),p[i]==Mn&&--v,p[i]==Mn+1&&++t,//更新最小值是Mn+1的答案,然后更新区间
!~a[i]&&ans<res+g&&(ans=res+g,tx=i,ty=nxt2),p[i]==Mn+1&&--w,p[i]==Mn&&--k,p[i]==Mn+2&&++g;//更新最小值是Mn+2的答案,然后更新区间
}
return printf("%d\n%d %d",ans,tx,ty),0;//输出答案
}

\(C\):Queue in the Train(点此看题面

大致题意: 有\(n\)个人要灌水,每个人灌水用时皆为一个定值。每个人会在第\(p_i\)刻开始想要灌水,如果大于等于\(p_i\)的某一时刻他前方没有人在排队,他就会去排队。求每个人灌完水的时间。

模拟题,一开始理解错几个细节就挂飞了......

其实这道题说简单也很简单,个人认为我的做法可能有点复杂......

首先,我们把人以开始灌水时间为第一关键字、编号为第二关键字,排序,并用一个指针\(i\)来表示排序后的前\(i\)个人当前能够灌水。

然后,我们开一个小根堆(优先队列),把当前能够灌水的人都扔到堆里。

扔的同时,我们要判断这个人是否能去排队了,这只需要判断他之前是否有人去排队了,因此我写了一个树状数组。

如果能排队,我们就把它扔到一个队列里,果然是排队。

还有一些细节自己注意一下吧,下面放出代码以供参考。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define LL long long
using namespace std;
int n,m,a[N+5];LL ans[N+5];queue<int> q;
struct data
{
int p,v;I data(CI x=0,CI y=0):p(x),v(y){}
I bool operator < (Con data& o) Con {return v^o.v?v<o.v:p<o.p;}
}s[N+5];priority_queue<int,vector<int>,greater<int> > p;
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C==E&&(clear(),0),*C++=c)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI,C=FO,E=FO+FS;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Tp I void write(Con Ty& x,Con char& y) {write(x),pc(y);}
I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
class TreeArray//树状数组
{
private:
int v[N+5];
public:
I void Add(RI x,CI y) {W(x<=n) v[x]+=y,x+=x&-x;}//后缀修改
I int Qry(RI x,RI t=0) {W(x) t+=v[x],x-=x&-x;return t;}//单点查询
}T;
int main()
{
RI i,f;LL t=0;for(F.read(n),F.read(m),i=1;i<=n;++i) F.read(a[i]),s[i]=data(i,a[i]);
sort(s+1,s+n+1),i=1;W(i<=n||!p.empty()||!q.empty())
{
!(f=q.empty())&&(ans[q.front()]=(t+=m)),i<=n&&p.empty()&&q.empty()&&(t=s[i].v);//计算当前时间
W(!p.empty()&&!T.Qry(p.top())) q.push(p.top()),T.Add(p.top()+1,1),p.pop();//处理原有能够灌水的人
W(i<=n&&s[i].v<=t) p.push(s[i++].p),!T.Qry(p.top())&&(q.push(p.top()),T.Add(p.top()+1,1),p.pop(),0);//处理新增能够灌水的人,同时判断是否能去排队
!f&&(T.Add(q.front()+1,-1),q.pop(),0);//当前正在灌水的人已经灌完
}
for(i=1;i<=n;++i) F.write(ans[i]," \n"[i==n]);return F.clear(),0;//输出答案
}

\(D\):Catowice City(点此看题面

大致题意: 有\(n\)个人,每人有一只猫。每个人都认识若干猫(一定认识自己的猫),现在要选出人和猫共\(n\)个,使得至少有一个人和一只猫,且每个人都不认识任何一只猫。

首先,我们可以发现,因为一个人一定认识自己的猫,所以一个数不可能被选两次。而题目要求选出\(n\)个数,就必然是把\(1\sim n\)分成两部分。

则,我们考虑如果选择了编号为\(i\)的人,那么他认识的所有的猫的主人都必须被选择,而他认识的猫的主人认识的猫的主人同样也都必须被选择,以此类推。

如果我们把编号为\(i\)的人认识编号为\(j\)的猫看作一条有向边,那么选择一个人,就相当于选择了从这个点出发能够到达的所有点。

因此,我们用\(Tarjan\)将图缩点,显然,如果原图只有一个强连通分量,就会输出\(No\),否则必然输出\(Yes\)。

考虑如果我们选择一个强连通分量中的点作为人,如果这个强连通分量出度为\(0\),是肯定合法的。而根据\(Tarjan\)的原理可知,\(Tarjan\)过程中得到的第一个强连通分量,出度是必然为\(0\)的。

因此我们只要把编号为\(1\)的强连通分量中的点作为人,剩余点作为猫,即可满足题目要求了。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,m,ee,d,cnt,T,lnk[N+5],dfn[N+5],low[N+5],col[N+5],vis[N+5],S[N+5],Sz[N+5];
struct edge {int to,nxt;}e[N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C==E&&(clear(),0),*C++=c)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI,C=FO,E=FO+FS;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Tp I void write(Con Ty& x,Con char& y) {write(x),pc(y);}
I void writes(Con string& x) {for(RI i=0,y=x.length();i^y;++i) pc(x[i]);}
I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
I void Tarjan(CI x,CI lst)//Tarjan缩点
{
dfn[x]=low[x]=++d,vis[S[++T]=x]=1;for(RI i=lnk[x];i;i=e[i].nxt) dfn[e[i].to]?
vis[e[i].to]&&Gmin(low[x],dfn[e[i].to]):(Tarjan(e[i].to,x),Gmin(low[x],low[e[i].to]));
if(dfn[x]^low[x]) return;Sz[col[x]=++cnt]=1,vis[x]=0;
W(S[T]^x) ++Sz[col[S[T]]=cnt],vis[S[T--]]=0;--T;
}
int main()
{
RI Tt,i,j,x,y;F.read(Tt);W(Tt--)
{
for(F.read(n),F.read(m),ee=d=cnt=0,i=1;i<=n;++i) lnk[i]=dfn[i]=vis[i]=0;//清空
for(i=1;i<=m;++i) F.read(x),F.read(y),x^y&&add(x,y);//连边
for(i=1;i<=n;++i) !dfn[i]&&(Tarjan(i,0),0);//Tarjan缩点
if(cnt==1) {F.writes("No\n");continue;}F.writes("Yes\n");//判断是否有解
F.write(Sz[1],' '),F.write(n-Sz[1],'\n');//分别输出人与猫的个数
for(i=1;i<=n;++i) col[i]==1&&(F.write(i,' '),0);F.writes("\n");//输出强连通分量内的点,作为人
for(i=1;i<=n;++i) col[i]^1&&(F.write(i,' '),0);F.writes("\n");//输出剩余的点,作为猫
}return F.clear(),0;
}

\(E\):Turtle(点此看题面

大致题意: 让你把\(2n\)个元素放到一个\(2\times n\)的矩阵中,使得从左上角走到右下角(只能往下或往右)所经元素总和的最大值最小。

因为只能往下或往右走,所以我们可以找到一个关键点\(p\),使得在\(p\)点之前在第一行走,\(p\)点之后在第二行走,\(p\)点从第一行到第二行。

设所经元素总和为\(f(p)=\sum_{i=1}^pa_{1,i}+\sum_{i=p}^na_{2,i}\)。

根据贪心,显而易见,第一排的数应当递增摆放,第二排的数应当递减摆放。

考虑将\(p\)从\(1\)移动到\(n\),则每次移动,元素总和发生的变化其实都是加上\(a_{1,p+1}-a_{2,p}\)。由于第一排递增,第二排递减,则\(a_{1,p+1}-a_{2,p}\)也是递增的。

假设当\(p=x\)时满足使\(f(p)\)最大的同时\(x\)最大,则我们可以得到结论:\(x=1\)或\(x=n\)。

当\(x≠1\)且\(x≠n\)时,\(\because f(x)=f(x-1)+a_{1,x}-a_{2,x-1},f(x)\ge f(x-1),\therefore a_{1,x}-a_{2,x-1}\ge0.\)

又\(\because a_{1,x+1}-a_{2,x}\ge a_{1,x}-a_{2,x-1},\therefore a_{1,x+1}-a_{2,x}\ge 0.\)

\(\therefore f(x+1)=f(x)+a_{1,x+1}-a_{2,x}\ge f(x).\)

这与\(p=x\)时满足使\(f(p)\)最大的同时\(x\)最大矛盾,\(\therefore x=1\)或\(x=n.\)

也就是说,最大值应该是\(max(f(1),f(n))\),即\(max(a_{1,1}+\sum_{i=1}^na_{2,i},\sum_{i=1}^na_{1,i}+a_{2,n})\)。

如果我们同时从两个式子中拿出\(a_{1,1}\)和\(a_{2,n}\),就得到:\(a_{1,1}+a_{2,n}+max(\sum_{i=1}^{n-1}a_{2,i},\sum_{i=2}^na_{1,i})\)。

由于\(a_{1,1}\)和\(a_{2,n}\)无论如何都会被取到,显然它们两个应该分别填上最小值和次小值。

那么,题目也就变成了,将剩余的\(2n-2\)个数分成两个大小为\(n-1\)的集合,使得两个集合内数总和的较大值最小。

所以,我们可以先通过背包来求出这个答案。

设\(f_{i,j,k}\)表示是否能在前\(i\)个数中选择\(j\)个数使得它们和为\(k\),第一维根据背包问题的常用技巧,可以在数组中去掉,变成\(f_{j,k}\)。然后由于只需知道是否可行,因此我们可以用\(bitset\)优化。

最后我们枚举\(k\),然后找到一个\(f_{n-1,k}=1\)且\(max(k,s-k)\)最小的\(k\)(\(s\)为这\(2n-2\)个数的和)。

然后,我们考虑题目要求输出一个合法方案。

则我们用\(Meet\ in\ middle\),搜索过程中记录\(t,v,s\)分别表示选择数的个数、选择数的和以及选择数状压后的状态。

这样一来,这道题就算做完了。

说实话,其实最后的实现是很简单的,难点主要是前面的部分。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 25
#define V 50000
#define LL long long
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int n,a[2*N+5];
class DpSolver//背包
{
private:
bitset<2*N*V+5> f[2*N+5];
public:
I int GetAns()
{
RI i,j,s=0,ans=s;for(i=3;i<=2*n;++i) s+=a[i];//记录数的和
for(f[0].set(0),i=3;i<=2*n;++i) for(j=min(n-1,i-2);j;--j) f[j]|=f[j-1]<<a[i];//背包
for(i=0;i<=s;++i) f[n-1].test(i)&&max(ans,s-ans)>max(i,s-i)&&(ans=i);//统计答案
return min(ans,s-ans);//返回
}
}DP;
class DfsSolver//Meet in middle
{
private:
#define pb push_back
LL w[N+5][N*V+5];
I void dfs1(CI p,CI x,CI y,CI t,CI v,Con LL& s)//前一半dfs
{
if(x>y) return (void)(!~w[t][v]&&(w[t][v]=s));//记录答案
dfs1(p,x+1,y,t,v,s),v+a[x]<=p&&(dfs1(p,x+1,y,t+1,v+a[x],s|(1LL<<x)),0);return;
}
I void dfs2(CI p,CI x,CI y,CI t,CI v,Con LL& s)//后一半dfs
{
if(x>y)
{
if(!~w[n-1-t][p-v]) return;RI i;LL g=w[n-1-t][p-v]|s;
printf("%d ",a[1]);for(i=3;i<=2*n;++i) g>>i&1&&printf("%d ",a[i]);putchar('\n');//第一行,升序输出
for(i=2*n;i>=3;--i) !(g>>i&1)&&printf("%d ",a[i]);printf("%d\n",a[2]);exit(0);//第二行,降序输出
}
dfs2(p,x+1,y,t,v,s),v+a[x]<=p&&(dfs2(p,x+1,y,t+1,v+a[x],s|(1LL<<x)),0);
}
public:
I void Solve(CI x) {memset(w,-1,sizeof(w)),dfs1(x,3,n+1,0,0,0),dfs2(x,n+2,2*n,0,0,0);}
}DFS;
int main()
{
RI i,j;for(scanf("%d",&n),i=1;i<=2*n;++i) scanf("%d",a+i);
return sort(a+1,a+2*n+1),DFS.Solve(DP.GetAns()),0;
}

\(F\):Swiper, no swiping!(点此看题面

大致题意: 让你在一张图中删去若干点(不能不删,也不能删完),使得剩余的每个点度数模\(3\)的余数不变。

大码量多细节分类讨论题,调了两天......(话说这场比赛实在太多细节了,心态爆炸啊)

首先,这里我们按照一个点度数模\(3\)的余数将其分为\(0\)类点、\(1\)类点、\(2\)类点。(注意,下面的每一个情况都建立于之前情况不成立的基础上)

  • 如果存在\(0\)类点:对于\(n=1\)的情况输出\(No\),否则保留这个点。
  • 如果存在一个由\(2\)类点组成的环:对于整张图是一个环的情况输出\(No\),否则保留这个环。(注意,这个环必须是简单环,不然度数会出锅)
  • 如果存在两个\(1\)类点:对于整张图是一条链的情况输出\(No\),否则保留一条以两个\(1\)类点为端点、中间全为\(2\)类点的链。(同样要注意这些\(2\)类点之间不能有边,可以用\(BFS\))
  • 此时,由于没有\(0\)类点、没有超过\(1\)个\(1\)类点、没有\(2\)类点组成的环,所以可以保证这张图必然由一个\(1\)类点和一片\(2\)类点森林组成。可以证明,必然存在至少两棵树皆与这个\(1\)类点有至少两条边相连。则在两棵树上各找出一条以和\(1\)号点有边相连的点为端点的链,这个以\(1\)号点为唯一交点的两个相交的环,显然是符合要求的。但如果图中只有这样一对相交的环,也应该输出\(No\)。(再次强调,要注意这些\(2\)类点之间不能有边相连,我已经被这坑惨了)

具体实现可以看代码。

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,ee,x[N+5],y[N+5],s[N+5],q[N+5],lst[N+5],lnk[N+5],deg[N+5],vis[N+5],fa[N+5],tag[N+5];
struct edge {int to,nxt;}e[2*N+5];
struct data {int v;I bool operator < (Con data& o) Con {return fa[v]<fa[o.v];}}p[N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define pc(c) (C==E&&(clear(),0),*C++=c)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
public:
I FastIO() {A=B=FI,C=FO,E=FO+FS;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
Tp I void write(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);}
Tp I void write(Con Ty& x,Con char& y) {write(x),pc(y);}
I void writes(Con string& x) {for(RI i=0,y=x.length();i^y;++i) pc(x[i]);}
I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
I int getfa(CI x) {return fa[x]^x?fa[x]=getfa(fa[x]):x;}
I void FindLine(CI x)//有两个度数为1的点,找出一条链
{
RI i,k,H=1,T=0;vis[q[++T]=x]=1;W(H<=T)
{
if(deg[k=q[H++]]%3==1&&k^x) break;
for(i=lnk[k];i;i=e[i].nxt) !vis[e[i].to]&&(lst[q[++T]=e[i].to]=k,vis[e[i].to]=1);
}W(s[k]=1,k^x) k=lst[k];
}
I void FindOnTree(CI x,CI st)//在以x为根的树上找出一条合法路径
{
RI i,k,H=1,T=0;vis[q[++T]=x]=1;W(H<=T)
{
if(tag[k=q[H++]]&&k^x) break;
for(i=lnk[k];i;i=e[i].nxt) !vis[e[i].to]&&(lst[q[++T]=e[i].to]=k,vis[e[i].to]=1);
}W(s[k]=1,k^x) k=lst[k];
}
I bool FillCircle(CI x,CI st,CI f=1)//找出以st为环上一点的简单环,f表示点数
{
if(f>2) for(RI i=lnk[x];i;i=e[i].nxt) if(e[i].to==st) return s[x]=1;//先判断是否有环
vis[x]=2;for(RI i=lnk[x];i;i=e[i].nxt) if(vis[e[i].to]==1)
if(FillCircle(e[i].to,st,f+1)) return s[x]=1;return vis[x]=1,false;
}
I bool FindCircle(CI x,CI lst)//找度数模3余2的点之间的环
{
vis[x]=1;for(RI i=lnk[x];i;i=e[i].nxt) if(e[i].to^lst&&vis[e[i].to]) return FillCircle(x,x);//将环补充完整
for(RI i=lnk[x];i;i=e[i].nxt) if(e[i].to^lst&&FindCircle(e[i].to,x)) return true;return false;
}
int main()
{
freopen("a.in","r",stdin),freopen("a.out","w",stdout);
RI Tt,i,j,t1,t2;F.read(Tt);W(Tt--)
{
for(F.read(n),F.read(m),ee=0,i=1;i<=n;++i) s[i]=lnk[i]=deg[i]=vis[i]=tag[i]=0,fa[i]=i;//清空
for(i=1;i<=m;++i) F.read(x[i]),F.read(y[i]),++deg[x[i]],++deg[y[i]];//读入边,计算点数
if(n==1) {F.writes("No\n");continue;}//只有一个点,无解
for(i=1;i<=n;++i) if(!(deg[i]%3)) {s[i]=1;goto End;}//存在度数模3余0的点,保留该点
for(t2=0,i=1;i<=n;++i) deg[i]==2&&++t2;if(t2==n) {F.writes("No\n");continue;}//整张图是一个环,无解
for(i=1;i<=m;++i) deg[x[i]]%3==2&&deg[y[i]]%3==2&&
(fa[getfa(x[i])]=getfa(y[i]),add(x[i],y[i]),add(y[i],x[i]));//给度数模3余2的点连边
for(i=1;i<=n;++i) if(deg[i]%3==2&&!vis[i]&&FindCircle(i,0)) goto End;//找度数模3余2的点之间的环
for(i=1;i<=n;++i) vis[i]=0;
for(t1=t2=0,i=1;i<=n;++i) deg[i]==1&&++t1,deg[i]==2&&++t2;
if(t1==2&&t2==n-2) {F.writes("No\n");continue;}//整张图是一条链,无解
for(t1=0,i=1;i<=n;++i) deg[i]%3==1&&++t1;if(t1>1)//有两个度数模3余1的点,找出一条链
{
for(i=1;i<=m;++i) ((deg[x[i]]%3)^2||(deg[y[i]]%3)^2)&&(add(x[i],y[i]),add(y[i],x[i]));//补上之前没有连的边
for(i=1;i<=n;++i) if(deg[i]%3==1) {FindLine(i);goto End;}//搜索出一条路径
}
for(t1=t2=0,i=1;i<=n;++i) deg[i]%3==1&&(t1=i),deg[i]==2&&++t2;
if(deg[t1]==4&&t2==n-1) {F.writes("No\n");continue;}//整张图是两个相连的环,无解
for(s[t1]=1,t2=0,i=1;i<=m;++i) x[i]==t1&&(tag[p[++t2].v=y[i]]=1),y[i]==t1&&(tag[p[++t2].v=x[i]]=1);//记下与度数模3余1的点相连的点
for(i=1;i<=n;++i) deg[i]%3==2&&(fa[i]=getfa(i));
for(t1=0,sort(p+1,p+t2+1),i=1;i<t2;++i) if(fa[p[i].v]==fa[p[i+1].v])
if(FindOnTree(p[i].v,p[i].v),!t1) {t1=1;W(i<t2&&fa[p[i].v]==fa[p[i+1].v]) ++i;}else goto End;//从两棵树中找一条合法路径
End:for(t1=0,i=1;i<=n;++i) !s[i]&&++t1;F.writes("Yes\n"),F.write(t1,'\n');//统计删去的点数
for(i=1;i<=n;++i) !s[i]&&(F.write(i,' '),0);F.writes("\n");//输出删去的点
}return F.clear(),0;
}

【CodeForces】CodeForcesRound594 Div1 解题报告的更多相关文章

  1. 【CodeForces】CodeForcesRound576 Div1 解题报告

    点此进入比赛 \(A\):MP3(点此看题面) 大致题意: 让你选择一个值域区间\([L,R]\),使得序列中满足\(L\le a_i\le R\)的数的种类数不超过\(2^{\lfloor\frac ...

  2. codeforces 31C Schedule 解题报告

    题目链接:http://codeforces.com/problemset/problem/31/C 题目意思:给出 n 个 lessons 你,每个lesson 有对应的 起始和结束时间.问通过删除 ...

  3. codeforces 499B.Lecture 解题报告

    题目链接:http://codeforces.com/problemset/problem/499/B 题目意思:给出两种语言下 m 个单词表(word1, word2)的一一对应,以及 profes ...

  4. codeforces 495C. Treasure 解题报告

    题目链接:http://codeforces.com/problemset/problem/495/C 题目意思:给出一串只有三种字符( ')','(' 和 '#')组成的字符串,每个位置的这个字符 ...

  5. codeforces 490B.Queue 解题报告

    题目链接:http://codeforces.com/problemset/problem/490/B 题目意思:给出每个人 i 站在他前面的人的编号 ai 和后面的人的编号 bi.注意,排在第一个位 ...

  6. CodeForces 166E -Tetrahedron解题报告

    这是本人写的第一次博客,学了半年的基础C语言,初学算法,若有错误还请指正. 题目链接:http://codeforces.com/contest/166/problem/E E. Tetrahedro ...

  7. codeforces 489A.SwapSort 解题报告

    题目链接:http://codeforces.com/problemset/problem/489/A 题目意思:给出一个 n 个无序的序列,问能通过两两交换,需要多少次使得整个序列最终呈现非递减形式 ...

  8. codeforces 485A.Factory 解题报告

    题目链接:http://codeforces.com/problemset/problem/485/A 题目意思:给出 a 和 m,a 表示第一日的details,要求该日结束时要多生产 a mod ...

  9. codeforces 483A. Counterexample 解题报告

    题目链接:http://codeforces.com/problemset/problem/483/A 题目意思:给出一个区间 [l, r],要从中找出a, b, c,需要满足 a, b 互质,b, ...

随机推荐

  1. git配置:本地仓库提交到远程仓库

    前提:1.已安装git 一:创建公钥,一台机子匹配一个公钥 桌面右键选择 Git Bash Here 打开命令行输入:ssh-keygen -t rsa -C "xxx@xxx.com&qu ...

  2. Missing associated label more...

    1.加上placeholder,可以为空 2.放在label标签中

  3. 网站后台getshell

    phpmyadmin后台Getshell 获取 web 绝对路径 select @@basedir; 检测是否有写入权限 show global variables like 'secure%' ## ...

  4. solo升级以及自动化更新的方法

    使用solo过程总涉及到更新问题,所以就在这里把solo更新的方法总结一下.希望能给小伙伴们一些帮助.如何选择更新方法主要是跟你的部署方式有关,如果你是通过 docker方式进行部署,那么你可以还可以 ...

  5. 小程序模板template使用介绍

    template(模板):是可以在wxml中引用的代码,就是在wxml中引用公用的wxml类型的代码,它的作用类似于组件,因此这里简单的说明下template与Component (组件)的区别. t ...

  6. sql server2017开启远程连接

    1.安装完SQL server2017之后,选择SQL 身份验证登录,可以先用windows身份验证登录把密码更改好了,然后服务器右键重新启动 ,再断开连接 ,选择SQL身份验证登录验证,关闭SQL ...

  7. Educational Codeforces Round 77 (Rated for Div. 2) D A game with traps

    题意:x正轴上有着一个陷阱的位置,开关和灵敏度,如果一个士兵灵敏度输给陷阱,他是过不去这个陷阱的幸运的是,你可以先过去把开关给关了,没错你是不怕陷阱的接下来呢你有操作,你移动一个,耗费一秒而你的团队需 ...

  8. VUE Base64编码图片展示与转换图片

    图片的 base64 编码就是可以将一副图片数据编码成一串字符串,使用该字符串代替图像地址,使用 base64 传输图片文件可以节省一个 http 请求,图片的 base64 编码可以算是前端优化的一 ...

  9. Java题库——Chapter6 一维数组

    1)What is the representation of the third element in an array called a? 1) _______ A)a(3) B) a(2) C) ...

  10. 这几个 Chrome 的 Tab 增强插件你都用上了吗?

    1.OneTab:将无数 Tab 合并在一个页面 很多时候我们在一个窗口打开太多的tab,每一个tab太小不容易管理,这时候使用OneTab能够把所有tab收起放在一个页面,点击就可打开该tab,非常 ...