• 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
    ",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
    ", 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
  • 相关阅读:
    几款网络测试工具总结
    Linux安装telnet
    Linux下iptables 禁止端口和开放端口
    mysql创建某个数据库中的某张表 只读用户
    查看nginx版本号的几种方法
    Ngxtop-Nginx日志实时分析利器
    Nginx监控运维
    oracle经典书籍推荐
    华为典型局域网组网案例介绍(1)
    技术说明 路由器是如何工作的呢? 一个简单的解释
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/8453958.html
Copyright © 2020-2023  润新知