jzoj5442. 【NOIP2017提高A組衝刺11.1】荒誕
阿新 • • 發佈:2021-01-20
Description
一個無向圖,最長的簡單路徑長度不超過10,要求每個點要麼被選,要麼有一個直接相連的點被選,求最小代價
Solution
30pts
暴力
45pts
加上樹形DP
滿分做法
還是考慮DP,由於原圖是無向圖,其 dfs 樹深度不超過10,因此考慮跟樹形DP一樣做
設 f x , s f_{x,s} fx,s 表示已經覆蓋完 尤拉序/dfs序 在x之前的點(x到根路徑上的點除外),x到根路徑上的點狀態為s,最小代價
往下傳時列舉祖先的狀態,分選和不選兩種情況轉移
往上傳時比較簡單
時間複雜度
O
(
n
∗
3
10
)
O(n*3^{10})
O(n∗310),然而狀態數明顯跑不滿,空間上根據深度滾動一下就可以
code
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
const int INF=1e9+7;
using namespace std;
const int N=2e4+10;
const int M=2.5e4+10;
const int K=59050;
int n,m,a[N],f[20][K],depth[N],cnt,q[N*2];
int tot, last[N],mi[20],dis,ans;
bool bz[N];
struct edge{
int st,en,next;
}E[M*2];
void dfs(int x){
q[++cnt]=x;
bz[x]=1;
for(int p=last[x];p;p=E[p].next){
int y=E[p].en;
if(bz[y])continue;
depth[y]=depth[x]+1;
dfs(y);
q[++cnt]=x;
}
}
void add(int x,int y){
E[++tot]=(edge){x,y,last[x]};
last[x] =tot;
}
int main(){
freopen("absurdity.in","r",stdin);
freopen("absurdity.out","w",stdout);
mi[0]=1;
fo(i,1,10)mi[i]=mi[i-1]*3;
scanf("%d%d",&n,&m);
fo(i,1,n)scanf("%d",&a[i]);
fo(i,1,m){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
fo(i,1,n){
if(bz[i])continue;
cnt=0;
dfs(i);
memset(f[0],127,sizeof(f[0]));
dis=f[0][0];
f[0][0]=0;
f[0][2]=a[i];
fo(i,2,cnt){
int x=depth[q[i]];
memset(f[x],127,sizeof(f[x]));
if(x>depth[q[i-1]]){
fo(j,0,mi[x]-1){
if(f[x-1][j]==dis)continue;
int s=j;
bool flag=0;
for(int p=last[q[i]];p;p=E[p].next){
int y=E[p].en;
if(depth[y]>x)continue;
if(j/mi[depth[y]]%3==2)flag=1;
else s=(s/mi[depth[y]+1]*3+1)*mi[depth[y]]+s%mi[depth[y]];
}
if(j/mi[x-1]==2)flag=1;
else s=s%mi[x-1]+mi[x-1];
s+=mi[x]*2;
if(flag)f[x][j+mi[x]]=min(f[x][j+mi[x]],f[x-1][j]);
if(j/mi[x-1]!=2)f[x][j]=min(f[x][j],f[x-1][j]);
f[x][s]=min(f[x][s],f[x-1][j]+a[q[i]]);
}
}else{
fo(j,0,mi[x+1]-1){
f[x][j]=min(f[x+1][j+mi[x+1]],f[x+1][j+2*mi[x+1]]);
}
}
}
ans+=min(f[0][1],f[0][2]);
}
printf("%d\n",ans);
return 0;
}