1. 程式人生 > >uoj388 【UNR #3】配對樹

uoj388 【UNR #3】配對樹

git efi digi 長度 簡單 get char struct 分享圖片

link

題意:

給定一棵n個點的樹,每條邊有權值,樹上兩點路徑長度定義為邊權和。給定一個元素在[1,n]的長為m的序列,求出對於每個長為偶數的區間,區間中的數字兩兩匹配後每對點的路徑長度之和最小值。輸出所有長為偶數區間的這個最小值之和。

$n,m\leq 10^5.$

題解:

轉化很巧妙。

直接算很不好算,考慮計算每條邊的貢獻。一個性質是:假定在區間中的元素集合為S,對於某一條邊分成的兩個子樹,如果兩個子樹中出現在S中的元素個數均為奇數,則這條邊有1的貢獻,否則沒有貢獻。

證明很簡單,對於都為偶數的情況考慮反證,如果存在路徑經過這條邊,則一定至少兩(偶數)條,那麽可以把這兩條路徑都刪去這條邊得到更優解。對於都為奇數的情況,一定至少存在一條經過這條邊的路徑,去掉這條路徑後則轉化為了偶數的情況。證畢。

那麽原題轉化為:對於每個子樹,如果將子樹中的元素在序列中標記為1,那麽要求的就是這個01串中有多少長為偶數的區間內1的個數為奇數。

暴力算是$\mathcal{O}(nm)$的。我們考慮用線段樹維護01串,記錄區間內1的個數,區間內位置為奇/偶,前綴和mod2為奇/偶的下標數量。線段樹合並即可。復雜度$\mathcal{O}(n\log m)$。

code:

技術分享圖片
 1 #include<bits/stdc++.h>
 2 #define rep(i,x,y) for (int i=(x);i<=(y);i++)
 3 #define ll long long
 4 #define
inf 1000000001 5 #define y1 y1___ 6 using namespace std; 7 ll read(){ 8 char ch=getchar();ll x=0;int op=1; 9 for (;!isdigit(ch);ch=getchar()) if (ch==-) op=-1; 10 for (;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-0; 11 return x*op; 12 } 13 #define N 100005 14 #define M 2000005 15 #define
mod 998244353 16 int n,m,cnt,tot,ans,head[N],rt[N],ls[M],rs[M],sum[M],a[M][2][2]; 17 struct edge{int to,nxt,v;}e[N<<1]; 18 void adde(int x,int y,int z){ 19 e[++cnt].to=y;e[cnt].nxt=head[x];head[x]=cnt; 20 e[cnt].v=z; 21 } 22 void up(int k,int l,int r){ 23 sum[k]=0; 24 if (ls[k]) sum[k]+=sum[ls[k]]; 25 if (rs[k]) sum[k]+=sum[rs[k]]; 26 int x=ls[k]?sum[ls[k]]&1:0; 27 rep (i,0,1) rep (j,0,1){ 28 a[k][i][j]=0; 29 if (ls[k]) a[k][i][j]+=a[ls[k]][i][j]; 30 if (rs[k]) a[k][i][j]+=a[rs[k]][i^x][j]; 31 } 32 int mid=l+r>>1;//註意這兩句別忘 33 if (!ls[k]) a[k][0][0]+=mid/2-(l-1)/2,a[k][0][1]+=(mid+1)/2-l/2; 34 if (!rs[k]) a[k][x][0]+=r/2-mid/2,a[k][x][1]+=(r+1)/2-(mid+1)/2; 35 } 36 void ins(int &k,int l,int r,int x){ 37 if (!k){//註意賦初始值 38 k=++tot; 39 a[k][0][0]=r/2-(l-1)/2; 40 a[k][0][1]=(r+1)/2-l/2; 41 } 42 if (l==r){sum[k]++;return;} 43 int mid=l+r>>1; 44 if (x<=mid) ins(ls[k],l,mid,x);else ins(rs[k],mid+1,r,x); 45 up(k,l,r); 46 } 47 int merge(int x,int y,int l,int r){ 48 if (!x||!y) return x|y; 49 int mid=l+r>>1; 50 ls[x]=merge(ls[x],ls[y],l,mid); 51 rs[x]=merge(rs[x],rs[y],mid+1,r); 52 up(x,l,r); 53 return x; 54 } 55 void upd(int &x,int y){x+=y;x-=x>=mod?mod:0;} 56 void dfs(int u,int pr){ 57 for (int i=head[u];i;i=e[i].nxt) if (e[i].to!=pr){ 58 int v=e[i].to; 59 dfs(v,u); 60 upd(ans,((ll)a[rt[v]][0][0]*a[rt[v]][1][0]%mod+(ll)a[rt[v]][0][1]*a[rt[v]][1][1]%mod)%mod*e[i].v%mod); 61 rt[u]=merge(rt[u],rt[v],1,m+1); 62 } 63 } 64 int main(){ 65 n=read(),m=read(); 66 rep (i,1,n-1){ 67 int x=read(),y=read(),z=read(); 68 adde(x,y,z);adde(y,x,z); 69 } 70 rep (i,1,m) ins(rt[read()],1,m+1,i); 71 dfs(1,0); 72 cout<<ans<<\n; 73 return 0; 74 }
View Code

易錯:

註意初始情況不是0,需要處理。

uoj388 【UNR #3】配對樹