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));  
    }
}
View Code

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;
}
状压dp

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);
}
View Code

 

posted @ 2018-02-19 17:06  探险家Mr.H  阅读(200)  评论(0编辑  收藏  举报