AtCoder Grand Contest 039 题解

传送门

\(A\)

首先只有一串的情况下,遇到相同的肯定是改后面那一个最优,然后两串的话可能要分奇偶讨论一下

//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 cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=105;
typedef long long ll;
char s[N];int t[N],n,k;ll res,sum;
int main(){
	scanf("%s%d",s+1,&k),n=strlen(s+1);
	fp(i,1,n)t[i]=s[i];
	fp(i,2,n)if(t[i]==t[i-1])++res,t[i]=2333;
	if(t[n]==t[1]){
		fp(i,1,n)t[i]=s[i];t[1]=2333,++sum;
		fp(i,2,n)if(t[i]==t[i-1])++sum,t[i]=2333;
		if(t[n]==2333)return printf("%lld\n",res*((k+1)>>1)+sum*(k>>1)),0;
		return printf("%lld\n",res+sum*(k-1)),0;
	}
	res*=k;
	printf("%lld\n",res);
	return 0;
}

\(B\)

首先有奇环肯定无解,否则我们枚举哪个点是\(1\)号点,用\(bfs\)依次确定剩下的每个点的编号,因为图中不存在奇环所以这样跑出来的肯定合法

//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 cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=205,M=5e5+5;
struct eg{int v,nx;}e[M];int head[N],tot;
inline void add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
char s[N];int vis[N],ins[N],col[N],q[N],cnt,tim,n,mx,fl;
void dfs(int u,int d){
	col[u]=d,ins[u]=1;
//	printf("qwq %d %d %d\n",u,d,col[u]);
	go(u)if(!ins[v])dfs(v,d^1);
		else if(col[v]!=(d^1))fl=1;
}
void bfs(R int s){
	R int h=1,t=0,u;
	++tim,q[++t]=s,vis[s]=tim,col[s]=1;
	while(h<=t){
		u=q[h++];
		go(u)if(vis[v]!=tim)vis[v]=tim,col[v]=col[u]+1,q[++t]=v;
			else assert(abs(col[u]-col[v])==1);
	}
	cmax(mx,col[q[t]]);
}
int main(){
	scanf("%d",&n);
	fp(i,1,n){
		scanf("%s",s+1);
		fp(j,1,n)if(s[j]=='1')add(i,j);
	}
	dfs(1,0);if(fl)return puts("-1"),0;
	fp(i,1,n)bfs(i);
	printf("%d\n",mx);
	return 0;
}

\(C\)

把操作放到二进制意义下考虑,就是每次把最低位取反然后放到最高位,反过来就可以看成是把最高位取反放到最低位,不难发现任何一个数最多\(2n\)次之后必定会变回原数

那么每一个数最少需要的次数\(k\)肯定是\(2n\)的因子,鉴于直接计算很麻烦,我们计算\(k\)次之后相等的数的个数,然后容斥即可得到最少\(k\)次之后相等的个数

对于一个\(n\)位的二进制数\(S\),我们把\(S\)按位取反之后接在后面得到一个长为\(2n\)的数,那么\(S\)\(k\)次操作之后相等当且仅当这个长为\(2n\)的数以\(k\)为一个周期且\({2n\over k}\)必须是奇数
(如果\({2n\over k}\)是偶数说明\(S\)和它按位取反之后的那个数相等了,显然不可能)

进一步考虑之后我们发现,整个\(S\)必定是由一个长为\({k\over 2}\)的串\(T\)和它的按位取反轮流拼接而成的,所以我们只要数出合法的\(T\)的个数即可

由于还需要字典序小于\(X\),那么我们考虑\(X\)的前\({k\over 2}\)位,只要\(T\)的字典序小于它那么后面必定小于,顺便特判一下\(T\)\(X\)的前\({k\over 2}\)位相等的情况就行了

//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 cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(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;
char s[N];int a[N],b[N],c[N],st[N],n,res,tot,t;
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%s",&n,s+1);
	fp(i,1,n)a[i]=s[i]-'0';
	n<<=1;
	fp(k,1,n)if(n%k==0&&((n/k)&1)){
		++tot,b[tot]=k,c[tot]=0;
		fp(i,1,k>>1)c[tot]=((c[tot]<<1ll)+a[i])%P;
		fp(i,1,k>>1)st[i]=a[i];
		fp(i,(k>>1)+1,n>>1)st[i]=st[i-(k>>1)]^1;
		++c[tot];
		fp(i,(k>>1)+1,n>>1)if(a[i]!=st[i]){
			c[tot]-=(a[i]<st[i]);
			break;
		}
	}
	fp(i,1,tot)fp(j,1,i-1)if(b[i]%b[j]==0)upd(c[i],P-c[j]);
	fp(i,1,tot)upd(res,mul(c[i],b[i]));
	printf("%d\n",res);
	return 0;
}

\(D\)

数学太差,没有办法……

前置芝士\(1\)

假设圆上的三点分别为\(A,B,C\),三角形\(ABC\)的内心为\(O\),令\(A'\)\(AO\)与单位圆的另一个交点(不难发现\(A'\)也是\(BC\)这一段弧的中点),同理定义\(B',C'\),则三角形\(A'B'C'\)的垂心与\(O\)重合

证明:自行画图理解

前置芝士\(2\)

对于任意一个三角形,它的重心\(G\),垂心\(H\),外心\(O\)三点共线,且\(2|GO|=|GH|\)

证明:自行百度"欧拉线"

题解

因为所有的点都在单位圆上,那么\(ABC\)的内心即为\(A'B'C'\)的垂心

而因为\(A'B'C'\)的外心就是原点,重心就是三个点的坐标的平均值,那么我们只要算出重心的期望,就可以推出垂心的期望了。而\(A'\)的坐标只和\(BC\)有关,与\(A\)无关(只要保证\(A\)不在\(A'\)这段弧上即可),那么我们枚举\(BC\),计算对应的\(A'\)的贡献就行了

//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 cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=3005;double Pi=acos(-1.0);
double x[N],y[N],rx,ry,tmp;int a[N],n,L;
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d",&n,&L);Pi*=2.0/L;
	fp(i,1,n)scanf("%d",&a[i]);
	fp(i,1,n)fp(j,1,i-1){
		R int t=i-j-1;
		if(t){
			rx+=t*-cos((a[i]+a[j])*0.5*Pi);
			ry+=t*-sin((a[i]+a[j])*0.5*Pi);
		}
		t=n-i+j-1;
		if(t){
			rx+=t*cos((a[i]+a[j])*0.5*Pi);
			ry+=t*sin((a[i]+a[j])*0.5*Pi);
		}
	}
	tmp=1.0*n*(n-1)*(n-2)/2;
	rx/=tmp,ry/=tmp;
	rx*=3,ry*=3;
	printf("%.10lf %.10lf\n",rx,ry);
	return 0;
}

\(E\)

好迷的题目啊……

首先把环从\(2n\)\(1\)那里断开变成一条链,然后假设与\(1\)配对的点是\(i\)

那么我们接下来就要对\([2,i)\cup (i,n]\)之间的点继续配对,因为边要构成一棵树,所以跨过两个区间的边至少要有一条,且如果有多条时端点要单调

枚举最靠外侧的横跨两个区间的边\((j,k)\),然后先考虑\((j,i)\)之间的点,它们连出的边要么和\(j\)连出的这条边相连,要么和\(i\)连出的这条边相连,且一定存在一个分界点\(p\),由于\((j,k)\)是最靠外侧的横跨区间的边,所以对于\([2,p]\)之间的点我们不需要再知道\([i,n]\)的信息,那么可以递归为一个\([2,j)\cup (j,p]\)的子问题

同理定义\((i,k)\)的分界点\(q\),那么后面这个可以递归为一个\([q,k)\cup (k,n]\)的子问题。而中间那部分就是一个\([p+1,i)\cup (i,q-1]\)的子问题

那么\(dp\)就行了,复杂度\(O(n^7)\)

//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 cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=55;
char s[N][N];ll f[N][N][N];int n;
inline ll calc(R int l,R int r,R int m){
	if(~f[l][r][m])return f[l][r][m];
	if(l==r)return f[l][r][m]=1;
	if(l==m||r==m)return f[l][r][m]=0;
	R ll res=0;
	fp(j,l,m-1)fp(k,m+1,r)if(s[j][k]=='1')
		fp(p,j,m-1)fp(q,m+1,k)
			res+=calc(l,p,j)*calc(p+1,q-1,m)*calc(q,r,k);
	return f[l][r][m]=res;
}
int main(){
	memset(f,-1,sizeof(f));
	scanf("%d",&n);
	fp(i,1,n<<1)scanf("%s",s[i]+1);
	R ll res=0;
	fp(i,2,n<<1)if(s[1][i]=='1')res+=calc(2,n<<1,i);
	printf("%lld\n",res);
	return 0;
}

\(F\)

好玄学的\(dp\)……

首先,对于一个矩阵\(B\),它的权值等价于重新填一个矩阵\(A\),且\(A[i][j]\)要小于等于\(B[i][j]\)对应位置上的那\(n+m-1\)个值中的最小值的方案数,等价于\(A\)中每行的最大值小于等于\(B\)中对应行的最小值,\(A\)中每列的最大值小于等于\(B\)中对应列的最小值,的方案数

那么最终答案可以转化为所有合法的矩阵\(A,B\)的个数

我们记\(X_i\)\(A\)中第\(i\)行的最大值,\(Y_i\)\(B\)中第\(i\)列的最小值,记\(dp[i][j][k]\)表示已经考虑完了所有\(X_i\leq k\)\(i\)行,\(Y_i\leq k\)\(j\)列,此时的方案总数,转移分两步

第一步,枚举\(X_i=k+1\)的行数,那么对于每一个这样的行,在矩阵\(B\)已经考虑完\(Y_i\leq k\)的那\(j\)列中,显然是可以任取\(\geq k+1\)的数,而在矩阵\(A\)还没有考虑的那\(m-j\)列中,显然是可以任取\(\leq k+1\)的数,且得保证至少有一个\(k+1\)

第二步,枚举\(Y_i=k+1\)的列数,那么对于每一个这样的列,在矩阵\(B\)已经考虑完\(X_i\leq k+1\)的那\(i\)行中,显然是可以任取\(\geq k+1\)的数,且至少得有一个\(k+1\),在矩阵\(A\)还没有考虑\(X_i\leq k+1\)的那\(n-i\)行中,可以任取\(leq k+1\)的数

那么额外记一个\(0/1\)表示转移的两步,预处理转移系数,时间复杂度为\(O(nmk(n+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)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(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=105;
int f[N][N][N][2],coef[N][N][N][2],bn[N][N];
int n,m,L,ret,cp;
int main(){
	scanf("%d%d%d%d",&n,&m,&L,&P);
	fp(i,0,max(n,m)){
		bn[i][0]=1;
		fp(j,1,i)bn[i][j]=add(bn[i-1][j],bn[i-1][j-1]);
	}
	fp(k,1,L)fp(i,0,m){
		cp=mul(ksm(L-k+1,i),dec(ksm(k,m-i),ksm(k-1,m-i)));
		ret=1;
		fp(j,0,n)coef[k][i][j][0]=ret,ret=mul(ret,cp);
	}
	fp(k,1,L)fp(i,0,n){
		cp=mul(dec(ksm(L-k+1,i),ksm(L-k,i)),ksm(k,n-i));
		ret=1;
		fp(j,0,m)coef[k][i][j][1]=ret,ret=mul(ret,cp);
	}
	f[1][0][0][0]=1;
	fp(k,1,L)fp(i,0,n)fp(j,0,m){
		ret=f[k][i][j][0];
		fp(l,0,n-i)upd(f[k][i+l][j][1],1ll*ret*bn[n-i][l]%P*coef[k][j][l][0]%P);
		ret=f[k][i][j][1];
		fp(l,0,m-j)upd(f[k+1][i][j+l][0],1ll*ret*bn[m-j][l]%P*coef[k][i][l][1]%P);
	}
	printf("%d\n",f[L+1][n][m][0]);
	return 0;
}
posted @ 2019-10-09 09:01  源曲明  阅读(553)  评论(0编辑  收藏  举报