1. 程式人生 > 實用技巧 >ARC104遊記

ARC104遊記

目錄

ARC104遊記

本人打的第一場 ARC ,還好沒有太難看。

A Plus Minus

題意簡述

給定 \(X+Y\)\(X-Y\) ,求 \(X\)\(Y\)

題目分析

\(X=((X+Y)+(X-Y))/2,Y=((X+Y)-(X-Y))/2\)

參考程式碼

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
int main(){
	int a,b;
	read(a),read(b);
	write((a+b)/2),pc(' '),write((a-b)/2);
	return 0;
}

B DNA Sequence

題意簡述

給定鹼基序列 \(S\) ,詢問 \(S\) 中有多少個子串 \(T\) 滿足 \(T\) 重排後可以與重排前配對。

\(1\le |S|\le 5000\)

題目分析

\(T\) 重排後可以與重排前配對當前僅當 \(T\)A 的數量等於 T 的數量、 C 的數量等於 G 的數量,列舉所有子串,然後字首和差分或者動態維護求出 A T C G 數量,直接判斷即可。

參考程式碼

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
const int maxn=5005;
char s[maxn];
int A[maxn],C[maxn],G[maxn],T[maxn];
int main(){
	int n;read(n);
	scanf("%s",s+1);
	for(int i=1;i<=n;++i){
		A[i]=A[i-1]+(s[i]=='A');
		C[i]=C[i-1]+(s[i]=='C');
		G[i]=G[i-1]+(s[i]=='G');
		T[i]=T[i-1]+(s[i]=='T');
	}
	int ans=0;
	for(int i=1;i<=n;++i){
		for(int j=i;j<=n;++j){
			if(A[j]-A[i-1]==T[j]-T[i-1]&&C[j]-C[i-1]==G[j]-G[i-1])
				++ans;
		}
	}
	write(ans),pc('\n');
	return 0;
}

C Fair Elevator

題意簡述

有一個長度為 \(2N\) 的序列 \(1,2,\dots,2N\) ,有 \(N\) 個配對關係 \(A_i\to B_i(A_i<B_i)\) ,滿足每個 \(1\sim 2N\) 中的每個數只出現一次,並且如果 \(l_0,r_0\) 配對且 \(l_1,r_1\) 配對且 \(l_0<l_1<r_0\) 那麼必須滿足 \(r_0-l_0=r_1-l_1\) ,現在有些關係損壞了(即有一些 \(A_i\)\(B_i\) 將不會給出),請問給出的配對關係是否可能合法(滿足上述條件)。

\(1\le N\le 100\)

題目分析

假設存在配對關係 \(l\to r(l<r)\) ,並且沒有配對關係跨越 \(l\) ,那麼必然存在配對關係 \(l+1\to r+1,l+2\to r+2,\dots,r-1\to r+r-l-1\) ,也就是說這些配對關係構成了一個區間 \([l,r+r-l-1]\) ,由此可以得到啟發:或許我們可以使用區間 dp 來解決這個問題。

\(dp(l,r)\) 表示僅考慮 \([l,r]\) 這個區間內的數兩兩之間的配對,是否可以使得這個區間內的數互相兩兩配對。

兩種情況 \(dp(l,r)\) 是 true ,第一種情況就是 \([l,r]\) 中的配對關係恰好構成了這個區間,第二種情況就是這個區間可以通過某個分界點分成兩個區間,而這兩個區間都是 true 。

第一種情況直接暴力判斷即可,第二種情況直接暴力列舉遞迴即可,時間複雜度 \(\mathcal O(n^3)\) ,細節可能有點多,注意特判(我居然在考場上一遍 A ,真的舒適)。

參考程式碼

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
const int maxn=105;
int match[maxn*2],n;
int Match(int d,int u){
	int re=true;
	if(match[d]){
		if(match[d]<=n*2)
			re&=(match[d]==u);
		else
			re&=(match[d]==n*2+1);
	}
	if(match[u]){
		if(match[u]<=n*2)
			re&=(match[u]==d);
		else
			re&=(match[u]==n*2+2);
	}
	if(match[d]==n*2+1&&match[u]==n*2+2)
		re=false;
	return re;
}
// 0:no match 1-n*2:ma n*2+1:up n*2+2:down
int dp[maxn][maxn];
int solve(int l,int r){
	if(~dp[l][r])return dp[l][r];
	int len=r-l+1,re=true;
	for(int i=l;i<=r&&re;++i)
		re&=Match(l-1+i,l-1+i+len);
	if(re)return dp[l][r]=true;
	for(int x=l;x<r&&!re;++x)
		re|=solve(l,x)&&solve(x+1,r);
	return dp[l][r]=re;
}
int main(){
	int ok=true;read(n);
	for(int i=1;i<=n&&ok;++i){
		int a,b;
		read(a),read(b);
		if(a!=-1&&b!=-1&&a>=b)ok=false;
		else{
			if(a==-1&&b==-1)continue;
			else if(a==-1){
				if(match[b])ok=false;
				else match[b]=n*2+2;
			}
			else if(b==-1){
				if(match[a])ok=false;
				else match[a]=n*2+1;
			}
			else{
				if(match[a]||match[b])ok=false;
				else match[a]=b,match[b]=a;
			}
		}
	}
	memset(dp,-1,sizeof dp);
	if(!ok)puts("No");
	else puts(solve(1,n)?"Yes":"No");
	return 0;
}

