AtCoder Grand Contest 043 題解 [A~D,EF暫坑]

Task

前言

比賽時只做對A,實在是丟臉.

正題

AA

題意:給你一個n*m的矩陣01矩陣,每次操作使得一個子矩陣的數全部置反,求最少操作使得有一條只經過0就從左上到右下的方案.(只能往右或往下走)

思路:暴力枚舉是不易找到邊界的,我們考慮轉換.
如果把一個子矩陣置反,相當於走原來的圖中的1.
那麼顯然,最少操作數等價於路徑上最少有多少個1的塊.
線性dp即可.

#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pi pair<int,int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=110,size=1<<20;

//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
	char c=gc; x=0; int f=1;
	while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
	while(isdigit(c)) x=x*10+c-'0',c=gc;
	x*=f;
}
template<class o> void qw(o x) {
	if(x/10) qw(x/10);
	putchar(x%10+'0');
}
template<class o> void pr1(o x) {
	if(x<0)x=-x,putchar('-');
	qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
	if(x<0)x=-x,putchar('-');
	qw(x); puts("");
}

char s[N][N];
int f[N][N],n,m;

int main() {
	qr(n); qr(m); memset(f,63,sizeof f);
	for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
	f[1][1]=(s[1][1]=='#');
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=m;j++)
			f[i+1][j]=min(f[i+1][j],f[i][j]+(s[i][j]!=s[i+1][j])),
			f[i][j+1]=min(f[i][j+1],f[i][j]+(s[i][j]!=s[i][j+1]));
	}
	pr2(f[n][m]+1>>1);
	return 0;
}

BB

比賽的時候覺得題意簡單但是不會.
但是賽後題解還是講的很好的.


首先,把輸入的[1,3]變爲[0,2].(以下均認爲輸入爲[0,2])
考慮弱化版本,輸入只有[0,1].
那麼xy=xxory|x-y|=x \operatorname{xor }y
那麼每個數ii對結果的貢獻就是nn行楊輝三角的第ii個數,大小正好爲Cn1i1=(n1)!(i1)!(ni)!C_{n-1}^{i-1}=\dfrac{(n-1)!}{(i-1)!(n-i)!}.
答案顯然爲[0,1],所以我們只在意Cn1i1=(n1)!(i1)!(ni)!C_{n-1}^{i-1}=\dfrac{(n-1)!}{(i-1)!(n-i)!}的奇偶性,具體處理方法爲求每個階乘有多少個2的因子.

現在開始推廣.如果輸入有1,那麼答案不能爲2.
爲啥呢?因爲只要有1,到倒數第2行(只有2個數)就一定有1,答案只能爲[0,1].
所以得到以下求解思路:

  • 若輸入有1,判斷答案的奇偶性則可得到答案.
  • 輸入無1(即只可能有0,2),那麼把每個數/2,就是與上面相同的問題啦.
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e6+10;
int n,cnt[N],sum[N],tmp=2,ans;
char a[N];
int main() {
	scanf("%d",&n); n--;scanf("%s",a);
	for(int i=0;i<=n;i++) {
		a[i]-='1';
		if(a[i]==1) tmp=1;
	}
	for(int i=1;i<=n;i++) 
		sum[i]=sum[i-1]+(cnt[i]=(i&1?0:cnt[i>>1]+1));
	for(int i=0;i<=n;i++)
		if(sum[n]==sum[i]+sum[n-i])
			(ans+=a[i]/tmp)&=1;
	printf("%d\n",ans*tmp); return 0;
}

CC

求一個神奇的圖的最大權獨立集.

顯然可以貪心先選擇權值較大的(不知道如何理性證明 ).
upd:我得到了兔隊的回覆.
在這裏插入圖片描述
總結:對於這個(i+j+k)(i+j+k)分層圖,層數大的一個點要比所有層數比它小的節點的權值都要多,所以當然要先取層數大的啦.(同一層的點必然不交),爲了取得儘量多,我們間隔着撿,這明顯對應着一個公平組合問題.
所以我們把邊看成指向權值大的.
然後把每個圖當做有向圖遊戲那樣求出所有的點的sg函數.(不能動者輸)
每個圖看作一個子遊戲的話,那麼全局的sg函數就爲仨的異或值.

引理:對於全局的有向圖必敗態而言,它們必然無邊相連.
證明:根據sg函數的定義,必敗態只能接必勝態. Q.E.D.

然後,根據題解神奇的定義:一張有向圖sg函數的最大值爲m\sqrt m.

證明:設f(i)maxsg=if(i)表示maxsg=i時的最小邊數,則有f(0)=0,f(i)=f(i1)+i=(i+1)i2f(0)=0,f(i)=f(i-1)+i=\dfrac{(i+1)*i}{2}
然後我們解方程f(x)=(x+1)x2=mf(x)=\dfrac{(x+1)*x}{2}=m,則xmx\approx \sqrt m

所以,可以抄得如下代碼:

#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lc (x<<1)
#define rc (x<<1|1)
#define gc (p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pi pair<int,int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e5+10,size=1<<20,mod=998244353;

char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
	char c=gc; x=0; int f=1;
	while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
	while(isdigit(c)) x=x*10+c-'0',c=gc;
	x*=f;
}
template<class o> void qw(o x) {
	if(x/10) qw(x/10);
	putchar(x%10+'0');
}
template<class o> void pr1(o x) {
	if(x<0)x=-x,putchar('-');
	qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
	if(x<0)x=-x,putchar('-');
	qw(x); puts("");
}

