洛谷4630APIO2018鐵人兩項(圓方樹+dp)
阿新 • • 發佈:2018-12-22
QWQ神仙題啊(據說是今年第一次出現圓方樹的地方)
首先根據題目,我們就是求對於每一個路徑\((s,t)\)他的貢獻就是兩個點之間的點數,但是圖上問題我並沒有辦法很好的解決。。。
這時候考慮圓方樹,我們將圓方樹建出來之後,
我們令方點的權值是他所連線的圓點之和,圓點的權值是\(-1\)。
這裡之所以讓圓點的貢獻是-1,是為了方便表示路徑的貢獻(不然貌似比較複雜)。
如果我們這麼賦值的話,那麼一個條路經的貢獻就應該是點權之和。
QWQ可惜列舉兩個端點是\(O(n^2)\)複雜度的
那麼這時候,我們就可以直接考慮每個點作為中心的貢獻,那麼他的貢獻就應該是:
子樹外到子樹內的貢獻+子樹之間的貢獻。
那麼我們只需要一邊\(dfs\),一邊維護\(size\)並更新\(ans\)就行
void dfs(int x) { vis[x]=1; int tmp=0; if (x<=n) tmp=1; for (int i=point[x];i;i=nxt[i]) { int p = to[i]; if (vis[p]) continue; dfs(p); ans=ans+tmp*size[p]*val[x]; tmp+=size[p]; // cout<<ans<<endl; } ans=ans+size[x]*(sum-size[x])*val[x]; }
不過要注意的是,最後的\(ans\)需要乘2,因為是雙向的
而且圖不一定聯通!!!!!
// luogu-judger-enable-o2 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #include<map> #include<set> #define mk makr_pair #define ll long long #define int long long using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();} return x*f; } const int maxn = 3e5+1e2; const int maxm = 2*maxn; int point[maxn],nxt[maxm],to[maxm]; int point1[maxn],nxt1[maxm],to1[maxm]; int cnt,cnt1; int n,m; int f[maxn],val[maxn],size[maxn],g[maxn]; int vis[maxn]; int top,st[maxn]; int low[maxn],dfn[maxn]; int ans; void addedge(int x,int y) { nxt[++cnt]=point[x]; to[cnt]=y; point[x]=cnt; } void addedge1(int x,int y) { nxt1[++cnt1]=point1[x]; to1[cnt1]=y; point1[x]=cnt1; } int tot,num; void tarjan(int x,int fa) { dfn[x]=low[x]=++tot; st[++top]=x; for (int i=point1[x];i;i=nxt1[i]) { int p = to1[i]; if (p==fa) continue; if (!dfn[p]) { tarjan(p,x); low[x]=min(low[x],low[p]); if (low[p]>=dfn[x]) { ++num; addedge(num,x); addedge(x,num); val[num]++; do{ addedge(st[top],num); addedge(num,st[top]); val[num]++; top--; }while (st[top+1]!=p); } } else low[x]=min(low[x],dfn[p]); } } void dp(int x,int faa) { if (x<=n) size[x]=1; for (int i=point[x];i;i=nxt[i]) { int p = to[i]; if (p==faa) continue; dp(p,x); size[x]+=size[p]; } //cout<<x<<" "<<size[x]<<endl; } int sum; void dfs(int x) { vis[x]=1; int tmp=0; if (x<=n) tmp=1; for (int i=point[x];i;i=nxt[i]) { int p = to[i]; if (vis[p]) continue; dfs(p); ans=ans+tmp*size[p]*val[x]; tmp+=size[p]; // cout<<ans<<endl; } ans=ans+size[x]*(sum-size[x])*val[x]; } signed main() { n=read(),m=read(); num=n; for (int i=1;i<=n;i++) val[i]=-1; for (int i=1;i<=m;i++) { int x=read(),y=read(); addedge1(x,y); addedge1(y,x); } for (int i=1;i<=n;i++) { if(!dfn[i]) tarjan(i,0); } for (int i=1;i<=n;i++) { if(!vis[i]) { dp(i,0); sum=size[i]; dfs(i); } } cout<<ans*2<<endl; return 0; }