ARC108 游記


ARC108 游記

繼續沒臉見人

果然這就是罰時吃到飽。/kk

F 好像比 E 要簡單。

D - AB

題意簡述

給定 \(n\) 和四個字符 \(c_{AA},c_{AB},c_{BA},c_{BB}\) ,並且 \(c_{AA},c_{AB},c_{BA},c_{BB}\in \{A,B\}\)

一開始你有一個長度為 \(2\) 的字符串 AB ,每次操作你都可以選擇兩個相鄰字符 \(c_l,c_r\) ,並在它們之間插入字符 \(c_{c_lc_r}\) ,詢問你最終可以得到多少種長度為 \(n\) 的本質不同的字符串。

\(2\le n\le 1000\)

題目分析

分類討論,一開始肯定是在 AB 間插入 \(c_{AB}\)\(c_{AB}=A\)\(c_{AB}=B\) 的情況沒有什么區別,不妨假設 \(c_{AB}=A\)

此時字符串會變成 AAAA...AAB ,如果 \(c_{AA}=A\) ,那么最終只能變成 AAA...AAAB ,答案就是 \(1\) ,否則 \(c_{AA}=B\) ,此時可以選擇在 AA 之間插入 B 變成 ABA ,如果 \(c_{BA}=A\) ,說明在最終情況中不能出現 B 相鄰的情況,並且第一個字符和倒數第二個字符必須是 A ,最后一個字符必須是 B ,答案也就是 \(\sum_{i\ge 0}{n-3-(i-1)\choose i}\) ,也就是斐波那契數列的第 \(n-3\) 項,矩乘優化到一個 \(\log_2\),否則如果 \(c_{BA}=B\) ,說明在最終情況中可以出現 B 相鄰的情況,答案也就是 \(2^{n-3}\)

總的復雜度是 \(\mathcal O(\log_2n)\)

參考代碼

賽時代碼,當時寫的不是最優復雜度,僅供參考。

#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=1005,mod=1000000007;
int mo(const int x){
	return x>=mod?x-mod:x;
}
int fac[maxn],iac[maxn];
int binom(int n,int m){
	return 1ll*iac[m]*iac[n-m]%mod*fac[n]%mod;
}
char caa[5],cab[5],cba[5],cbb[5];
int main(){
	int n;read(n);if(n==2)return puts("1"),0;fac[0]=fac[1]=iac[0]=iac[1]=1;
	for(int i=2;i<=n;++i)iac[i]=1ll*(mod-mod/i)*iac[mod%i]%mod;
	for(int i=2;i<=n;++i)iac[i]=1ll*iac[i-1]*iac[i]%mod,fac[i]=1ll*fac[i-1]*i%mod;
	scanf("%s%s%s%s",caa,cab,cba,cbb);int ans=0;
	if(cab[0]=='A'){
		if(caa[0]=='A')ans=1;
		else{
			if(cba[0]=='A'){
				ans=1;
				for(int i=1;n-3-(i-1)>=i;++i)
					ans=mo(ans+binom(n-3-(i-1),i));
			}
			else{
				for(int i=0;i<=n-3;++i)
					ans=mo(ans+binom(n-3,i));
			}
		}
	}
	else{
		if(cbb[0]=='B')ans=1;
		else{
			if(cba[0]=='B'){
				ans=1;
				for(int i=1;n-3-(i-1)>=i;++i)
					ans=mo(ans+binom(n-3-(i-1),i));
			}
			else{
				for(int i=0;i<=n-3;++i)
					ans=mo(ans+binom(n-3,i));
			}
		}
	}
	write(ans),pc('\n');
	return 0;
}

E Random IS

題意簡述

定義大小為 \(n\) 的排列 \(a\) 的一個子序列 \(b\) 是好的當且僅當 \(b\) 遞增,對於 \(a\) 的子序列 \(b\) 而言,定義一個數 \(i\) 是好的當且僅當 \(a_i\) 加入 \(b\)\(b\) 仍然遞增,現在給定 \(n\) 和排列 \(a\) ,你有一個 \(a\) 的子序列 \(b\) ,初始為空,每次操作你會在所有好的數中選擇等概率隨機一個好的數 \(i\) 並將 \(a_i\) 加入 \(b\) ,詢問期望操作次數。

\(1\le n\le 2000\)

題目分析

簡單期望題,但是自己賽時由於太蠢沒有想出來。

題目等價於隨機一個排列 \(p\) ,然后從前往后加數,如果可以加就加,問期望加入了多少個數。

如果當前考慮的是一段區間 \((l,r)\) ,如果選擇了 \(s\) ,那么 \(s\) 就會將其分成兩個區間 \((l,s),(s,r)\) ,並且在這兩個區間中選數是互不影響的,所以可以認為是先在 \((l,s)\) 中隨機排列然后選數,然后再在 \((s,r)\) 中隨機排列然后選數,所以它們的期望值是可以直接加起來的。

\(dp(l,r)\) 表示僅考慮區間 \((l,r)\) 並且選擇了 \(l\)\(r\) 答案的期望,設可選的數為 \(s_1,\dots,s_k\) ,那么轉移就是:

\[dp(l,r)=1+\frac{1}{k}\sum_{i=1}^k(dp(l,s_i)+dp(s_i,r)) \]

考慮一個數 \(s\) 可選當且僅當 \(l< s< r,a_l< a_s< a_r\) ,顯然可以使用數據結構優化,時間復雜度 \(\mathcal O(n^2\log_2n)\)

參考代碼

#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=2005,mod=1000000007;int n;
int mo(const int x){
	return x>=mod?x-mod:x;
}
void change(int*tr,int pos,int val){
	while(pos<=n){
		tr[pos]=mo(tr[pos]+val);
		pos+=pos&(-pos);
	}
}
int query(int*tr,int pos){
	int re=0;
	while(pos){
		re=mo(re+tr[pos]);
		pos^=pos&(-pos);
	}
	return re;
}
int tr0[maxn][maxn],tr1[maxn][maxn],sum[maxn][maxn],dp[maxn],a[maxn],inv[maxn];
int main(){
	read(n);++n;
	for(int i=2;i<=n;++i)read(a[i]),++a[i];
	a[1]=1;++n;a[n]=n;inv[1]=1;
	for(int i=2;i<=n;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
	for(int len=1;len<n;++len){
		for(int l=1,r=l+len;r<=n;++l,++r){
			if(a[r]<a[l])continue;int cnt=query(sum[l],a[r]);
			if(cnt>0){
				dp[l]=mo(1+1ll*mo(query(tr0[l],a[r])+query(tr1[r],n-a[l]+1))*inv[cnt]%mod);
				change(tr0[l],a[r],dp[l]);change(tr1[r],n-a[l]+1,dp[l]);
			}
			change(sum[l],a[r],1);
		}
	}
	write(dp[1]),pc('\n');
	return 0;
}

F Paint Tree

題意簡述

給定一棵 \(n\) 個點的數,每個點可能染成黑色也可能染成白色,定義一種染色方案的權值為 所有黑色點中距離最遠的兩個點的距離 和 所有白色點中距離最遠的兩個點的距離 的最大值,詢問所有可能染色方案的權值之和。

\(2\le n\le 2\times 10^5\)

題目分析

答案等於枚舉 \(i\ge 1\) ,然后最遠距離大於等於 \(i\) 的方案數之和,大於等於不太好求,考慮求小於等於。

首先找到直徑,設直徑的兩個端點為 \(S\)\(T\) ,如果 \(S\)\(T\) 顏色相同,那么最遠距離就是 \(S\)\(T\) 的距離,假設 \(S\)\(T\) 顏色不同, \(S\)\(T\) 黑,那么白色點的所有最遠距離中一定存在一個最遠距離的端點是 \(S\) ,如果需要滿足最遠距離小於等於 \(i\) ,那么所有白色點到 \(S\) 的距離就要小於等於 \(i\) ,同理所有黑色點到 \(T\) 的距離也要小於等於 \(i\) ,如果一個點 到 \(S\) 的距離 和 到 \(T\) 的距離 都小於等於 \(i\) ,那么這個點可黑可白,否則這個點顏色確定,如果一個點 到 \(S\) 的距離 並且 到 \(T\) 的距離 都大於 \(i\) ,說明這種情況根本不存在。

求有多少點可黑可白只需要 dfs 后一遍前綴和就行了,時間復雜度是 \(\mathcal O(n)\) 的。

參考代碼

#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=200005,mod=1000000007;
int mo(const int x){
	return x>=mod?x-mod:x;
}
struct Edge{
	int v,nt;
	Edge(int v=0,int nt=0):
		v(v),nt(nt){}
}e[maxn*2];
int hd[maxn],num;
void qwq(int u,int v){
	e[++num]=Edge(v,hd[u]),hd[u]=num;
}
int dp[maxn];
int dfs(int u,int fa){
	int re=u;dp[u]=dp[fa]+1;
	for(int i=hd[u];i;i=e[i].nt){
		int v=e[i].v;
		if(v==fa)continue;
		int te=dfs(v,u);
		if(dp[te]>dp[re])
			re=te;
	}
	return re;
}
int cnt[maxn],_2[maxn],ds[maxn],dt[maxn];
int main(){
	int n;read(n);_2[0]=1;
	for(int i=1;i<=n;++i)_2[i]=mo(_2[i-1]<<1);
	for(int i=1;i<n;++i){
		int u,v;
		read(u),read(v);
		qwq(u,v),qwq(v,u);
	}
	int S=dfs(1,0),T=dfs(S,0),n2=1;
	for(int i=1;i<=n;++i)ds[i]=dp[i]-1;dfs(T,0);
	for(int i=1;i<=n;++i)dt[i]=dp[i]-1;int mx=0;
	for(int i=1;i<=n;++i)++cnt[max(ds[i],dt[i])],mx=max(mx,min(ds[i],dt[i]));
	for(int i=1;i<n;++i)cnt[i]+=cnt[i-1];int ans=0;
	for(int i=0;i<mx;++i)ans=mo(ans+_2[n]);
	for(int i=mx;i<dt[S];++i)ans=mo(ans+mo(mod-mo(_2[cnt[i]]<<1)+_2[n]));
	write(ans),pc('\n');
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM