zoukankan      html  css  js  c++  java
  • Codeforces Round #594 (Div. 1)

    Preface

    这场CF真是细节多的爆炸,B,C,F都是大细节题,每道题都写了好久的说

    CSP前的打的最后一场比赛了吧,瞬间凉意满满

    希望CSP可以狗住冬令营啊(再狗不住真没了)


    A. Ivan the Fool and the Probability Theory

    原谅我脑子不如陈指导好想不出来正解,然后打了个暴力3min找到规律做掉了。。。

    然后讲一下正确的做法(陈指导教我的):

    考虑我们用DP做出(1 imes m)的矩阵的答案记为(f_m),考虑用它填了第一行

    然后我们考虑第一行的填法:

    1. 第一行填的是(0/1)相间的,此时方案数有(2)种,考虑第二行填的就要么与第一行相同,要么与第一行相反。而且相同的不能连续填两行。我们仔细想想发现这个问题是不是和原来的一样了?(第一行看作某种状态,相反看作另一种状态),那么显然此时的方案数就是(f_n)
    2. 第一行填的是非(0/1)相间的,此时方案数有(f_m-2)种,显然第二行的填法只有每个与第一行的相反。而且这样的情况要一直延续到第(n)行,那么此时的总方案数就是(f_m-2)

    综上所述,最后的答案就是(f_n+f_m-2)

    #include<cstdio>
    #include<iostream>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=100005,mod=1e9+7;
    int n,m,f[N][2];
    inline int sum(CI x,CI y)
    {
    	int t=x+y; return t>=mod?t-mod:t;
    }
    int main()
    {
    	scanf("%d%d",&n,&m); int lim=max(n,m); RI i;
    	for (f[1][0]=f[1][1]=1,f[2][0]=f[2][1]=2,i=3;i<=lim;++i)
    	f[i][0]=f[i-1][1],f[i][1]=sum(f[i-1][0],f[i-1][1]);
    	return printf("%d",sum(sum(sum(f[n][0],f[n][1]),sum(f[m][0],f[m][1])),mod-2)),0;
    }
    

    B. The World Is Just a Programming Task (Hard Version)

    真NM烦人(假装有很多细节)的题,中间想了好多假算法的说

    考虑我们先判掉必然无解的情况,那么显然这时我们可以搞出一个合法的括号序列

    从这个合法的序列开始,我们把它的括号匹配求出来,同时记一个(ct_i)表示第(i)对括号里面有多少对匹配的括号

    我们现在可以进行的交换有哪几类:

    1. 什么都不干(原地交换),此时的答案就是(ct_0)
    2. 交换某对匹配的括号,而这对括号外面没有其它的括号。我们可以发现此时这个串大概是(cdots(ct_i)cdots)的样子,因此我们交换后就变成((cdots)ct_i)的样子,显然答案是了(ct_i+1)
    3. 交换某对匹配的括号,而这对括号外面只有一对其它的括号。此时的形式就是(cdots(cdots(ct_i)cdots)cdots),那么交换后就变成(cdots(cdots)ct_i(cdots)cdots)的样子,那么这时候答案就是(ct_i+ct_0+1)
    4. 交换某对匹配的括号,而这对括号外面有多于一对其它的括号。简单分析后答案不会改变
    5. 交换不匹配的括号,就是个弟弟玩意,显然答案也不会增加

    那么这题总算是做完了

    #include<cstdio>
    #include<queue>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=600005;
    int n,dlt,st,cl,cr,stk[N],top,tot,L[N],R[N],ct[N],out[N],a[N],b[N],pfx[N],ans,x,y; char s[N];
    struct heap
    {
    	priority_queue <int,vector <int>,greater <int> > val,del;
    	inline void insert(CI x)
    	{
    		val.push(x);
    	}
    	inline void remove(CI x)
    	{
    		del.push(x);
    	}
    	inline int top(void)
    	{
    		while (!val.empty()&&!del.empty()&&val.top()==del.top())
    		val.pop(),del.pop(); return val.top();
    	}
    }hp;
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	RI i,j; for (scanf("%d%s",&n,s+1),i=n+1;i<=(n<<1);++i)
    	a[i]=a[i-n]=s[i-n]=='('?1:-1,s[i-n]=='('?++cl:++cr;
    	if (cl!=cr) return puts("0
    1 1"),0;
    	for (i=1;i<=n;++i) hp.insert(pfx[i]=pfx[i-1]+a[i]);
    	for (i=1;i<=n;++i)
    	{
    		if (hp.top()+dlt>=0) { st=i; break; }
    		hp.remove(pfx[i]); dlt-=a[i]; hp.insert(0-dlt);
    	}
    	for (i=st;i<=st+n-1;++i) b[i-st+1]=a[i];
    	for (i=1;i<=n;++i) if (b[i]==1) stk[++top]=++tot,L[tot]=i;
    	else out[stk[top]]=stk[top-1],R[stk[top]]=i,++ct[stk[--top]];
    	for (ans=ct[0],x=y=i=1;i<=tot;++i)
    	{
    		if (!out[i]) { if (ct[i]+1>ans) ans=ct[i]+1,x=L[i],y=R[i]; }
    		else if (!out[out[i]]) { if (ct[i]+ct[0]+1>ans) ans=ct[i]+ct[0]+1,x=L[i],y=R[i]; }
    	}
    	return printf("%d
    %d %d",ans,(x+st-2)%n+1,(y+st-2)%n+1),0;
    }
    

    C. Queue in the Train

    真是令人头晕脑涨的模拟题,我们直接按题意模拟操作

    考虑我们用一些数据结构来维护答案:

    1. 给原数组按时间第一关键字,序号第二关键字排序,然后把可选每个数进行操作
    2. 开一个队列表示正在排队的人
    3. 有些人可能已经可以打水了,但是由于前面有人再它前面排队,因此它不会去打水。那么我们需要一个来维护这些人的编号
    4. 怎么判掉堆顶能不能去打水呢,我们再开一个set用来动态维护队列里正在排队的人的编号,每次用堆顶和set里的最小值比较即可

    注意这些操作维护的顺序以及各种各样的细节

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<set>
    #define RI register int
    #define CI const int&
    #define int long long
    using namespace std;
    const int N=100005;
    struct data
    {
    	int val,id;
    	friend inline bool operator < (const data& A,const data& B)
    	{
    		return A.val<B.val||(A.val==B.val&&A.id<B.id);
    	};
    }a[N],t[N]; int n,p,ans[N],cnt; queue <int> q;
    set <int> s; priority_queue <int, vector <int>, greater <int> > hp;
    signed main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	RI i,j,pos; for (scanf("%I64d%I64d",&n,&p),i=1;i<=n;++i)
    	scanf("%I64d",&a[i].val),a[i].id=i; sort(a+1,a+n+1);
    	int cur=0; for (i=pos=1;i<=n;++i)
    	{
    		if (hp.empty()&&q.empty()&&pos<=n)
    		q.push(a[pos].id),s.insert(a[pos].id),cur=a[pos].val,++pos;
    		while (!hp.empty()) if (s.empty()||hp.top()<*s.begin())
    		q.push(hp.top()),s.insert(hp.top()),hp.pop(); else break; cur+=p;
    		while (pos<=n&&a[pos].val<=cur) t[++cnt]=a[pos++];
    		for (j=1;j<=cnt;++j) if (s.empty()||t[j].id<*s.begin())
    		q.push(t[j].id),s.insert(t[j].id); else hp.push(t[j].id);
    		ans[q.front()]=cur; s.erase(q.front()); q.pop(); cnt=0;
    	}
    	for (i=1;i<=n;++i) printf("%I64d ",ans[i]); return 0;
    }
    

    D. Catowice City

    比B,C都清新的思维题,还算是帮CSP复习了一波板子

    考虑由于每对人和猫都要作出选择,那么如果我们有人(i)和猫(j)的边,那么说明选了人(i)就必须选人(j)(除去与自己猫的边)

    那么我们可以把这种关系看成一条有向边,那么容易发现SCC里的点都是互相依赖的,选了一个其它的都要选

    那么我们先Tarjan缩点,然后如果最后的图就是一个联通块,那么显然是无解的

    否则我们考虑找出一个度数为(0)的联通块,把这个联通块里的点选作人,其它的点都选做猫即可

    #include<cstdio>
    #include<iostream>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=1000005;
    struct edge
    {
    	int to,nxt;
    }e[N]; int t,head[N],n,m,cnt,x,y,ans1[N],c1,ans2[N],c2;
    int dfn[N],low[N],idx,col[N],stk[N],top,deg[N],scc; bool vis[N];
    inline void clear(void)
    {
    	RI i; for (i=1;i<=n;++i) head[i]=dfn[i]=low[i]=deg[i]=0;
    	cnt=scc=top=c1=c2=0;
    }
    inline void addedge(CI x,CI y)
    {
    	e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
    }
    #define to e[i].to
    inline void Tarjan(CI now)
    {
    	dfn[now]=low[now]=++idx; vis[stk[++top]=now]=1;
    	for (RI i=head[now];i;i=e[i].nxt)
    	if (!dfn[to]) Tarjan(to),low[now]=min(low[now],low[to]);
    	else if (vis[to]) low[now]=min(low[now],dfn[to]);
    	if (dfn[now]==low[now])
    	{
    		for (col[now]=++scc,vis[now]=0;stk[top]!=now;--top)
    		col[stk[top]]=scc,vis[stk[top]]=0; --top;
    	}
    }
    int main()
    {
    	for (scanf("%d",&t);t;--t)
    	{
    		RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
    		scanf("%d%d",&x,&y),x!=y&&(addedge(x,y),0);
    		for (i=1;i<=n;++i) if (!dfn[i]) Tarjan(i);
    		if (scc==1) { puts("No"); clear(); continue; }
    		for (j=1;j<=n;++j) for (i=head[j];i;i=e[i].nxt)
    		if (col[j]!=col[to]) ++deg[col[j]];
    		int id; for (i=1;i<=scc;++i) if (!deg[i]) { id=i; break; }
    		for (i=1;i<=n;++i) if (col[i]==id) ans1[++c1]=i; else ans2[++c2]=i;
    		puts("Yes"); printf("%d %d
    ",c1,c2);
    		for (i=1;i<=c1;++i) printf("%d%c",ans1[i]," 
    "[i==c1]);
    		for (i=1;i<=c2;++i) printf("%d%c",ans2[i]," 
    "[i==c2]); clear();
    	}
    	return 0;
    }
    

    E. Turtle

    思维难度较大的一道题,写到一半瞄了一眼陈指导的题解才会做

    首先我们容易发现第一行的数必须是不降排序的,而第二行的数也要是不升的,证明非常简单,这里不再赘述

    我们接下来考虑最大的路径是怎么走的,我们发现我们可以根据路径向下走的位置给它分类,例如我们设(f(x)=sum_{i=1}^n a_{1,i}=sum_{i=p}^n a_{2,i})

    那么我们考虑一个结论,当(f(x))(max)(x=1)(x=n),证明如下:

    (x ot = 1)(x ot =n)时,设(f(x))(max)

    (f(x)=f(x-1)+a_{1,x}-a_{2,x-1})(f(x)ge f(x-1))得出(a_{1,x}-a_{2,x-1}ge 0)

    同理对于(f(x))(f(x+1))也有同样的分析,那么(-a_{1,x+1}+a_{2,x}ge 0)

    将不等式两边相加,得到((a_{1,x}-a_{1,x+1})+(a_{2,x}-a_{2,x-1})ge 0),这显然与(a_1)不降与(a_2)不升矛盾,因此原命题得证

    因此我们只需要让(f(1))(f(n))尽量小即可,贪心地想把两个都要经过的起点终点选上最小值和次小值,考虑剩下的怎么填

    由于总和确定,那么我们只要尽量使剩下数的分成两组,和最接近即可

    考虑用DP算出答案,我们设(f_{i,j,k})表示前(i)个数选了(j)个和为(k)能否达成,然后用bitset可以优化掉一维

    那么知道答案怎么构造方案呢,发现数据中(n)非常小,因此我们可以用meet in middle构造一组解,状态的话状压一下就可以存了

    #include<cstdio>
    #include<bitset>
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #define RI register int
    #define CI const int&
    using namespace std;
    typedef long long LL;
    const int N=27,S=50005;
    int n,a[N<<1],ans[2][N],ret,cur=1e9;
    long long g[N][N*S]; bitset <N*S> f[N]; bool vis[N<<1];
    inline bool cd(CI x,CI y)
    {
    	return x>y;
    }
    inline void DFS1(CI nw,CI lim,CI cs=0,CI sum=0,const LL& sts=0)
    {
    	if (nw>lim) return (void)(g[cs][sum]=sts);
    	DFS1(nw+1,lim,cs,sum,sts); DFS1(nw+1,lim,cs+1,sum+a[nw],sts|(1LL<<nw));
    }
    inline void DFS2(CI nw,CI lim,CI cs=0,CI sum=0,const LL& sts=0)
    {
    	if (nw>lim)
    	{
    		if (cur-sum>=0&&~g[n-1-cs][cur-sum])
    		{
    			RI i,c=1; for (i=3;i<=(n<<1);++i)
    			if ((g[n-1-cs][cur-sum]>>i)&1LL) ans[0][++c]=a[i],vis[i]=1;
    			for (i=3;i<=(n<<1);++i)
    			if ((sts>>i)&1LL) ans[0][++c]=a[i],vis[i]=1;
    			for (c=0,i=3;i<=(n<<1);++i) if (!vis[i]) ans[1][++c]=a[i];
    			sort(ans[0]+1,ans[0]+n+1); sort(ans[1]+1,ans[1]+n+1,cd);
    			for (i=1;i<=n;++i) printf("%d%c",ans[0][i]," 
    "[i==n]);
    			for (i=1;i<=n;++i) printf("%d%c",ans[1][i]," 
    "[i==n]);
    			exit(0);
    		}
    		return;
    	}
    	DFS2(nw+1,lim,cs,sum,sts); DFS2(nw+1,lim,cs+1,sum+a[nw],sts|(1LL<<nw));
    }
    int main()
    {
    	RI i,j; for (scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&a[i]);
    	for (i=n+1;i<=(n<<1);++i) scanf("%d",&a[i]);
    	sort(a+1,a+(n<<1)+1); ans[0][1]=a[1]; ans[1][n]=a[2];
    	for (f[0][0]=1,i=3;i<=(n<<1);++i)
    	for (ret+=a[i],j=min(n-1,i-2);j;--j) f[j]|=(f[j-1]<<a[i]);
    	for (i=0;i<=ret;++i) if (f[n-1][i])
    	if (max(i,ret-i)<cur) cur=max(i,ret-i);
    	return memset(g,-1,sizeof(g)),DFS1(3,n+1),DFS2(n+2,n<<1),0;
    }
    

    F. Swiper, no swiping!

    细节超级多的分类讨论题,写了一天最后偷来了错掉的数据才过掉

    考虑把点按(mod 3)的余数分为三类,记为(Z,A,B)

    • 如果有(Z)类的点,显然我们只用保留一个就可以了(SP:除了只有一个点的情况)
    • 如果(A)类的点之间有边相连,那么显然只用保留一对这样的点就可以了(SP:除了只有两个点相连的情况)
    • 如果B类点之间有环,那么我们可以搜出一个最短的环保留即可(SP:除了整张图就是一个大环的情况)
    • 如果存在两个即以上的(A)类点,那么我们显然可以找出一条连接两个(A)类点的边,满足这两点之间只有(B)类点(SP:除了整张图是一条链的情况)
    • 除去上面的情况那么现在我们的图一定只有一个(A)类点,然后剩下的(B)类点构成了森林。那么我们找出两个子树,从里面找出包含(A)的两个环即可(SP:除了整张图就是一个节点套上两个环的情况)

    大体思路就是这样,关于一些原理的证明可以看官方题解:

    F

    然后就做完了,看了题解觉得很简单,写了代码就会很绝望……

    #include<cstdio>
    #include<vector>
    #define RI register int
    #define CI const int&
    using namespace std;
    const int N=500005;
    struct edge
    {
    	int to,nxt;
    }e[N<<1]; int t,n,m,x[N],y[N],head[N],cnt,fa[N],deg[N],tim[N],cur,ans;
    bool ext[N],flag; vector <int> v[N]; int q[N],pre[N],dep[N],vis[N];
    inline void addedge(CI x,CI y)
    {
    	e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
    	e[++cnt]=(edge){x,head[y]}; head[y]=cnt;
    }
    inline void paint(CI s,CI t)
    {
    	for (int nw=t;~nw;nw=pre[nw]) ext[nw]=1;
    }
    #define to e[i].to
    inline void findcircle(CI now,CI tc)
    {
    	if (flag) return; RI i; vis[now]=1; tim[now]=tc; int pos,cur=-1;
    	for (i=head[now];i;i=e[i].nxt)
    	if (vis[to]==1&&to!=pre[now]&&deg[to]%3==2) 
    	if (dep[to]>cur) cur=dep[to],pos=to;
    	if (~cur)
    	{
    		for (int x=now;x!=pos;x=pre[x])	ext[x]=1;
    		flag=1; ext[pos]=1; return;
    	}
    	for (i=head[now];i;i=e[i].nxt) if (!vis[to]&&deg[now]%3==2)
    	pre[to]=now,dep[to]=dep[now]+1,findcircle(to,tc); vis[now]=2;
    }
    inline void BFS1(CI st)
    {
    	RI H=0,T=1,i; for (i=1;i<=n;++i) pre[i]=0;
    	pre[q[T]=st]=-1; while (H<T)
    	{
    		int now=q[++H]; if (now!=st&&deg[now]%3==1) return paint(st,now);
    		for (i=head[now];i;i=e[i].nxt)
    		if (!pre[to]) pre[to]=now,q[++T]=to;
    	}
    }
    inline void BFS2(CI st)
    {
    	RI H=0,T=1,i; for (i=1;i<=n;++i) pre[i]=0;
    	pre[q[T]=st]=-1; while (H<T)
    	{
    		int now=q[++H]; if (now!=st&&vis[now]) return paint(st,now);
    		for (i=head[now];i;i=e[i].nxt)
    		if (!pre[to]) pre[to]=now,q[++T]=to;
    	}
    }
    #undef to
    inline void output(void)
    {
    	RI i; for (puts("Yes"),ans=0,i=1;i<=n;++i) if (!ext[i]) ++ans;
    	for (printf("%d
    ",ans),i=1;i<=n;++i)
    	if (!ext[i]) printf("%d ",i); putchar('
    ');
    }
    inline void clear(void)
    {
    	for (RI i=1;i<=n;++i) head[i]=deg[i]=ext[i]=vis[i]=dep[i]=0,v[i].clear(); cnt=cur=flag=0;
    }
    int main()
    {
    	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    	for (scanf("%d",&t);t;--t)
    	{
    		RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
    		scanf("%d%d",&x[i],&y[i]),++deg[x[i]],++deg[y[i]];
    		if (n==1) { puts("No"); continue; }
    		for (i=1;i<=n;++i) if (deg[i]%3==0)
    		{
    			ext[i]=flag=1; break;
    		}
    		if (flag) { output(); clear(); continue; }
    		if (n==2) { puts("No"); clear(); continue; } 
    		for (i=1;i<=m;++i) if (deg[x[i]]%3==1&&deg[y[i]]%3==1)
    		{
    			ext[x[i]]=ext[y[i]]=flag=1; break;
    		}
    		if (flag) { output(); clear(); continue; }
    		int ct1,ct2=0; for (i=1;i<=n;++i) if (deg[i]==2) ++ct2;
    		if (ct2==n) { puts("No"); clear(); continue; }
    		for (i=1;i<=m;++i) if (deg[x[i]]%3==2&&deg[y[i]]%3==2) addedge(x[i],y[i]);
    		for (i=1;i<=n;++i) pre[i]=0; for (i=1;i<=n;++i)
    		if (deg[i]%3==2&&!vis[i]) findcircle(i,++cur);
    		if (flag) { output(); clear(); continue; }
    		for (ct1=ct2=0,i=1;i<=n;++i) ct1+=(deg[i]==1),ct2+=(deg[i]==2);
    		if (ct1==2&&ct2==n-2) { puts("No"); clear(); continue; }
    		for (ct1=0,i=1;i<=n;++i) if (deg[i]%3==1) ++ct1;
    		if (ct1>=2)
    		{
    			for (i=1;i<=m;++i) if (deg[x[i]]%3!=2||deg[y[i]]%3!=2) addedge(x[i],y[i]);
    			for (i=1;i<=n;++i) if (deg[i]%3==1) { ct1=i; break; }
    			BFS1(ct1); output(); clear(); continue;
    		}
    		for (i=1;i<=n;++i) if (deg[i]%3==1) { ct1=i; break; }
    		for (ct2=0,i=1;i<=n;++i) ct2+=(deg[i]%3==2);
    		if (deg[ct1]==4&&ct2==n-1) { puts("No"); clear(); continue; }
    		for (i=1;i<=n;++i) vis[i]=0; 
    		for (i=1;i<=m;++i)
    		{
    			if (x[i]==ct1) v[tim[y[i]]].push_back(y[i]),vis[y[i]]=1;
    			if (y[i]==ct1) v[tim[x[i]]].push_back(x[i]),vis[x[i]]=1;
    		}
    		for (ct2=0,ext[ct1]=1,i=1;i<=cur;++i) if (v[i].size()>=2)
    		{
    			BFS2(v[i][0]); if (++ct2==2) break;
    		}
    		output(); clear();
    	}
    	return 0;
    }
    

    Postscript

    不说什么了,各路神仙保我CSP RP++++RP

  • 相关阅读:
    编译gcc报错make[3]: Leaving directory `/usr/local/src/gcc-7.4.0/build/gcc' make[2]: *** [all-stage1-gcc] Error 2 处理
    ERROR 1176 (42000): Key 'XXX' doesn't exist in table 'XXX'报错处理
    /lib64/libc.so.6: version `GLIBC_2.18' not found报错解决
    Centos7上pkg-config的安装
    ERROR: Error in Log_event::read_log_event(): 'Found invalid event in binary log', data_len: 31, event_type: 35报错处理
    MySQL5.7主从复制slave报Last_Errno: 1146错误解决
    详述 hosts 文件的作用及修改 hosts 文件的方法
    Java Decompiler(Java反编译工具)
    使用Charles代理工具导致电脑无法正常访问网站(您的连接不是私密连接)
    阿里云服务器Svn-Server无法连接,阿里云服务器SVNServer配置
  • 原文地址:https://www.cnblogs.com/cjjsb/p/11861881.html
Copyright © 2011-2022 走看看