D Multiset Mean

題意簡述

給定 \(N,K,M\) ,對於每個整數 \(1\le x\le N\) ,求出構造整數序列 \(\{a\}\) ,滿足 \(a\) 的長度為 \(N\) ,並且對於任意的 \(1\le i\le N\) ,都滿足 \(0\le a_i\le K\) ,且 \(\frac{\sum_{i=1}^na_i\times i}{\sum_{i=1}^na_i}=x\) (帶權平均數為 \(x\) )對 \(M\) 取模的方案數。

\(1\le N,K\le 100,10^8\le M\le 10^9+9\) ,保證 \(M\) 為質數。

題目分析

平均數為 \(x\) 不太好算,考慮到:

\[\frac{\sum_{i=1}^na_i\times i}{\sum_{i=1}^na_i}=x \]

\[\Leftrightarrow \sum_{i=1}^na_i\times (i-x)=0 \]

\[\Leftrightarrow \sum_{i=1}^{n-x}a_i\times i=\sum_{i=1}^{x-1}a_{x-i}\times i \]

\(f(n,m)\) 為構造長度為 \(n\) 的序列 \(\{a\},0\le a_i\le K\) 滿足 \(m=\sum_{i=1}^na_i\times i\) 的方案數,然後遞推計算,需要用到類似字首和優化的方法,預處理時間複雜度是 \(\mathcal O(N^3K)\) 的。

然後列舉 \(x\) ,直接列舉 \(m\) ,找出令上面等式左邊右邊均為 \(m\) 的方案數,需要注意一點細節,比如 \(a_x\) 的取值,這部分的時間複雜度是 \(\mathcal O(N^3K)\) 的。

參考程式碼

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<assert.h>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
const int maxn=105,maxs=510000;
int m;
int mo(const int x){
	return x>=m?x-m:x;
}
int k;
int dp[maxn][maxs];
int main(){
	int n;
	read(n),read(k),read(m);
	dp[0][0]=1;
	for(int i=1,mx=0;i<=n;++i){
		mx+=i*k;
		dp[i][0]=1;
		for(int j=0;j<i;++j)
			dp[i][j]=dp[i-1][j];
		for(int j=i;j<=mx;++j){
			dp[i][j]=mo(dp[i][j-i]+dp[i-1][j]);
			if(j>=i*(k+1))dp[i][j]=mo(m-dp[i-1][j-i*(k+1)]+dp[i][j]);
		}
	}
	for(int i=1;i<=n;++i){
		if(i==1||i==n)write(k),pc('\n');
		else{
			int l=i-1,r=n-i,ans=0;
			int mx=min(l*(l+1)/2,r*(r+1)/2)*k;
			for(int j=1;j<=mx;++j)
				ans=mo(ans+1ll*dp[l][j]*dp[r][j]%m);
			ans=1ll*ans*(k+1)%m;ans=mo(ans+k);
			write(ans),pc('\n');
		}
	}
	return 0;
}

E Random LIS

這題考場上沒切,感覺這種 dp 方法以前都沒有用過,所以有點不太會用。

題目簡述

給定長度為 \(N\) 的序列 \(\{A\}\) ,有一個整數序列 \(\{a\}\) ,其中 \(a_i\)\([1,A_i]\) 中均勻隨機,求 \(a_i\) 最長嚴格遞增子序列的期望長度。

\(1\le N\le 6,1\le A_i\le 10^9\)

題目分析

這個 \(N\) 怎麼這麼小啊?這個 \(A\) 怎麼這麼大啊?

考慮直接列舉所有可能的大小關係,也就是列舉有順序集合劃分 \(S_1,S_2,\dots,S_k\) ,滿足 \(S_i\) 中的 \(a\) 兩兩相等,並且 \(S_i\) 中的 \(a\) 小於 \(S_{i+1}\) 中的 \(a\) (當 \(N=6\) 時,這個列舉次數為 \(4683\) ),然後求方案數,因為 \(S\) 都被枚舉出來了,所以這個方案數對答案的貢獻也就求出來了,接下來的問題就是求方案數了。

求方案數的時候把所有相等的 \(a\) 合併,其中的 \(A_i\) 去最小值,問題就轉化成了:給定長度為 \(N\) 的序列 \(\{A\}\) ,有一個整數序列 \(\{a\}\) ,其中 \(a_i\)\([1,A_i]\) 中均勻隨機,求 \(a_i\) 是嚴格遞增序列的方案數。

然後就變成了這道題目了:CF1295F;這道題目也有點像:[APIO2016]划艇

在這裡簡單講一下做法,值域這麼大,顯然要離散化,不妨規定離散化後的第 \(i\) 小的數和第 \(i+1\) 小的陣列成的區間為第 \(i\) 個區間,離散化完後設 \(f_{i,j}\) 表示僅考慮序列的前 \(i\) 項,其中 \(a_i\) 的取值在離散化後的第 \(j\) 個區間內的方案數,轉移就是列舉 \(a_i\) 前面有多少個也在第 \(j\) 個區間內。

具體轉移如下(設第 \(j\) 個區間為 \(I_j\) ,區間長度為 \(L_j\)\(a_k\) 的取值區間為 \(S_k\) ):

\[f_{i,j}=\sum_{k=0}^{j-1}f_{i-1,k}{L_j\choose j-k}[\forall k<t\le i,I_j\in S_t] \]

參考程式碼

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
const int maxn=10,mod=1000000007;
int mo(const int x){
	return x>=mod?x-mod:x;
}
int power(int a,int x){
	int re=1;
	while(x){
		if(x&1)re=1ll*re*a%mod;
		a=1ll*a*a%mod,x>>=1;
	}
	return re;
}
int inv[maxn],st[maxn],cp[maxn],tp,all[maxn],f[maxn],g[maxn];
int solve(void){
	int cn=0;
	for(int i=1;i<=tp;++i)
		st[i]=cp[i];
	for(int i=1;i<=tp;++i)
		all[cn++]=++st[i],f[i]=0;
	all[cn++]=1;sort(all,all+cn);
	cn=unique(all,all+cn)-all;
	for(int i=1;i<=tp;++i)
		st[i]=lower_bound(all,all+cn,st[i])-all;
	f[0]=1;
	for(int i=0;i<cn-1;++i){
		int len=all[i+1]-all[i];g[0]=1;
		for(int j=1;j<=tp;++j)g[j]=1ll*g[j-1]*inv[j]%mod*(len-j+1)%mod;
		for(int j=tp;j;--j)if(i<st[j]){
			for(int k=j-1;~k;--k){
				f[j]=mo(f[j]+1ll*f[k]*g[j-k]%mod);
				if(i>=st[k])break;
			}
		}
	}
	return f[tp];
}
int a[maxn],id[maxn],n,ans,pos[maxn],mx[maxn];
void dfs(int no){
	if(no==0){
		int cnt=0;
		for(int i=n;i>=1;--i){
			mx[i]=1;
			for(int j=i+1;j<=n;++j){
				if(pos[i]==pos[j]||id[j]>=id[i])continue;
				mx[i]=max(mx[i],mx[j]+1);
			}
			cnt=max(cnt,mx[i]);
		}
		return ans=mo(ans+1ll*cnt*solve()%mod),void();
	}
	int U=1<<no;++tp;
	for(int s=1;s<U;++s){
		cp[tp]=1000000001;
		int up=no;
		for(int i=no;i>=1;--i){
			if((s>>(i-1))&1){
				cp[tp]=min(cp[tp],a[id[i]]);
				pos[up]=tp;swap(id[i],id[up--]);
			}
		}
		dfs(up);
		for(int i=1;i<=no;++i){
			if((s>>(i-1))&1){
				swap(id[i],id[++up]);
			}
		}
	}
	--tp;
}
int main(){
	read(n);int sum=1;
	for(int i=1;i<=n;++i)
		read(a[i]),id[i]=i,inv[i]=(i==1?1:1ll*(mod-mod/i)*inv[mod%i]%mod),sum=1ll*sum*a[i]%mod;
	dfs(n);write(1ll*ans*power(sum,mod-2)%mod);pc('\n');
	return 0;
}

F Visibility Sequence

這題考場上也沒有 A 。

先估著,把程式碼貼上來。

參考程式碼

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ch() getchar()
#define pc(x) putchar(x)
template<typename T>inline void read(T&x){
	int f;char c;
	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>inline void write(T x){
	static char q[64];int cnt=0;
	if(!x)pc('0');if(x<0)pc('-'),x=-x;
	while(x)q[cnt++]=x%10+'0',x/=10;
	while(cnt--)pc(q[cnt]);
}
const int maxn=105,mod=1000000007;
int mo(const int x){
	return x>=mod?x-mod:x;
}
int f[maxn][maxn][maxn],g[maxn][maxn][maxn],a[maxn];
int main(){
	int n;read(n);a[1]=++n;
	for(int i=2;i<=n;++i)read(a[i]);
	for(int i=1;i<=n;++i){
		f[i][i][1]=1;
		for(int j=1;j<=n;++j)
			g[i][i][j]=mo(g[i][i][j-1]+f[i][i][j]);
	}
	for(int len=1;len<n;++len){
		for(int l=1,r=l+len;r<=n;++l,++r){
			for(int x=2;x<=n&&x<=a[l];++x){
				for(int i=l+1;i<=r;++i){
					f[l][r][x]=mo(f[l][r][x]+1ll*g[l][i-1][x]*f[i][r][x-1]%mod);
					f[l][r][x]=mo(f[l][r][x]+1ll*f[l][i-1][x]*g[i][r][x-2]*(a[i]>=x-1)%mod);
				}
			}
			for(int x=1;x<=n;++x)
				g[l][r][x]=mo(g[l][r][x-1]+f[l][r][x]);
		}
	}
	write(g[1][n][n]),pc('\n');
	return 0;
}