一些題

Abnormal123發表於2024-08-24

一些題

模擬賽遇到的trick,有意思的,有啟發的題,不一定很難。

蜀道難

噫籲嚱,危乎高哉!蜀道之難,難於上青天!蠶叢及魚鳧,開國何茫然!爾來四萬八千歲,不與秦塞通人煙。西當太白有鳥道,可以橫絕峨眉巔。地崩山摧壯士死,然後天梯石棧相鉤連。上有六龍回日之高標,下有衝波逆折之回川。黃鶴之飛尚不得過,猿猱欲度愁攀援。青泥何盤盤,百步九折縈巖巒。捫參歷井仰脅息,以手撫膺坐長嘆。

問君西遊何時還?畏途巉巖不可攀。但見悲鳥號古木,雄飛雌從繞林間。又聞子規啼夜月,愁空山。蜀道之難,難於上青天,使人聽此凋朱顏!連峰去天不盈尺,枯松倒掛倚絕壁。飛湍瀑流爭喧豗,砯崖轉石萬壑雷。其險也如此,嗟爾遠道之人胡為乎來哉!

劍閣崢嶸而崔嵬,一夫當關,萬夫莫開。所守或匪親,化為狼與豺。朝避猛虎,夕避長蛇;磨牙吮血,殺人如麻。錦城雖雲樂,不如早還家。蜀道之難,難於上青天,側身西望長諮嗟!

連通塊

有一棵 \(n\) 個節點的樹,他現在要對這棵樹做 \(m\) 次操作,每次操作如下

  1. 刪除樹上的一條邊

  2. 查詢在節點 \(u\) 目前所在的連通塊內,離 \(u\) 最遠的點的距離。兩點之間的距離定義為兩點之間簡單路徑的邊數。

樹的直徑的性質
  1. 直徑的端點一定是樹的葉子節點。

  2. 從樹上任意一節點出發,與它距離最遠的節點一定是直徑的一個端點。

  3. 在樹上增加一個葉子節點,最多改變樹的直徑的一個端點。

  4. 設兩棵樹的直徑端點分別為 $ (u,v) 和 (x,y) $ ,將兩棵樹合併後的直徑端點仍在這四點中。

  5. 如果一棵樹有多條直徑,它們一定交與一個節點,且這個交點是每條直徑的中點。

考慮求樹的直徑:

  1. 貪心。由性質2,任由一點開始求最遠點,兩邊DFS即可。

  2. DP。求該子樹中的最長鏈和次長鏈。

再來看這道題,我們把操作離線,反向操作,就成為合併子樹問題。由性質2可知,我們只需要知道直徑端點就能求出最大距離。再由興致4,很容易維護樹的直徑。

CODE
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+100;
int n,m,a,b,cnt,head[N],fa[N];
bool p[N],vis[N],vs[N];
int dad[N],siz[N],depth[N],son[N],top[N];
int fir[N],sec[N],maxn[N],maxx[N],ans[N];
struct node{
	int x,y,dis;
};
struct edge{
	int to,nxt;
}e[N<<1];
struct query{
	int opt,x;
}q[N];
bool cmp(node i,node j){
	return i.dis>j.dis;
}
inline void add(int u,int v){
	cnt++;
	e[cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
int find(int x){
	if(x==fa[x])return x;
	else return fa[x]=find(fa[x]);
}
void dfs(int u,int f){
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==f)continue;
		if(!p[i>>1]){
			int fx=find(u),fy=find(v);
			if(fx!=fy)fa[fx]=fy;
		}
		dfs(v,u);
	}
}
void dfs1(int u,int f){
	depth[u]=depth[f]+1;
	siz[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==f)continue;
		dad[v]=u;
		dfs1(v,u);
		if(siz[son[u]]<siz[v])son[u]=v;
		siz[u]+=siz[v];
	}
}
void dfs2(int u,int t){
	top[u]=t;
	if(son[u]){
		dfs2(son[u],t);
	}
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==dad[u]||v==son[u])continue;
		dfs2(v,v);
	}
}
int LCA(int x,int y){
	int fx=top[x],fy=top[y];
	while(fx!=fy){
		if(depth[fx]<depth[fy]){
			y=dad[fy];
		}
		else{
			x=dad[fx];
		}
		fy=top[y];fx=top[x];
	}
	if(depth[x]<depth[y])return x;
	else return y;
}
void dfs3(int rt,int u,int f){
	fir[u]=u;
	vis[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		if(p[i>>1])continue;
		int v=e[i].to;
		if(v==f)continue;
		if(vis[v])continue;
		dfs3(rt,v,u);
		if(maxn[v]+1>maxn[u]){
			maxn[u]=maxn[v]+1;
			fir[u]=fir[v];
		}
	}
}
void dfs4(int rt,int u,int f){
	sec[u]=u;
	vs[u]=1;
	for(int i=head[u];i;i=e[i].nxt){
		if(p[i>>1])continue;
		int v=e[i].to;
		if(v==f)continue;
		if(vs[v])continue;
		dfs4(rt,v,u);
		if(maxx[v]+1>maxx[u]){
			maxx[u]=maxx[v]+1;
			sec[u]=sec[v];
		}
	}
}
int Dis(int x,int y){
	return depth[x]+depth[y]-depth[LCA(x,y)]*2;
}
signed main()
{
	freopen("block.in","r",stdin);
	freopen("block.out","w",stdout);
	scanf("%d%d",&n,&m);
	cnt=1;
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
	for(int i=1;i<n;i++){
		scanf("%d%d",&a,&b);
		add(a,b);
		add(b,a);
	}
	for(int i=1;i<=m;i++){
		scanf("%d%d",&q[i].opt,&q[i].x);
		if(q[i].opt==1){
			p[q[i].x]=1;
		}
	}
	dfs(1,0);
	dfs1(1,0);
	dfs2(1,1);
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			int anc=find(i);
			dfs3(anc,anc,0);
			dfs4(anc,fir[anc],0);
			sec[anc]=sec[fir[anc]];
		}
	}
	for(int i=m;i>=1;i--){
		if(q[i].opt==1){
			int x=(e[q[i].x<<1].to),y=(e[q[i].x<<1|1].to);
			int fx=find(x),fy=find(y);
			node d[6];
			d[0]={fir[fx],sec[fx],Dis(fir[fx],sec[fx])};
			d[1]={fir[fx],fir[fy],Dis(fir[fx],fir[fy])};
			d[2]={fir[fx],sec[fy],Dis(fir[fx],sec[fy])};
			d[3]={sec[fx],fir[fy],Dis(sec[fx],fir[fy])};
			d[4]={sec[fx],sec[fy],Dis(sec[fx],sec[fy])};
			d[5]={fir[fy],sec[fy],Dis(fir[fy],sec[fy])};
			int dis=0;
			for(int j=0;j<6;j++){
				if(dis<d[j].dis){
					dis=d[j].dis;
					fir[fy]=d[j].x;
					sec[fy]=d[j].y;
				}
			}
			fa[fx]=fy;
		}
		else{
			int fx=find(q[i].x);
			ans[i]=max(Dis(q[i].x,fir[fx]),Dis(q[i].x,sec[fx]));
		}
	}
	for(int i=1;i<=m;i++){
		if(q[i].opt==2)printf("%d\n",ans[i]);
	}
}

Wallpaper Collection

設 $ dp_{i,j,k} $ 為第 \(i\) 行,所選區間為 $ [j,k] $ 最大價值,轉移時用滾動陣列和一些最佳化,就可做到 $ O(n^3) $ 。

改變DP狀態:我們發現相鄰兩行所選區間必須有交集,想象一個學長在矩陣上走動,他走過的路徑剛好符合以上定義。設 $ dp{i,j} $ 為學長在第 \(i\) 行,第一次走到的是第 \(j\) 格的最大價值,加一些最佳化就可以 $ O(n^2) $ 。

進擊的巨人

一個 $ O(n^2) $ 的做法:對於每一個位置 \(i\) ,列舉 $ j \le i $ ,計算 $ [j,i] $ 都是1的機率,進而可以算出期望。因為賽時資料水,$ n\le 1e4 $ , $ O(n^2) $ 就跑過來了。

設 $ [L,R] $ 都是1,那麼 $ [L-1,R] $ 都沒有被損壞,防禦值為 $ (R-L+1)^k $ 。

二項式定理展開得到:

\[(R-L+1)^k = \sum _ { i=0 } ^k \binom{k}{i} \times R^i \times (-L+1)^{ k-i } \]