struct edge{int y,next;}a[N];int len,last[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]};last[x]=len;}

void upd(int &x) {x+=x>>31&mod;}

ll power(ll a,ll b=mod-2) {
	ll c=1;
	for(   ;b;b>>=1,a=a*a%mod)
		if(b&1) c=c*a%mod;
	return c;
}

int n,m,f[N],sum[3][N],sz[3],g[N],num,ans,fir,inv=power(fir=power(10,18));

int main() {
	qr(n);
	for(int graph=0;graph<3;graph++) {
		if(len) memset(last+1,0,n<<2),len=0;
		qr(m); for(int j=1,x,y;j<=m;j++) {
			qr(x),qr(y);
			if(x>y) swap(x,y);
			ins(x,y);
		}
		int now=power(fir,n);
		for(int i=n; i;i--) {
			//求sg函數(f)
			++num;
			for(int k=last[i];k;k=a[k].next)
				g[f[a[k].y]]=num;
			f[i]=0;
			while(g[f[i]]==num) f[i]++;
			sz[graph]=max(sz[graph],f[i]);
			upd(sum[graph][f[i]]+=now-mod);
			now=(ll)now*inv%mod;
		}
	}
	for(int i=0;i<=sz[0];i++)
		for(int j=0,k;j<=sz[1];j++) {
			k=i^j;
			upd(ans+=(ll)sum[0][i]*sum[1][j]%mod*sum[2][k]%mod-mod);
		}
	pr2(ans);
	return 0;
}

DD

DP好題.思維難度高,代碼量小.

簡明題意:有n個含三個元素的隊列(A[1...n][1..3]A[1...n][1..3]),(這3n個數構成一個3n的排列),每次操作如下:
選出最小隊頭,輸出並刪除.求不同的輸出( P )數.

可以發現:若A[i][j]>A[i][j+1]A[i][j]>A[i][j+1]的話,那麼A[i][j],A[i][j+1]PA[i][j],A[i][j+1]爲P中的相鄰位置.
所以我們可以把AA進行壓縮.設壓縮成BB.
B(block,)B(block,存塊信息)數組中存放的元素爲A[i]A[i]中的前綴最大值的去重結果.
舉個栗子,設A={{3,1,2},{5,6,1}},B={3,2,5,6},{2,3,5,6}A=\{ \{3,1,2\},\{5,6,1\}\},則B=\{3,2,5,6\},輸出就是\{2,3,5,6\}所對應的塊.
然後解題的重要轉化就是P<>A<>BP<->A<->B.
映射關係:

定義域 映射
A B
A P

所以,我們可以換一種思路:把判斷PP是否成立轉換爲判斷是否存在BB能與之對應.
BB排序後有如下性質:

  1. 有序(廢話)
  2. 每個B中元素對應A中至多3個元素,且B中元素對應長度爲1的數量\geB中元素對應長度爲2的數量.

反過來,設B排序後的對應長度分別爲a1,a2,..,aka_1,a_2,..,a_k,則其對應的的PP的數量爲n!/(j=1k(i=1jai))n!/(\prod_{j=1}^k (\sum_{i=1}^j a_i)).
因爲排序後B[1]a1,1/a1B[1]是a_1個元素中最小的,成立的概率爲1/a_1,
B[2]1/(a1+a2)B[2]成立的概率爲1/(a_1+a_2).
綜上得證.

所以我們可以進行DP.
定義狀態f[i][j]f[i][j],i=ai,j=12.i=\sum a_i,j=長度爲1的-長度爲2的.
複雜度O(n2)O(n^2).

#include<map>
#include<set>
#include<queue>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pi pair<int,int>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=12010,size=1<<20;

//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
	char c=gc; x=0; int f=1;
	while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
	while(isdigit(c)) x=x*10+c-'0',c=gc;
	x*=f;
}
template<class o> void qw(o x) {
	if(x/10) qw(x/10);
	putchar(x%10+'0');
}
template<class o> void pr1(o x) {
	if(x<0)x=-x,putchar('-');
	qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
	if(x<0)x=-x,putchar('-');
	qw(x); puts("");
}

int n,mod,tot,f[N][N];ll inv[N],jc;//f[i][j]表示i個數,j表示1的個數-2的個數 
void upd(int &x) {x+=x>>31&mod;}

int main() {
	qr(n); qr(mod); tot=3*n+1; 
	f[0][tot]=1;
	inv[1]=jc=1; for(int i=2;i<tot;i++) inv[i]=inv[mod%i]*(mod-mod/i)%mod,jc=jc*i%mod;
	for(int i=1;i<tot;i++) 
		for(int j=-i+tot;j<=i+tot;j++) {
			f[i][j]=f[i-1][j-1]*inv[i]%mod;
			if(i>=2)upd(f[i][j]+=f[i-2][j+1]*inv[i]%mod-mod);
			if(i>=3)upd(f[i][j]+=f[i-3][j  ]*inv[i]%mod-mod);
		}
	ll ans=0; 
	for(int i=tot;i<=tot*2;i++) ans+=f[tot-1][i];
	ans%=mod; pr2(ans*jc%mod);
	return 0;
}

EE

FF

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