[CF1556H]DIY Tree
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CF1556H]DIY Tree相关的知识,希望对你有一定的参考价值。
DIY Tree
题解
卡退火的概率就很离谱
首先,官方题解给的是一种拟阵交的做法,然而某蒟蒻看半天都没能看懂,于是就去尝试了一个退火的做法。
好吧,既然是退火,我们想想我们怎样才能随机改变图。
显然,在确定了与前
k
k
k个点相关的边后,我们的连边方法是固定的,都是按照最小生成树的方法来连边,而确定的点由于有度数限制,需要单独讨论。
所以我们需要做的是随机改变前
k
k
k个点与其他点间的连边状态。
我们可以通过一定概率删边,再通过一定概率加边,以获得新的连边状态。
但事实上我们删完边与加完边后得到的图不一定可以变成一棵生成树,如果我们有一个联通块里没有编号大于
k
k
k的点都没有,那这肯定不能变成一棵生成树,因为怎么连边都会有个块单出来,我们建生成树的过程中只能在编号大于
k
k
k的块之间连边。
所以对于这种情况我们可能需要特别调整一下,使得块内至少包含一个编号大于
k
k
k的点。
而我们的退火相当于就是根据温度设定概率不断的加边删边构建新图,然而再根据
e
x
p
exp
exp值往这边跳。
很显然,当最后稳定下来得到的答案就是我们的答案。
虽然由于一些概率的问题,可能需要跑很多次,其实初始温度设为
5000
5000
5000差不多久够了,多跑几次。
但离谱的是如果加边删边的概率通过当前温度压缩到
[
0
,
1
]
[0,1]
[0,1]之间反而会过不了,但如果直接将温度传进去当概率却可以过,然而温度大部分时间都是大于
1
1
1的呀。
时间复杂度
O
(
n
2
α
)
O\\left(n^2\\alpha\\right)
O(n2α)。
然而如果你把枚举次数设太低了的话可能会卡
W
A
WA
WA很久,退火系数也进量高点,退得慢一点,反正
6
s
6s
6s也不至于
T
T
T。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 2505
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=998244353;
const int inv2=499122177;
const double jzm=0.9995;
const int zero=5000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-3;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,k,d[55],mp[55][55],t[55],fa[55],tota,totb,deg[55],summ,ans,ord[55][55],sta[55],stak;
bool cho[55][55],tmp[55][55],ap[55][55],fg[55];
struct edge{int u,v,w;}a[MAXN],b[MAXN];
bool cmp(edge x,edge y){return x.w<y.w;}
bool cmp1(int x,int y){return t[x]<t[y];}
double Rand(){return 1.0*(rand()*RAND_MAX+rand())/(RAND_MAX*RAND_MAX);}
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
void unionSet(int a,int b){int u=findSet(a),v=findSet(b);if(u!=v)fa[u]=v;}
void work(int x){
for(int i=1;i<=n;i++){
if(ord[x][i]<=k&&!fg[findSet(ord[x][i])])continue;
if(ord[x][i]<=k&°[ord[x][i]]==d[ord[x][i]])continue;
unionSet(ord[x][i],x);tmp[min(ord[x][i],x)][max(ord[x][i],x)]=1;
deg[ord[x][i]]++;deg[x]++;break;
}
}
void sakura(double tp){
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)tmp[i][j]=0;
for(int i=1;i<=n;i++)deg[i]=fg[i]=0;summ=0;makeSet(n);int tim=0;
for(int i=1;i<=tota;i++){
int u=a[i].u,v=a[i].v,w=a[i].w;
if(!cho[u][v])continue;
if(Rand()<tp){tmp[u][v]=0;continue;}
tmp[u][v]=1;deg[u]++;deg[v]++;unionSet(u,v);tim++;
}
random_shuffle(a+1,a+tota+1);
for(int i=1;i<=tota;i++){
int u=a[i].u,v=a[i].v,w=a[i].w;
if(tmp[u][v]||findSet(u)==findSet(v))continue;
if(Rand()>tp||deg[u]==d[u]||deg[v]==d[v])continue;
tmp[u][v]=1;deg[u]++;deg[v]++;unionSet(u,v);tim++;
}
for(int i=k+1;i<=n;i++)fg[findSet(i)]=1;tim=0;
for(int i=1;i<=k;i++){
if(fg[findSet(i)])continue;int p=findSet(i),spc=0;
for(int j=1;j<=k;j++)if(findSet(j)==p&°[j]<d[j])spc=j;
if(!spc){
stak=0;for(int j=1;j<=k;j++)if(findSet(j)==p)sta[++stak]=j;
for(int j=1;j<=stak;j++){
int u=sta[j];fa[u]=u;deg[u]=0;
for(int l=1;l<=k;l++)tmp[l][u]=tmp[u][l]=0;
}
for(int j=1;j<=stak;j++)work(sta[j]),fg[findSet(sta[j])]=1;
}
else work(spc),fg[findSet(spc)]=1;
}
for(int i=1;i<=totb;i++)
if(findSet(b[i].u)!=findSet(b[i].v))
unionSet(b[i].u,b[i].v),deg[b[i].u]++,deg[b[i].v]++,tmp[b[i].u][b[i].v]=1;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(tmp[i][j])summ+=mp[i][j],tim++;
}
void nagisa(){
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)cho[i][j]=ap[i][j];
summ=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
if(cho[i][j])summ+=mp[i][j];
for(int t=1;t<=500;t++){
int now=summ;sakura(0.5);
if(summ<now)
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
cho[i][j]=tmp[i][j];
else summ=now;
}
if(ans>summ){
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
ap[i][j]=cho[i][j];
ans=summ;
以上是关于[CF1556H]DIY Tree的主要内容,如果未能解决你的问题,请参考以下文章