zoukankan      html  css  js  c++  java
  • HAOI2015 泛做

    T1

    有一棵点数为N的树,树边有边权。给你一个在0~N之内的正整数K,你要在这棵树中选择K个点,将其染成黑色,并将其他的N-K个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。N,K<=2000

    这个树形dp不是很好想...因为贡献十分混乱...

    题解十分神奇...我们记f[i][j]为i的子树有j个黑点的最大权值。

    注意直接dp十分蛋疼,这个权值指的是,这个子树内部的贡献,以及i与父亲之间的边对答案的贡献(比如这条边对黑点对距离和的贡献就是子树内部的黑点数*子树外部的黑点数*这条边的权值)。

    这个转移就是正常的子树合并...似乎子树合并的题目这个trick十分有用啊...

    #include <iostream>
    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <sstream>
    #include <stack>
    #include <iomanip>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    typedef double ld;
    typedef vector<int> vi;
    #define fi first
    #define se second
    #define fe first
    #define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
    #define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
    #define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
    #define es(x,e) (int e=fst[x];e;e=nxt[e])
    #define cif(x) if(x) continue
    #define VIZ {printf("digraph G{
    "); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;
    ",i,vb[e]); puts("}");}
    #define TIMER cerr<<clock()<<"ms
    "
    #define gmax(a,b) if((a)<(b)) (a)=(b);
    #define gmin(a,b) if((a)>(b)) (a)=(b);
    #define SZ 666666
    Edgc
    int n,k,sz[SZ];
    ll dp[2333][2333],tmp[2333];
    void dfs(int x,int f=0,int fv=0)
    {
        int cs=1;
        for es(x,e)
        {
            int b=vb[e]; cif(b==f);
            dfs(b,x,vc[e]);
            int cc=cs+sz[b];
            for(int i=0;i<=min(cc,k);i++) tmp[i]=0;
            for(int i=0;i<=cs&&i<=k;i++)
            {
                for(int j=0;j<=sz[b]&&j<=k;j++)
                {
                    if(i+j>k) continue;
                    gmax(tmp[i+j],dp[x][i]+dp[b][j]);
                }
            }
            for(int i=0;i<=min(cc,k);i++) dp[x][i]=tmp[i];
            cs=cc;
        }
        sz[x]=cs;
        for(int i=0;i<=cs&&i<=k;i++)
        {
            if(cs+(k-i)>n)
            {
                dp[x][i]=-2147400000; continue;
            }
            //xblack=i xwhite=cs-i
            //wblack=k-i wwhite=n-cs-(k-i)
            dp[x][i]+=(ll)i*(k-i)*fv;
            dp[x][i]+=(ll)(cs-i)*(n-cs-(k-i))*fv;
        }
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<n;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            adde(a,b,c);
        }
        dfs(1);
        cout<<dp[1][k]<<"
    ";
    }

    T2

    链剖裸题就不说了...

    似乎有两遍dfs序的做法写了写没调出来...

    #include <iostream>
    #include <stdio.h>
    #include <stdlib.h>
    using namespace std;
    #define SZ 666666
    typedef long long ll;
    ll a1[210001],a2[210001];
    ll qzh(int r)
    {
        ll s1=0,s2=0;
        for(int i=r;i>=1;i-=i&-i) s1+=a1[i], s2+=a2[i];
        return (r+1)*s1-s2;
    }
    ll sum(int l,int r)
    {
        return qzh(r)-qzh(l-1);
    }
    void edt(ll a,ll s1)
    {
        ll s2=a*s1;
        for(;a<=210000;a+=a&-a) a1[a]+=s1, a2[a]+=s2;
    }
    void edt(int l,int r,ll a) {edt(l,a); edt(r+1,-a);}
    namespace lct
    {
    #define SZ 666666
    int n,S=0,ns[SZ],fs[SZ],ss[SZ],fa[SZ],siz[SZ],ws[SZ],dep[SZ],fe[SZ],top[SZ],X=0,ls[SZ];
    void ad_de(int x,int y)
    {
        ++S; ns[S]=fs[x]; fs[x]=S; ss[S]=y;
    }
    void adde(int x,int y) {ad_de(x,y); ad_de(y,x);}
    void dfs1(int cur)
    {
        siz[cur]=1; ws[cur]=0;
        int csc=-233;
        for(int x=fs[cur];x;x=ns[x])
        {
            int c=ss[x];
            if(c==fa[cur]) continue;
            fa[c]=cur;
            dep[c]=dep[cur]+1;
            dfs1(c);
            if(siz[c]>csc) csc=siz[c], ws[cur]=c;
            siz[cur]+=siz[c];
        }
    }
    void dfs2(int cur,int tp)
    {
        fe[cur]=++X; top[cur]=tp;
        if(ws[cur]) dfs2(ws[cur],tp);
        for(int x=fs[cur];x;x=ns[x])
        {
            int c=ss[x];
            if(c!=ws[cur]&&c!=fa[cur]) dfs2(c,c);
        }
        ls[cur]=X;
    }
    void s1(int cur,int V)
    {
        edt(fe[cur],ls[cur],V);
    }
    ll s2(int x)
    {
        int u=1,v=x; ll ans=0;
        int f1=top[u],f2=top[v];
        while(f1!=f2)
        {
            if(dep[f1]<dep[f2]) swap(f1,f2), swap(u,v);
            ans+=sum(fe[f1],fe[u]);
            u=fa[f1]; f1=top[u];
        }
        if(dep[u]>dep[v]) swap(u,v);
        ans+=sum(fe[u],fe[v]);
        return ans;
    }
    }
    int qq[233333];
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",qq+i);
        }
        for(int i=1;i<n;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            lct::adde(a,b);
        }
        lct::dfs1(1); lct::dfs2(1,1);
        for(int i=1;i<=n;i++) edt(lct::fe[i],lct::fe[i],qq[i]);
        for(int i=1;i<=m;i++)
        {
            int p; scanf("%d",&p);
            if(p==1)
            {
                int x,a; scanf("%d%d",&x,&a);
                edt(lct::fe[x],lct::fe[x],a);
            }
            else if(p==2)
            {
                int x,a; scanf("%d%d",&x,&a);
                lct::s1(x,a);
            }
            else
            {
                int x; scanf("%d",&x);
                printf("%lld
    ",lct::s2(x));
            }
        }
    }

    T3

    这个sg题真是感人至深啊...

    参考链接 http://blog.csdn.net/lych_cys/article/details/50896005

    首先我们可以发现问题可以转化为存在若干白点,然后将所有点翻转成黑点,先全部翻转的为胜。因为如果从黑点开始翻转后手可以翻过来...

    那么我们就可以假装只有一个白点,然后把sg值异或在一起。

    算一下sg,可以发现一个白点i可以转移到2i,2i、3i,2i、3i、4i...

    那就是说sg[i]=mex{sg[i]^sg[2i],sg[i]^sg[2i]^sg[3i]...}

    经过仔细观察(归纳证明)可以发现sg[i]只与n/i有关。

    那么我们就可以暴力像莫比乌斯反演那样做,预处理时大模拟显然是不超过O(n)的,对于<=根号n的我们直接存,>根号n的就用n除一下再存。

    #include <iostream>
    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    #include <time.h>
    #include <stdlib.h>
    #include <string>
    #include <bitset>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <sstream>
    #include <stack>
    #include <iomanip>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    typedef pair<int,int> pii;
    typedef long long ll;
    typedef double ld;
    typedef vector<int> vi;
    #define fi first
    #define se second
    #define fe first
    #define FO(x) {freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);}
    #define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);}
    #define Edgc int M=0,fst[SZ],vb[SZ],nxt[SZ],vc[SZ];void ad_de(int a,int b,int c){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;vc[M]=c;}void adde(int a,int b,int c){ad_de(a,b,c);ad_de(b,a,c);}
    #define es(x,e) (int e=fst[x];e;e=nxt[e])
    #define VIZ {printf("digraph G{
    "); for(int i=1;i<=n;i++) for es(i,e) printf("%d->%d;
    ",i,vb[e]); puts("}");}
    #ifdef LOCAL
    #define TIMER cerr<<clock()<<"ms
    "
    #else
    #define TIMER
    #endif
    #define SZ 666666
    int nxt(int x,int n)
    {
        if(x==n) return n+1;
        return n/(n/(x+1)); //end
    }
    int n,k,sg[SZ],fsg[SZ],Q,tmp[SZ];
    int main()
    {
        scanf("%d%d",&n,&k); Q=sqrt(n)+1;
        for(int i=1;i<=n;i=nxt(i,n))
        {
            int cur=0; tmp[cur]=i;
            for(int j=2;j<=i;j=nxt(j,i))
            {
                int t=i/j,p=(t<=Q)?sg[t]:fsg[n/t];
                tmp[cur^p]=i;
                if((i/t-i/(t+1))&1) cur^=p;
            }
            cur=0;
            while(tmp[cur]==i) ++cur;
            if(i<=Q) sg[i]=cur;
            else fsg[n/i]=cur;
        }
        while(k--)
        {
            int ans=0,m;
            scanf("%d",&m);
            for(int i=1;i<=m;i++)
            {
                int x; scanf("%d",&x);
                int t=n/x; ans^=(t<=Q)?sg[t]:fsg[n/t];
            }
            if(ans) puts("Yes"); else puts("No");
        }
    }
  • 相关阅读:
    云时代架构阅读笔记十一——分布式架构中数据一致性常见的几个问题
    云时代架构阅读笔记十——支付宝架构师眼中的高并发架构
    云时代架构阅读笔记九——Disruptor无锁框架为啥这么快
    云时代架构阅读笔记八——JVM性能调优
    lightoj 1024 (高精度乘单精度)
    lightoj 1023
    lightoj 1022
    codeforces 260 div2 C题
    codeforces 260 div2 B题
    codedorces 260 div2 A题
  • 原文地址:https://www.cnblogs.com/zzqsblog/p/5843955.html
Copyright © 2011-2022 走看看