E.Forsaken的數列(
數據結構題(splay,無旋treap)
F.Forsaken的位運算魔法(
類歐幾里得算法,
H.Forsaken喜歡獨一無二的樹
題意:
給定n個點m條邊的無向圖,問需要刪掉多少條邊使得最小生成樹的權值和不變且唯一(就是把額外的都刪掉),計算出刪掉的邊的邊權和。
思路:
最小生成樹統計衝突邊。
考慮問題形式我們可以轉換成刪掉所有可以代替最小生成樹種某條的邊的所有邊權和。
一種做法是先找出一顆最小生成樹,然後枚舉每條不在樹上的邊,利用RMQ判斷兩端路徑上的最大邊是否大於這條邊。
複雜度O(mlogn + mlogm)
一個好的做法就是在構造最小生成樹的時候直接計算每條可能加進最小生成樹的邊權和(邊權相同的邊)。
然後最後減掉最小 生成樹的邊權和就是答案。
複雜度是O(mlogm)。
ps:
在邊權各不相同的時候最小生成樹是唯一的,只有在一些邊的邊權相同的時候最小生成樹纔有可能不唯一。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=2e5+5;
struct Node{
int a,b,c;
}e[maxm];
int pre[maxm];
bool cmp(Node a,Node b){
return a.c<b.c;
}
int ffind(int x){
return pre[x]==x?x:pre[x]=ffind(pre[x]);
}
signed main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)pre[i]=i;
for(int i=1;i<=m;i++)cin>>e[i].a>>e[i].b>>e[i].c;
sort(e+1,e+1+m,cmp);
int ans=0;
for(int i=1;i<=m;i++){
int k=i;
while(k<=m&&e[k].c==e[i].c)k++;
for(int j=i;j<k;j++){
int x=ffind(e[j].a);
int y=ffind(e[j].b);
if(x!=y)ans+=e[j].c;//加上所有權值相同且可能的邊
}
for(int j=i;j<k;j++){
int x=ffind(e[j].a);
int y=ffind(e[j].b);
if(x!=y)pre[x]=y,ans-=e[j].c;//減掉最小生成樹的樹邊
}
i=k-1;
}
cout<<ans<<endl;
return 0;
}
I.Forsaken遇到了毒瘤(
J.Forsaken喜歡玩自走棋
題意:
n<=1e5,s<=1e5 -1e9<=v<=1e9
思路:
根據題目和數據範圍很容易想到狀壓dp
有個坑點是這題是小的羈絆會包含大的羈絆,容易誤解。
第一次聽到SoSdp(子集和dp)
典型的SOSDp問題,直接反着寫SOSDp的轉移就行了
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=(1<<21)+5;
int s[maxm];
int v[maxm];
int d[maxm];
signed main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>s[i]>>v[i];
d[s[i]]+=v[i];
}
for(int i=0;i<20;i++){
for(int j=0;j<(1<<20);j++){//
if(j>>i&1){
d[j^(1<<i)]+=d[j];//
}
}
}
int ans=0;
int ma=-1e18;
for(int i=0;i<(1<<20);i++){
if(d[i]>ma){
ma=d[i];
ans=i;
}
}
cout<<ans<<' '<<ma<<endl;
return 0;
}