2018/2/17

鸽了一天。。不要在意(反正也没人看

1.一个有趣的idea(不知道有没有题)

给一张图,每个边有边权,现在要求log时间内求出:
给出x值和k点,去除大于x的所有边后k连通块的信息(sum啊max啊等等线段树干的事)
做法:首先跑最小生成树,我们建立一棵新树,如果最小生成树连接了u和v,建立新点p,连接p,fa[u]和p,fa[v],并且把p作为新的连通块的父亲节点
这样就把连通块上询问的信息变到子树上询问了

2.ZOJ - 3949

题意:给出一棵树,再建一条边,使得 ni=2dis(1,i) ∑ i = 2 n d i s ( 1 , i ) 最小
做法:我们考虑直接求出另外一个点在x的贡献
我们得知了这个点在x父亲的贡献
考虑到x,x到1号节点路径上有一个节点的最优路径会改变
X的儿子贡献会改变
X的父亲以及父亲的父亲等等的子树贡献会改变
我们发现这些改变都是等差数列
分别维护即可

//Copyright(c)2017 Mstdream
#include<bits/stdc++.h>
using namespace std;
#define LL long long
inline void splay(LL &v){
    v=0;char c=0;LL p=1;
    while(c<'0' || c>'9'){if(c=='-')p=-1;c=getchar();}
    while(c>='0' && c<='9'){v=(v<<3)+(v<<1)+c-'0';c=getchar();}
    v*=p;
}
const LL N=400010;
LL nxt[N],fir[N],sz,n,siz[N],fa[N],to[N],dep[N],ans,tot,f[N];
void add(LL x,LL y){
    nxt[++sz]=fir[x],fir[x]=sz,to[sz]=y;
}
void init(){
    for(LL i=1;i<=n*2;i++){
        siz[i]=nxt[i]=fir[i]=to[i]=0;
        fa[i]=dep[i]=f[i]=0;
    }
    sz=0;
}
void dfs1(LL x,LL f){
    fa[x]=f;siz[x]=1;dep[x]=dep[f]+1;
    for(LL u=fir[x];u;u=nxt[u]){
        if(to[u]!=fa[x]){
            dfs1(to[u],x);
            siz[x]+=siz[to[u]];
        }
    }
}

void dfs2(LL x,LL pre,LL ooo){
    if(dep[x]>1){
        LL ret=tot;
        ret-=siz[x]*(dep[x]-2);
        ret-=pre;
        ans=min(ans,ret);
    }
    for(LL u=fir[x];u;u=nxt[u]){
        if(to[u]!=fa[x]){
            if(dep[x]<4){
                dfs2(to[u],0,0);
            }
            else{
                f[dep[x]]=siz[x]-siz[to[u]];
                LL now=(siz[x]-siz[to[u]])*(dep[x]-3);
                if(dep[x]&1)dfs2(to[u],pre-ooo+now,ooo-f[(dep[x]+3)/2]+siz[x]-siz[to[u]]);
                else dfs2(to[u],pre-ooo+now,ooo+siz[x]-siz[to[u]]);
            }
        }
    }
}
int main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    init();splay(n);
    for(LL i=1;i<n;i++){
        LL x,y;splay(x),splay(y);
        add(x,y),add(y,x);
    }
    dfs1(1,0);
    tot=0;
    for(LL i=1;i<=n;i++){
        tot+=dep[i]-1;
    }
    ans=tot;
    dfs2(1,0,0);
    cout<<ans<<endl;
}

3.idea*2

题意:给出一张地图,每个点需要 Wi W i 个士兵才能攻占,每个边需要 Vi V i 个士兵才能攻占,士兵攻占完后可以沿着攻占完了的边走动,假设士兵没有损伤,一开始可以向任意点空降任意数量的士兵(每个点空降的单位代价不同),求最小代价
做法:首先士兵沿着最小生成树行走一定最优
我们对每个节点记录a,b,v三个值域
a代表占领当前连通块所需要的最少士兵数
b代表占领当前连通块所经过的代价最大的边
v代表占领当前连通块的最小代价
每次合并,v用a*b更新一下即可

