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;
}

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