agc 041题解(除了F)

大概,接下来半年都不怎么打比赛了(大哭)
这场比赛自己打得并不好,似乎也是冥冥中的必然吧。
先看C:
题意:在n*n上放置多米诺骨牌,不重叠,不要求全覆盖。
对于每一行,我们记它的值为在这一行的方块上有覆盖的多米诺骨牌的个数,列同理。我们要求这些数要相同,且至少放置一块多米诺骨牌。1n1031\leqslant n \leqslant 10^3
行的话输出-1,不行的话输出方案。
解法:3,4,5,7构造出来
3,4比较容易手玩,但5,7博主这垃圾水平,绝对玩不出来。
如果有读者有算法方法,欢迎在下面评论。
然后4,5,7的时候构造满足每行每列都是3。
于是我们得到了一个这样的算法:
先特判是3的倍数的n
然后判可以表示成4x+5y+7z4x+5y+7z的n
然后你发现只有n=1,2会判成-1,显然

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
#define Maxn 1005
bool f[Maxn];
int pre[Maxn];
char ch[Maxn][Maxn];
char c3[10][10]={
	{'a','a','.'},
	{'.','.','a'},
	{'.','.','a'} 
};
char c4[10][10]={
    {'a','a','b','c'},
    {'d','d','b','c'},
    {'b','c','a','a'},
    {'b','c','d','d'}
};
char c5[10][10]={
    {'a','a','b','b','a'},
    {'b','c','c','.','a'},
    {'b','.','.','c','b'},
    {'a','.','.','c','b'},
    {'a','b','b','a','a'}
};
char c7[10][10]={
    {'a','a','b','b','c','c','.'},
    {'d','d','.','d','d','.','a'},
    {'.','.','d','.','.','d','a'},
	{'.','.','d','.','.','d','b'},
	{'d','d','.','d','d','.','b'},
	{'.','.','d','.','.','d','c'},
	{'.','.','d','.','.','d','c'} 
};


inline void output(){
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j)printf("%c",ch[i][j]);
		puts("");
	}
}

int main(){
	scanf("%d",&n);
	f[0]=true;
	for(int i=1;i<=n;++i){
		if(i>=4&&f[i-4]){
			f[i]=true;
			pre[i]=4;
		}
		if(i>=5&&f[i-5]){
			f[i]=true;
			pre[i]=5;
		}
		if(i>=7&&f[i-7]){
			f[i]=true;
			pre[i]=7;
		}
	}
	for(int i=1;i<=n;++i)
	    for(int j=1;j<=n;++j)ch[i][j]='.';
	if(n%3==0){
		for(int i=1;i<=n;i+=3)
		    for(int j=0;j<3;++j)
		        for(int k=0;k<3;++k)
		            ch[i+j][i+k]=c3[j][k];
		output();
	}else if(f[n]){
		int tmp=n;
		while(n){
			if(pre[n]==4){
				for(int i=n-3;i<=n;++i)
				    for(int j=n-3;j<=n;++j)
				         ch[i][j]=c4[i-n+3][j-n+3];
			}
			if(pre[n]==5){
				for(int i=n-4;i<=n;++i)
				    for(int j=n-4;j<=n;++j)
				        ch[i][j]=c5[i-n+4][j-n+4];
			}
			if(pre[n]==7){
				for(int i=n-6;i<=n;++i)
				   for(int j=n-6;j<=n;++j)
				        ch[i][j]=c7[i-n+6][j-n+6];
			}
			n-=pre[n];
		}
		n=tmp;
		output(); 
	}else puts("-1");
	return 0;
}

D:
问有多少个长度为n的序列满足一下条件
A1A2...AnA_1 \leqslant A_2 \leqslant ... \leqslant A_n
1Ain(1in)1\leqslant A_i \leqslant n(1\leqslant i \leqslant n)
i=1kAi>i=nk+2nAi,2kn\sum_{i=1}^kA_i > \sum_{i=n-k+2}^n A_i,2\leqslant k \leqslant n
2n50002\leqslant n \leqslant 5000
解法
不妨我们弄出一个新序列,在A的基础上构造
b1=A1,bi=AiAi1for i>1b_1=A_1,b_i=A_i-A_{i-1} for\ i>1
好嘞,这要求b1b_1为正,后面的为非负,且总和不超过n
再来考虑一下如何满足第二个条件
我们发现,当k=n2k=\left \lceil \frac{n}{2}\right \rceil满足的时候,所有n都会满足
那考虑k=n2k=\left \lceil \frac{n}{2}\right \rceil满足的时候b需要满足些什么
写成不等式,展开,化简,得到
b1>i=2ncibib_1>\sum_{i=2}^{n}c_ib_i
其中cic_i的系数读者自行得到,非负
当然,还有另一个不等式是我们需要满足的
b1ni=2nbib_1\leqslant n-\sum_{i=2}^nb_i
于是,当b2nb_{2到n}确定以后,b1b_1的取值有ni=2n(ci+1)bin-\sum_{i=2}^n(c_i+1)b_i个,当然负数就不能算上贡献了
于是,很容易想到,对右边的式子dp一下即可
考虑一下正确性
只要我们保证了过程中bib_i非负
且只枚举i=2n(ci+1)bi<n\sum_{i=2}^n(c_i+1)b_i <n时的贡献
b1b_1的取值也是会大于0的
没错

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,Mod;
#define Maxn 5005
int ans[Maxn],a[Maxn];
int main(){
	scanf("%d%d",&n,&Mod);
	if(n&1){
		int k=(n-1)/2+1;
		for(int i=0;i<=k-2;++i)a[2+i]=i;
		for(int i=k-1,j=1;i>=1;--i,++j)a[k+j]=i;
	}else{
		int k=n/2;
		for(int i=0;i<=k-2;++i)a[2+i]=i;
		a[k+1]=k-1;
		for(int i=k-1,j=1;i>=1;--i,++j)a[k+1+j]=i;
	}
	for(int i=2;i<=n;++i)a[i]++;
	ans[0]=1;
	for(int i=2;i<=n;++i){
		for(int j=a[i];j<n;++j){
			ans[j]+=ans[j-a[i]];
			if(ans[j]>=Mod)ans[j]-=Mod;
		}
	}
	int Ans=0;
	for(int i=0;i<n;++i)Ans=(Ans+1ll*(n-i)*ans[i])%Mod;
	printf("%d\n",Ans);
	return 0;
} 

