zjoi2015d1题解
闲来无事做了丽洁姐姐的题
t1给一棵树 每个点有点权 每次修改点权 修改后询问每个点到树的带权重心的带权距离是多少 每个点度数不超过20
很显然的一个点分树。。。
我们记一下
每个点的子树中的所有点到该点的带权距离。
每个点的子树中的所有点到该点的父亲的带权距离。
每个点的子树中的所有点的权值和。
大概就可以随便做了(当然说起来简单实际上写的又臭又长。。。)
转移的时候从分治重心开始往分治子树走看哪个更优
#include<bits/stdc++.h> #define LL long long const int maxn = 400010; using namespace std; int m,n; int first[maxn],to[maxn],next[maxn],val[maxn],cnt; inline void add(int u,int v,int w) { to[++cnt]=v; val[cnt]=w; next[cnt]=first[u]; first[u]=cnt; } int dep[maxn],dis[maxn],fat[maxn][22],s[maxn]; int id[maxn],ip[maxn],top; void dfs(int u,int fa) { s[++top]=u; if(!id[u])id[u]=top; dep[top]=dep[ip[fa]]+1;ip[u]=top; for(int i=first[u];i;i=next[i]) { int v=to[i]; if(v==fa)continue; dis[v]=dis[u]+val[i]; dfs(v,u);s[++top]=u;dep[top]=dep[ip[fa]+1]; } } void make() { for(int i=1;i<=top;i++) fat[i][0]=i; for(int j=1;j<=18;j++) for(int i=1;i<=top;i++) if(i+(1<<j)-1<=top) { int x=fat[i][j-1],y=fat[i+(1<<j-1)][j-1]; if(dep[fat[i][j-1]]<dep[fat[i+(1<<j-1)][j-1]]) fat[i][j]=fat[i][j-1]; else fat[i][j]=fat[i+(1<<j-1)][j-1]; } } inline int query(int l,int r) { int len=r-l+1,k=0; for(k=0;1<<k+1<=len;k++); if(dep[fat[l][k]]<dep[fat[r-(1<<k)+1][k]])return fat[l][k]; else return fat[r-(1<<k)+1][k]; } inline int lca(int u,int v) { if(id[u]>id[v]) swap(u,v); return s[query(id[u],id[v])]; } inline int caldis(int u,int v) { int LCA=lca(u,v); return dis[u]+dis[v]-2*dis[LCA]; } int rt,sum,f[maxn],size[maxn],vis[maxn]; inline void GetRT(int x,int fa) { size[x]=1;f[x]=0; for(int i=first[x];i;i=next[i]) { if(to[i]==fa || vis[to[i]])continue; GetRT(to[i],x);size[x]+=size[to[i]]; f[x]=max(f[x],size[to[i]]); } f[x]=max(f[x],sum-size[x]); if(f[x]<f[rt])rt=x; } int ret,dv[maxn],par[maxn]; LL ans[maxn],anss[maxn],summ[maxn]; inline void work(int x) { vis[x]=1;summ[x]=ret; for(int i=first[x];i;i=next[i]) { if(vis[to[i]])continue; rt=0,sum=size[to[i]]; GetRT(to[i],0); par[rt]=x;work(rt); } } LL cal(int u) { LL ret=ans[u]; for(int i=u;par[i];i=par[i]) { LL delt=caldis(par[i],u); ret+=(ans[par[i]]-anss[i]); ret+=delt*(summ[par[i]]-summ[i]); } return ret; } LL update(int u,int va) { summ[u]+=va; for(int i=u;par[i];i=par[i]) { LL di=caldis(par[i],u); summ[par[i]]+=va; anss[i]+=va*di; ans[par[i]]+=va*di; } } int last=1; LL query(int u) { LL ka=cal(u); for(int i=first[u];i;i=next[i]) { LL tmp=cal(to[i]); if(tmp < ka)return query(to[i]); } last=u; return ka; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w),add(v,u,w); }top=0,dfs(1,0); //cout<<top<<endl; make();sum=f[0]=n;GetRT(1,0); work(rt); for(int i=1;i<=m;i++) { int a,b; scanf("%d%d",&a,&b); update(a,b); printf("%lld\n",query(last)); } }
t2图上每个边是0~1的随机实数 求最小生成树上最大边的期望 n<=10
clj:我们可以积分啊
进一步分析可以注意到,考虑一个x,如果<x的边合起来不能使得图联通,<=x的边合起来能够使得图联通,那么这个图的最小瓶颈生成上的最大边就是x。 那么,用WC讲过的同样的方法,我们可以得到一个多项式P(x),表示<x的边不能使得图联通的概率。 那么注意到,我们只需要对P(x)从0到1求积分就是答案了。为什么呢?因为P(x)也是答案>x的概率,这样相当于一个分部积分。
然后积了两个多小时的分...
后来觉得可以考虑状压dp
可以点这个链接去看一下状压dp做法(白积了半天分QAQ
http://blog.csdn.net/skywalkert/article/details/47792065
#include <cstdio> const int maxn = 11, maxm = 46; int n, m, e[maxn], sz[1 << maxn], cnt[1 << maxn]; long long c[maxm][maxm], f[1 << maxn][maxm], g[1 << maxn][maxm]; double ans; int main() { scanf("%d%d", &n, &m); for(int i = 0; i < m; ++i) { int u, v; scanf("%d%d", &u, &v); --u; --v; e[u] |= 1 << v; e[v] |= 1 << u; } c[0][0] = 1; for(int i = 1; i <= m; ++i) { c[i][0] = c[i][i] = 1; for(int j = 1; j < i; ++j) c[i][j] = c[i - 1][j - 1] + c[i - 1][j]; } for(int s = 1; s < 1 << n; ++s) { sz[s] = sz[s >> 1] + (s & 1); if(sz[s] == 1) { g[s][0] = 1; continue; } for(int i = 0; i < n; ++i) if((s >> i) & 1) cnt[s] += sz[e[i] & s]; cnt[s] >>= 1; int lowbit = s & -s; for(int t = (s - 1) & s; t; t = (t - 1) & s) if(t & lowbit) for(int i = 0; i <= cnt[t]; ++i) for(int j = 0; j <= cnt[s ^ t]; ++j) f[s][i + j] += g[t][i] * c[cnt[s ^ t]][j]; for(int i = 0; i <= cnt[s]; ++i) g[s][i] = c[cnt[s]][i] - f[s][i]; } for(int i = 0; i <= m; ++i) ans += (double)f[(1 << n) - 1][i] / c[cnt[(1 << n) - 1]][i]; ans /= m + 1; printf("%.6f\n", ans); return 0; }
t3题意不可描述...bzoj3926
广义后缀自动机 解法不是很好描述 可以看代码
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn = 4000005; int n,c; int first[maxn],next[maxn],to[maxn],val[maxn],cnt; int rd[maxn]; ll ans; inline void add(int u,int v) { to[++cnt]=v; next[cnt]=first[u]; first[u]=cnt; to[++cnt]=u; next[cnt]=first[v]; first[v]=cnt; } struct SAM { int cnt; int fa[maxn],mx[maxn],a[maxn][20]; SAM(){cnt=1;} inline int extend(int p,int c) { int np=++cnt;mx[np]=mx[p]+1; while(!a[p][c]&&p)a[p][c]=np,p=fa[p]; if(!p)fa[np]=1; else { int q=a[p][c]; if(mx[p]+1==mx[q])fa[np]=q; else { int nq=++cnt;mx[nq]=mx[p]+1; memcpy(a[nq],a[q],sizeof(a[q])); fa[nq]=fa[q]; fa[np]=fa[q]=nq; while(a[p][c]==q)a[p][c]=nq,p=fa[p]; } } return np; } inline void calc(){for(int i=1;i<=cnt;i++)ans+=mx[i]-mx[fa[i]];} }sam; inline void dfs(int u,int fa,int curpos) { int nexpos = sam.extend(curpos,val[u]); for(int i=first[u];i;i=next[i]) if(to[i] != fa)dfs(to[i],u,nexpos); } int main() { scanf("%d%d",&n,&c); for(int i=1;i<=n;i++)scanf("%d",&val[i]); for(int i=1;i<n;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v);rd[u]++,rd[v]++; } for(int i=1;i<=n;i++) if(rd[i] == 1)dfs(i,0,1); sam.calc(); printf("%lld",ans); }