記前 \(i\) 個位置?的個數為 \(s_i\) ,位置 \(i\) 的答案為:

\[\sum _{ j=1 }^i \frac{1}{ 2^{ s_i -s_j } } \times \sum _{p=0}^k \binom{k}{p} \times i^p \times (-j+1)^{k-p} \]

\[= \frac{1}{ 2^{s_i} } \times \sum _{p=0}^k \binom{k}{p} \times i^p \times \sum_{j=1}^{i} (-j+1)^{k-p} \times 2^{s_j} \]

發現這個東西極好維護,複雜度 $ O(NK) $ 。

魔卡少女櫻

$ a_i \not\equiv a_{i+1} \pmod 3 $ 。差分的思路顯然, $ a_{i+1}-a_i>0 \ \And a_{i+1}-a_i \not\equiv 0 \pmod 3 $ 。我們先考慮差分結果只有1,2的情況,列舉填 \(k\) 個2, $ n-1-k $ 個1,這裡方案數為:設填2則 \(x_i\) 值為1,填1則 $ x_i $ 值為0,$ \sum x=k $ ,有 $ \dbinom{n+k-2}{n-2} $ 種。

當前 $ a_n= n-1+k $,對於剩下的 $ m-a_n $ 沒有使用,我們可以填進 $ a_1 $ ,也可以三個一組加到差分數上(正確性顯然)。

設填 \(k\) 個2,列舉 $ a_1 $,三個一組加,共有 $ t= \left \lfloor \frac{m-(n-1+k)-a_1} {3} \right \rfloor $ 組 。方案數為 $ \sum_{i=0}^{t} \dbinom{ n+i-2 }{ n-2 } $

最終答案

\[\sum_{ k=0 }^{n-1} \sum_{ a_1=0 }^{ m-n-k+1 } \sum_{ i=0 }^{ m-n-k+1-a_1 } \dbinom{ n+i-2 }{ n-2 } \]

列舉就得到 $ O(n^3) $ 做法,加一堆字首和最佳化就做到 $ O(n) $

《阿房宮賦》

六王畢,四海一;蜀山兀,阿房出。覆壓三百餘里,隔離天日。驪山北構而西折,直走咸陽。二川溶溶,流入宮牆。五步一樓,十步一閣;廊腰縵回,簷牙高啄;各抱地勢,鉤心鬥角。盤盤焉,囷囷焉,蜂房水渦,矗不知其幾千萬落!長橋臥波,未云何龍?複道行空,不霽何虹?高低冥迷,不知西東。歌臺暖響,春光融融;舞殿冷袖,風雨悽悽。一日之內,一宮之間,而氣候不齊。

妃嬪媵嬙,王子皇孫,辭樓下殿,輦來於秦,朝歌夜弦,為秦宮人。明星熒熒,開妝鏡也;綠雲擾擾,梳曉鬟也;渭流漲膩,棄脂水也;煙斜霧橫,焚椒蘭也。雷霆乍驚,宮車過也;轆轆遠聽,杳不知其所之也。一肌一容,盡態極妍,縵立遠視,而望幸焉;有不見者,三十六年。燕、趙之收藏,韓、魏之經營,齊、楚之精英,幾世幾年,剽掠其人,倚疊如山。一旦不能有,輸來其間。鼎鐺玉石,金塊珠礫,棄擲邐迤,秦人視之,亦不甚惜。

嗟乎!一人之心,千萬人之心也。秦愛紛奢,人亦念其家;奈何取之盡錙銖,用之如泥沙?使負棟之柱,多於南畝之農夫;架樑之椽,多於機上之工女;釘頭磷磷,多於在庾之粟粒;瓦縫參差,多於周身之帛縷;直欄橫檻,多於九土之城郭;管絃嘔啞,多於市人之言語。使天下之人,不敢言而敢怒;獨夫之心,日益驕固。戍卒叫,函谷舉;楚人一炬,可憐焦土。

嗚呼!滅六國者,六國也,非秦也。族秦者,秦也,非天下也。嗟乎!使六國各愛其人,則足以拒秦;使秦復愛六國之人,則遞三世可至萬世而為君,誰得而族滅也?秦人不暇自哀,而後人哀之;後人哀之而不鑑之,亦使後人而復哀後人也。

相關文章