每個人渴望與乙個人當同桌。
容易發現這個關係形成內向基環樹森林。
問題轉化為求基環樹森林的最大匹配。
任意選一條環上的邊,分別嘗試該邊為匹配邊、非匹配邊即可。
乙個常用的但想不到的東西:將每種顏色出現次數的差值為定值,轉化為對顏色序列差分後相等。
然後暴力的做法是列舉2^8,表示答案出現在指定的顏色集合中,分別將差分陣列插入、查詢雜湊表即可。
下面是乙個優化:
容易發現可行的顏色集合對於同乙個時刻只有不超過8種。
對於每乙個左端點,隨著右端點的右移,區間顏色集合改變次數不超過8次。
當右端點右移時,給區間顏色集合需要改變的,新增新的一種顏色。
具體實現方式是維護乙個$lst$陣列,表示第$i$種顏色的最後一次出現位置。
可以參考**。
1 #include2t2剛開始想的是只需要判定是否為二分圖,然後想用並查集來維護這個東西,然後發現有些東西沒法搞。#define ull unsigned long long
3using
namespace
std;
4const
int n=1e5+7;5
intn,k;
6 pair f[n],t[10];7
struct
hash
17}mp;
18int lst[10],cnt[10
][n];
19 ull pw[10
];20 inline int calc(int s,int
t)25 inline ull solve(int x,int
s)28
for(int i=fir+1;i<=8;++i) if(s>>i-1&1) r=r*133331+cnt[i][x]-cnt[fir][x];
29return r+s*pw[8
];30
}31 inline int read(register int x=0,register char ch=getchar(),register int f=0)36
intmain()
49 lst[f[i].second]=i;
50for(int j=1;j<=8;++j) t[j]=make_pair(lst[j],j);
51 sort(t+1,t+9); reverse(t+1,t+9
);52
for(int j=1,now=0;j<=8&&t[j].first;++j)
56 mp[0]=f[i+1
].first;57}
58 printf("
%d\n
",ans);
59return0;
60 }
考慮列舉答案$da$,$db$,滿足$da對於邊權$w<=da$,可以忽略。
對於邊權$w>db$,要將所連的點放入不同的集合。
對於邊權$da並查集維護前兩種情況是簡單的,但是對於第三種就偽了。
注意第三種情況可以轉化為$u,v$至少乙個在$b$中,這一步是考試時沒想到的。
於是問題可以用2-sat解決,定義選$b$為$1$,那麼情況二對應著$u$ $xor$ $v=1$,情況三對應著$u$ $or$ $v=1$。
容易發現對於確定的$db$,乙個$da$是否成立具有單調性,於是可以二分判斷。
還有很大的優化空間。
這裡丟擲乙個結論:只需要考慮$db$對應最大生成樹上的邊 和 $a$與$b$分別對應最大生成樹上的黑點和白點。
考慮首先生成最大生成樹並黑白染色。
如果集合$b$中同時含有黑點和白點,那麼如果$db$不為最大生成樹上邊,原圖會形成乙個偶環,這樣環上一定存在一條邊使得這條邊權大於$db$,所以$db$為最大生成樹上的邊。
否則,可以認為集合$b$中只含有黑點,特判這種情況就可以了。
省選模擬13
費用流,拆點,把點按奇偶分類 偶數的直接拆成 frac 奇數的也一樣,然後列舉哪一邊的流量多,再給他加上就行 code include define int long long define rint signed define inf 0x3f3f3f3f3f3f3f3f using namesp...
省選模擬104 題解
a.簽到題 把每個點向它右側比他大的第乙個點之間連邊,如果沒有那麼向 root 連邊。那麼可以構成一棵樹。特判一些情況之後,可以認為問題就是 1.給某節點和它的所有兒子節點權值加上乙個值。2.詢問一條路徑的權值和。首先考慮如果只詢問單點的維護方法,其實就是打乙個標記表示給整個兒子集合都加上了若干權值...
省選模擬102 題解
a.island 對於正負不同的情況,o n 列舉左側的位置然後計算。對於正負性相同的情況,把笛卡爾樹建出來,然後每次考慮跨過最小值的貢獻。分幾種情況 左右均不超過最小值,左右僅有乙個超過最小值,左右都超過最小值。然後順便統計上其中乙個端點為劃分點的貢獻。然後瘋狂的寫式子拆式子就沒了。做法挺簡單的,...