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;
}