E:
题意有点复杂了,不想说
解法
T=1:考虑枚举w,并且判断它可不可以。
我们从右向左加入balancer,并且动态维护一个长度为n的布尔数组B。BiB_i表示当前i出发,最后会不会停止在w。一开始,只有Bw=1B_w=1
假设我们当前从右往左加入到了x和y
那么显然,为了让更多的出发点最后集中在w
AxAyA_x和A_y会变成AxAyA_x|A_y,定方向的方法很好像
如果我们每个w都进行一遍这个过程,显然不太好
bitset压位!
维护n的长度为n的bitset
其中bi,jb_{i,j}表示当w=j时,当前时刻从i出发,会不会终止在j
初始bi,i=1b_{i,i}=1
每次只需要bx=by=bxbyb_x=b_y=b_x|b_y
最后对bib_i去个交即可
T=2:小清新的构造
首先n=2的时候肯定不行
考虑n>2的时候,我们证明它一定有方案
同样是从右到左加入
维护一个整数数组,长度为n,其中BiB_i表示i此刻出发的话,最后到达哪里
只要我们保证B每时每刻都会有两个不同的值即可
初始时Bi=iB_i=i显然满足
Bx=ByB_x=B_y无所谓
如果BxB_x在B中出现了至少两次,那么令Bx=ByB_x=B_y
如果ByB_y在B中出现了至少两次,同上
如果都只出现了一次,随意,然后因为n>2,所以还存在一个既不等于BxB_x,也不等于ByB_y的值
不同的值保证了至少存在两个

#include<iostream>
#include<cstring>
#include<cstdio>
#include<bitset>
#include<algorithm>
using namespace std;
int n,m,T;
#define Maxn 50010
int x[Maxn<<1],y[Maxn<<1];
bitset<Maxn> b[Maxn];
bool ap[Maxn];
int seq[Maxn<<1];
int to[Maxn],B[Maxn];

inline void rd(int &x){
	x=0;char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
}

int main(){
	rd(n);rd(m);rd(T);
	for(int i=1;i<=m;++i){
		rd(x[i]);rd(y[i]);
    }
    if(T==1){
		for(int i=1;i<=n;++i)b[i][i]=1;
		bitset<Maxn> res;
		for(int i=m;i>=1;--i){
			res=b[x[i]]|b[y[i]];
			b[x[i]]=b[y[i]]=res;
		}
		res=b[1];
		for(int i=2;i<=n;++i)res&=b[i];
		int flag=-1;
		for(int i=1;i<=n;++i)
		    if(res[i]){
				flag=i;
				break;
		    } 
		if(flag==-1){
			puts("-1");
			return 0;
		}
		ap[flag]=true;
		for(int i=m;i>=1;--i){
			if(ap[x[i]]==false&&ap[y[i]]==false)seq[i]=0;
			else if(ap[x[i]]==true&&ap[y[i]]==true)seq[i]=0;
		    else if(ap[x[i]])seq[i]=0;
		    else seq[i]=1;
		    bool tmp=ap[x[i]]|ap[y[i]];
		    ap[x[i]]=ap[y[i]]=tmp;
		}
		for(int i=1;i<=m;++i)
		    if(seq[i]==0)printf("^");
		    else printf("v");
    }else{
		if(n==2){
			puts("-1");
			return 0;
		}
		for(int i=1;i<=n;++i){
			B[i]=i;
			to[i]=1;
		}
		for(int i=m;i>=1;--i){
			if(B[x[i]]==B[y[i]]){
				seq[i]=0;
				continue;
			}
			if(to[B[x[i]]]>=2){
				seq[i]=1;
				to[B[x[i]]]--;
				B[x[i]]=B[y[i]];
				to[B[y[i]]]++; 
			}else{
				seq[i]=0;
				to[B[y[i]]]--;
				B[y[i]]=B[x[i]];
				to[B[x[i]]]++;
			}
		}
		for(int i=1;i<=m;++i)
		    if(seq[i]==0)printf("^");
		    else printf("v");
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章