//Copyright(c)2017 Mstdream
#include<bits/stdc++.h>
using namespace std;
#define LL long long
inline void splay(int &v){
    v=0;char c=0;int p=1;
    while(c<'0' || c>'9'){if(c=='-')p=-1;c=getchar();}
    while(c>='0' && c<='9'){v=(v<<3)+(v<<1)+c-'0';c=getchar();}
    v*=p;
}
const int N=300010;
int a[N],b[N],fa[N],n,m;
LL v[N],ans;
int getfa(int x){
    if(fa[x]==x)return x;
    return fa[x]=getfa(fa[x]);
}
struct E{
    int l,r,w;
    bool operator<(const E&x)const{
        return w<x.w;
    }
}e[N];
int main(){
    splay(n),splay(m);
    for(int i=1;i<=n;i++){
        splay(a[i]),splay(b[i]);
        v[i]=(LL)a[i]*b[i];
        fa[i]=i;
    }
    for(int i=1;i<=m;i++){
        splay(e[i].l),splay(e[i].r),splay(e[i].w);
    }
    sort(e+1,e+m+1);
    for(int i=1;i<=m;i++){
        int x=getfa(e[i].l),y=getfa(e[i].r);
        if(x!=y){
            fa[x]=y;
            a[y]=max(e[i].w,max(a[x],a[y]));
            b[y]=min(b[x],b[y]);
            v[y]=min((LL)a[y]*b[y],v[x]+v[y]);
        }
    }
    for(int i=1;i<=n;i++)if(fa[i]==i)ans+=v[i];
    cout<<ans<<endl;
}

4.scppc2016 problemH

题意:定义广义pascal三角:如三维的pascal三角为 a[i][j][k]=a[i1][j][k]+a[i][j1][k]+a[i][j][k1] a [ i ] [ j ] [ k ] = a [ i − 1 ] [ j ] [ k ] + a [ i ] [ j − 1 ] [ k ] + a [ i ] [ j ] [ k − 1 ]
求所有维数加起来等于x的值有哪些(比如三维就是 i+j+k==x i + j + k == x
做法:二维pascal三角中每个数是一个组合数,可以看做是 (1,1) ( 1 , 1 ) (n,m) ( n , m ) 的路径数。
三维中也可以看做 (1,1,1) ( 1 , 1 , 1 ) (n,m,k) ( n , m , k ) 的方案数
所以我们会求某个点的值了
观察发现交换某两位的数答案不变,我们规定必须数值必须从大到小
这样就可以搜索了

//Copyright(c)2017 Mstdream
#include<bits/stdc++.h>
using namespace std;
#define LL unsigned long long
LL ans[10000000],c[99][99];
LL t,d,n;
LL a[55];
void dfs(LL now,LL hs){
    if(now==d){
        a[now]=hs;
        LL x=n;LL p=1;
        for(LL i=1;i<=d;i++){
            p*=c[x][a[i]];
            x-=a[i];
        }
        ans[++t]=p;
        return;
    }
    for(LL i=a[now-1];(d-now+1)*i<=hs;i++){
        a[now]=i;dfs(now+1,hs-i);
    }
}
int main(){
    //freopen("xxx.in","r",stdin);
    c[0][0]=1;
    for(LL i=1;i<=55;i++){
        c[i][0]=1;
        for(LL j=1;j<=i;j++){
            c[i][j]=c[i-1][j]+c[i-1][j-1];
        }
    }
    cin>>d>>n;n--;
    dfs(1,n);
    sort(ans+1,ans+t+1);
    t=unique(ans+1,ans+t+1)-ans-1;
    for(LL i=1;i<=t;i++){
        printf("%I64u\n",ans[i]);
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值