1. 程式人生 > 實用技巧 >「考試」聯賽模擬40-45,晚間小測4-9

「考試」聯賽模擬40-45,晚間小測4-9

40.3 MC

刪邊是不好維護的,考慮離線然後倒序處理,然後就轉化成了加邊,修改與詢問,那麼用並查集記錄聯通塊並\(map\)啟發式合併即可
主要程式碼:

inline void merge(ll u,ll v){
	u=fd(u),v=fd(v);
	if(u==v)return;
	if(mp[u].size()>mp[v].size())swap(u,v);
	f[u]=v; cnt[v]+=cnt[u];
	for(auto i:mp[u])mp[v][i.first]+=i.second;
	mp[u].clear();
	return;	
}

(這是開\(c++11\)時的做法,否則需要老實寫\(iterator\)

40.4 簡單題

怎麼這麼多“簡單題”啊
發現其實就是要一棵最小生成樹,考慮\(kruskal\)的過程,那麼加邊時如果有邊加不進去了,那麼以這條邊兩個端點為端點的鏈上所有邊的答案都對這條邊取\(min\),而這條邊的答案就是這條鏈上的最大值
需要樹鏈的區間修改,查\(max\)操作,樹鏈剖分即可

41.1 四個質數的和

首先\(1e5\)以內只有大約一萬個質數,但\(n^4\)列舉肯定不行廢話
選出質數的數量只有4個,可以考慮列舉每兩個質數的和記錄方案數,\(4=2+2\),那麼再列舉所有的兩個質數的和,用詢問減去它,再查,再相乘即可
因為可以將和大於詢問的情況剪掉,所以預處理實際跑不滿\(O(n^2)\)

,就可以通過了

41.2 匹配最大異或

一個非常優美的性質:把\(0\)~\(2^n-1\)\(2^{n-1}\)處分成兩部分,那麼去掉所有數的二進位制最高位,剩下兩部分是完全相同的
考慮當前正在決定的一位應該放0還是1,

  • 如果左右相同的位置數相同,那麼是可以互換的,這一位可以選0/1有兩種
  • 如果左右的取值集合沒有相交,那麼左邊為了最大必須選1,右邊為了最大必須選0,這一位只有一種選擇方案,那麼直接繼續分治即可
  • 如果左右取值集合有相交且不是第一種情況,那麼一個數肯定無法做到在左右兩邊都是最大的,這時答案為0
因為自己也看不懂自己在說些什麼所以放了程式碼:


#include<bits/stdc++.h>
using namespace std;
#define ll int
#define S (1<<16)
#define mod 1000000007

ll n,m;
ll p[S],vis[S];

inline ll sol(ll l,ll r){
	if(l==r)return 1;
	ll flag=1,m=(l+r)>>1;
	for(int i=l;i<=m;i++)if(p[i]!=p[m+i-l+1])flag=0;
	if(flag)return 1ll*sol(l,m)*2%mod;
	memset(vis,0,sizeof vis);
	for(int i=l;i<=m;i++)vis[p[i]]=1;
	for(int i=m+1;i<=r;i++)if(vis[p[i]])return 0;
	return 1ll*sol(l,m)*sol(m+1,r)%mod;
}

inline ll read(){
	ll f=0,s=0; char c=getchar();
	while(c>'9'||c<'0')f=(c=='-'),c=getchar();
	while(c>='0'&&c<='9')s=(s<<3)+(s<<1)+(c^'0'),c=getchar();
	return f?-s:s;
}

int main(){
	freopen("match.in","r",stdin);
	freopen("match.out","w",stdout);
	m=read(); n=read();
	for(int i=0;i<(1<<m);i++){
		p[i]=read();
	}
	printf("%d",sol(0,(1<<m)-1));
	return 0;
}

41.3 染色相鄰的邊

發現一條被修改過的邊為黑色的充要條件即為它兩個端點最後被修改過的時間不同
因為這樣判斷是否被修改過很麻煩,又因為開始是全為黑色,所以開始時可以直接給每一個點賦不同的時間戳
那麼要看有多少黑邊就是看有多少兩個端點時間戳不同的邊,也就是看樹鏈上有多少顏色(時間戳)段再-1
就轉化成了[SDOI2011]染色
調樹剖真有意思

42.4 58號元素

首先可以單調棧處理出所有位置的\(next\),那麼可以建出一棵樹,如果加入一個數,那麼它的子樹裡答案都+1,刪除時即為-1
那麼在新樹上處理出dfs序就可以用線段樹維護了

43.1 糖果機器

首先如果一個機器人可以從\(i\)\(j\),有\(t_j-t_i\geq |s_j-s_i|\)
即:

\[\begin{cases} t_j-t_i\geq s_j-s_i\\ t_j-t_i\geq s_i-s_j \end{cases}\]

可轉化為

\[\begin{cases} t_j-s_j\geq t_i-s_i\\ t_j+s_j\geq t_i+s_i \end{cases}\]

換元可得:

\[\begin{cases} X_j\geq X_i\\ Y_j\geq Y_i \end{cases}\]

就是一個二維偏序,其實和導彈攔截是同理的,需要求一個最長反鏈,那麼按一維排序後樹狀陣列即可
關於方案:每個位置只要貪心選取上一個能轉移的即可