AtCoder Grand Contest 035

传送门

\(A\)

因为有\(a_1\oplus a_2\oplus a_3=0,a_2\oplus a_3\oplus a_4=0\),所以有\(a_1=a_4\),或者写成\(a_{i}=a_{i+3}\)

于是合法的情况只有以下三种

1.全是\(0\)

2.\({2n\over 3}\)\(x\)\({n\over 3}\)\(0\)

3.\(x,y,z\)\({n\over 3}\)个,且\(x\oplus y\oplus z=0\)

const int N=1e5+5;
int n,a[N],b[N],cnt[5],m;
int main(){
	scanf("%d",&n);
	fp(i,1,n)scanf("%d",&a[i]),b[i]=a[i];
	sort(b+1,b+1+n),m=unique(b+1,b+1+n)-b-1;
	fp(i,m+1,3)b[i]=-1;
	fp(i,1,n){
		if(a[i]==b[1])++cnt[1];
		if(a[i]==b[2])++cnt[2];
		if(a[i]==b[3])++cnt[3];
	}
	if(m==1&&b[1]==0)return puts("Yes"),0;
	if(m==2&&(cnt[2]==(cnt[1]<<1)&&b[1]==0))return puts("Yes"),0;
	if(cnt[1]==cnt[2]&&cnt[2]==cnt[3]&&((b[1]^b[2]^b[3])==0))return puts("Yes"),0;
	puts("No");
	return 0;
}

\(B\)

因为所有出度之和为偶数,每条边只能对一个点贡献出度,所以如果\(m\)为奇数显然\(gg\)

另一方面,如果\(m\)为偶数,我们随便跑一棵生成树,对于非树边随便定向,对于树边,如果此时\(u\)的出度为偶数就从父亲连向\(u\),否则从\(u\)连向父亲,这样可以保证除了根节点所有出度都为偶数。对于根节点,因为总的出度即\(m\)为偶数,其他点出度都为偶数,那么他的出度也肯定为偶数

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
const int N=5e5+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot=1;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int n,m,vis[N],ok[N],deg[N];
void dfs(int u,int fa){
	vis[u]=1;
	go(u)if(v!=fa){
		if(!vis[v])dfs(v,u);
		else if(!ok[i])printf("%d %d\n",u,v),ok[i]=ok[i^1]=1,deg[u]^=1;
	}
	if(fa){
		if(deg[u])printf("%d %d\n",u,fa);
		else printf("%d %d\n",fa,u),deg[fa]^=1;
	}
}
int main(){
	scanf("%d%d",&n,&m);
	if(m&1)return puts("-1"),0;
	for(R int i=1,u,v;i<=m;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
	dfs(1,0);
	return 0;
}

\(C\)

首先,如果\(n\)\(2\)的幂次显然无解,因为\(n\)\(2n\)这条路径无论如何都构造不起来

那么如果有解此时\(n\geq 3\),我们先把\(1\)\(3\)这条链(即题目中样例一的链)构造出来,对于每一个\(2i-1\)\(2i\),我们连接边\((2i-1,2i),(2i,1),(1,2i-1+n),(2i-1+n,2i)\),容易发现这样可以保证所有的路径都满足条件

如果\(n\)是偶数,因为此时\(1\)连接的点中权值可以取遍\(2\)\(n-1\),那么随便找两个点练一下就好了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
const int N=2e5+5;
int n;
void solve(){
	puts("Yes");
	printf("%d %d\n",1,2),printf("%d %d\n",2,3),printf("%d %d\n",3,n+1),printf("%d %d\n",n+1,n+2),printf("%d %d\n",n+2,n+3);
	for(R int i=5;i<=n;i+=2){
		printf("%d %d\n",i-1,i),printf("%d %d\n",i,1),
		printf("%d %d\n",1,n+i-1),printf("%d %d\n",n+i-1,n+i);
	}
	if(n&1^1)printf("%d %d\n",n,n-1),printf("%d %d\n",n+(n^1^(n-1)),n+n);
}
int main(){
	scanf("%d",&n);
	if(n^(n&-n))return solve(),0;
	return puts("No"),0;
}

\(D\)

很妙的思路哇

我们考虑倒着操作,也就是说我们不吃卡片我们吐出卡片。那么初始情况就是只有两个数,且这两个数对最终答案的贡献都是乘上\(1\)

那么如果此时有一堆数,其中第\(i\)个数对答案的贡献要乘上\(x_i\),那么如果我们吐出第\(i\)个数和第\(i+1\)个数之间的数,新的数对答案的贡献就要乘上\(x_{i}+x_{i+1}\),而原来的两个数乘上的数不变

我们考虑\(f_{l,r,xl,xr}\)表示如果\(l,r\)这两个数最终靠在一起,且两个数对答案的贡献分别要乘上\(xl,xr\),此时答案最小是多少,转移的话,我们枚举他们之间吐出了那张牌,即为

\[f_{l,r,xl,xr}=\min_{l<m<r}(f_{l,m,xl,xl+xr}+f_{m,r,xl+xr,xr}+(xl+xr)\times a_m) \]

这个直接爆搜就是,据说状态的个数是\(O(2^n\times poly(n))\)

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=25;
int n,a[N];
ll dfs(int l,int r,ll xl,ll xr){
	if(l+1==r)return 0;
	R ll res=1e18;
	fp(m,l+1,r-1)cmin(res,dfs(l,m,xl,xl+xr)+dfs(m,r,xl+xr,xr)+(xl+xr)*a[m]);
	return res;
}
int main(){
	scanf("%d",&n);
	fp(i,1,n)scanf("%d",&a[i]);
	printf("%lld\n",dfs(1,n,1,1)+a[1]+a[n]);
	return 0;
}

\(E\)

这题好毒啊……花了好几天时间问清楚……感谢\(scott\_wu\)的讲解……

首先我们转化为数删除的数的集合S的方案数。并建一张\(n\)个点的图,如果\(x\)\(x-2\)都在\(S\)里,就从\(x\)\(x-2\)连边,如果\(x\)\(x+k\)都在\(S\)里,就从\(x\)\(x+k\)连边。

结论:一张图合法等价于这张图无环

证明:留待读者自行思考

真证明:加入\(x\)的时候,\(x\)的所有出边指向的点如果已经在\(S\)里会被删去。如果无环,我们可以按照拓扑序把所有我们选了的数给加入\(S\)中,而有环的时候我们无论如何都找不到一个合法的顺序把环上所有数加入。所以充要性都没问题

因为偶数的情况很简单,先来考虑\(k\)为偶数的情况,那么奇偶互不影响,我们可以分开考虑,那么重新标号之后可以转化为\(1\)\(n\)的数,加入\(x\)的时候会删去\(x-1\)\(x+k\)。那么我们发现此时合法就代表着不存在\([x,x+k]\)全都在S里,即选择的连续的数最多只能有\(k\)个,直接\(dp\)就行了

对于\(k\)为奇数的情况呢?首先我们需要发现,图中的一个环的形式必然是形如\(a,a-2,a-4,...,b-k,b,b-2,b-4,...,a-k,a\),也就是说从\(a\)开始,走若干个\(-2\),走个\(k\),再经过若干个\(-2\),再走个\(k\),即经过总共\(k\)\(-2\)\(2\)\(k\)

我们把奇偶分成两列,每列从小到大,且把左右对齐至左边节点的值\(+k=\)右边节点的值,那么一个环一定是从一个右边的点开始,只往下和往左走,走总共\(k+2\)个点之后到达了一个左边的点

那么我们可以\(dp\)了,设\(f[i][j][l]\)表示考虑到第\(i\)行,以第\(i\)行右边节点为结尾的只往下走的路径长度是\(j\),以第\(i\)行左边节点为结尾的下\(->\)\(->\)下的路径最大长度为\(l\)的方案数,只有\(l\leq k\)的状态合法,转移即可

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
int P;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
const int N=155;
int n,p;
inline int max(R int x,R int y){return x>y?x:y;}
int solve(int n,int p){
    static int f[N][N];memset(f,0,sizeof(f));
    f[0][0]=1;
    fp(i,0,n-1)fp(j,0,p)if(f[i][j])upd(f[i+1][0],f[i][j]),upd(f[i+1][j+1],f[i][j]);
    R int res=0;fp(i,0,p)upd(res,f[n][i]);
    return res;
}
int f[N][N][N];
int main(){
//  freopen("testdata.in","r",stdin);
    scanf("%d%d%d",&n,&p,&P);
    if(p&1^1)return printf("%d\n",mul(solve(n>>1,p>>1),solve((n+1)>>1,p>>1))),0;
    f[0][0][0]=1;
    fp(i,1,n+1){
        R int r=(i<<1),l=r-p;
        R bool fr=(r>=1&&r<=n),fl=(l>=1&&l<=n);
        fp(j,0,n)fp(k,0,p+1)if(f[i-1][j][k]){
            upd(f[i][0][0],f[i-1][j][k]);
            if(fr)upd(f[i][j+1][0],f[i-1][j][k]);
            if(fl&&k+1<p+2)upd(f[i][0][k?k+1:k],f[i-1][j][k]);
            if(fl&&fr&&max(j+2,k+1)<p+2)upd(f[i][j+1][max(k+1,j+2)],f[i-1][j][k]);
        }
    }
    printf("%d\n",f[n+1][0][0]);
    return 0;
}

\(F\)

首先,假设存在一对\((i,j)\)满足\(k_i+1=j\)\(l_j=i\),那么我们可以把\((k_i,l_j)\)变成\((k_i+1,l_j-1)\)而不改变这个矩阵

我们对于一种\(k_i,l_j\),不断地找这样的点对\((i,j)\)并进行变换一直到没有这样的点对为止,称这种情况为最小表示。

有两个结论:

\(1.\)每一个原序列对应的最小表示是唯一的

\(2.\)两个矩阵相同当且仅当他们的最小表示相同

证明并不会……感性理解一下好……

所以我们只需要计算最小表示的个数,因为每一个\(i\)最多只能和一个\(j\)构成点对,所有我们可以枚举这种点对的个数,容斥计算即可

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
const int P=998244353;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
const int N=5e5+5;
int fac[N],ifac[N],bin[2][N],n,m,res,d;
inline int C(R int n,R int m){return 1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
inline int A(R int n,R int m){return mul(fac[n],ifac[n-m]);}
void init(){
	if(n>m)swap(n,m);
	bin[0][0]=bin[1][0]=fac[0]=ifac[0]=1;
	fp(i,1,m){
		fac[i]=mul(fac[i-1],i),
		bin[0][i]=mul(bin[0][i-1],m+1),
		bin[1][i]=mul(bin[1][i-1],n+1);
	}
	ifac[m]=ksm(fac[m],P-2);
	fd(i,m-1,1)ifac[i]=mul(ifac[i+1],i+1);
}
int main(){
	scanf("%d%d",&n,&m),init();
	fp(i,0,n){
		d=1ll*C(n,i)*A(m,i)%P*bin[0][n-i]%P*bin[1][m-i]%P;
		upd(res,(i&1)?P-d:d);
	}
	printf("%d\n",res);
	return 0;
}
posted @ 2019-07-15 12:32  源曲明  阅读(372)  评论(0编辑  收藏  举报