51nod算法马拉松20

9 篇文章 0 订阅
8 篇文章 0 订阅

先写一下做出的题吧(感觉自己好弱。。。。。)
A:
拆分贡献,组合数搞一搞;

#include<bits/stdc++.h>
#define rep(i,k,n) for(int i=k;i<=n;i++)
#define rep2(i,k,n) for(int i=k;i>=n;i--)
using namespace std;
typedef long long ll;
const int N=2e5+7;
const int mod=1e9+7;
void upd(int& x,int y){x+=y;if(x>=mod)x-=mod;} 
int qpow(int a,int b){int aa=a,res=1;for(;b;b>>=1){if(b&1)res=1ll*res*aa%mod;aa=1ll*aa*aa%mod;}return res;}
int bin[N],pin[N],ans=0;
void init(){bin[0]=pin[0]=1;
    rep(i,1,N-1)bin[i]=1ll*bin[i-1]*i%mod;
    pin[N-1]=qpow(bin[N-1],mod-2);
    rep2(i,N-2,1)pin[i]=1ll*pin[i+1]*(i+1)%mod;
}
int C(int x,int y){
    if(y>x)return 0;
    return 1ll*bin[x]*pin[x-y]%mod*pin[y]%mod;
}
struct E{
    int to,next;E(int to=0,int next=0):to(to),next(next){}
}edge[N];
int head[N],tot=0,n,k,sz[N];
void add(int x,int y){
    edge[++tot]=E(y,head[x]);head[x]=tot;
    edge[++tot]=E(x,head[y]);head[y]=tot;
}
void dfs(int x,int fa){
    sz[x]=1;
    for(int i=head[x];i;i=edge[i].next){
        int to=edge[i].to;
        if(to!=fa){
            dfs(to,x);
            sz[x]+=sz[to];
            upd(ans,C(n,k));
            upd(ans,mod-C(sz[to],k));
            upd(ans,mod-C(n-sz[to],k));
        }
    }
}
int main(){
    init();
    scanf("%d%d",&n,&k);
    rep(i,1,n-1){
        int x,y;scanf("%d%d",&x,&y);
        add(x,y);
    }dfs(1,0);
    printf("%d\n",ans);
}

B:
一开始看到这题思如泉涌,但没有一种靠谱……..
好长时间之后,通过数形结合(乱涂乱画)发现:
差分就行了……
说明了数形结合(乱涂乱画)的重要性;

#include<bits/stdc++.h>
#define rep(i,k,n) for(int i=k;i<=n;i++)
#define rep2(i,k,n) for(int i=k;i>=n;i--)
using namespace std;
void sc(int& x){x=0;char c=getchar();while(c>'9' || c<'0')c=getchar();
for(;c>='0' && c<='9';c=getchar())x=x*10+c-'0';
}
typedef int ll;
const int N=1e6+7;
vector<int> tu[N];
int a[N],n,mx=0,stk[N],top;
ll cha[N],ans=0;
void solve(int x){top=tu[x].size();
    rep(i,0,top-1)stk[i+1]=tu[x][i];
    ll sum=0;
    rep(i,2,top){
        sum+=(i-1)*(stk[i]-stk[i-1])+1;
        cha[stk[i]+1]-=(sum+i-1);
        cha[stk[i]+2]+=(sum-i+1);
    }
    sum=0;
    rep2(i,top-1,1){
        sum+=(top-i)*(stk[i+1]-stk[i])+1;
        cha[stk[i]]+=(top-i-sum);
        cha[stk[i]+1]+=(top-i+sum);
    }   
}
int main(){
    sc(n);
    rep(i,1,n){
        sc(a[i]);
        mx=max(mx,a[i]);
        tu[a[i]].push_back(i);
    }
    rep(i,1,mx)
        if(tu[i].size()>1)
            solve(i);
    rep(i,1,n)cha[i]+=cha[i-1];
    rep(i,1,n)cha[i]+=cha[i-1];
    rep(i,1,n){
        ans+=1ll*cha[i]*a[i];
    }
    printf("%u\n",ans);
    return 0;   
}

D:
一定要对每一道题认真刨析。
权函数凸性满足,四边形不等式满足;
放在序列上就是裸利用矩阵凸单调性,那么树上怎么做呢?
一样做!维护决策树就可以了,然后发现真正的树的结构也不那么重要了;

#include<bits/stdc++.h>
#define rep(i,k,n) for(int i=k;i<=n;i++)
#define rep2(i,k,n) for(int i=k;i>=n;i--)
using namespace std;
typedef long long ll;
const int N=1e5+7;
const int inf=0x3f3f3f3f;
const ll Inf=0x3f3f3f3f3f3f3f3full;
ll f[N],a[N],ans=Inf;
int son[N],fa[N],n,p,be[N],who[N];
struct E{
    int to,next;E(int to=0,int next=0):to(to),next(next){}
}edge[N<<1];
int head[N],tot=0;
void add(int x,int y){
    edge[++tot]=E(y,head[x]);head[x]=tot;
}
ll g(int j,int i){
    if(pow(i-j,p)>Inf)return Inf;
    return f[j]+1ll*pow(i-j,p)+a[i];
}
int vs(int x,int now){
    int l=x+1,r=n+1;
    while(l<r){
        int mid=(l+r)>>1;
        if(g(x,mid)<=g(now,mid))r=mid;
        else l=mid+1;
    }
    return l;
}
void dp(int x,int now){
    while(be[son[now]]<=x)now=son[now];
    who[x]=now;
    if(x>1)f[x]=g(now,x);
    else f[x]=a[x];
    for(;fa[x] && g(fa[x],max(be[fa[x]],x+1))>=g(x,max(be[fa[x]],x+1));fa[x]=fa[fa[x]],now=min(now,fa[x]));
    int cx=son[fa[x]];
    son[fa[x]]=x;
    be[x]=vs(x,fa[x]);


    for(int i=head[x];i;i=edge[i].next){
        int v=edge[i].to;
        dp(v,now);
    }
    son[fa[x]]=cx;

    if(!head[x])ans=min(ans,f[x]-a[x]);
}
int main(){
    scanf("%d%d",&n,&p);
    rep(i,1,n){
        scanf("%lld%d",&a[i],&fa[i]);
        add(fa[i],i);
    }be[0]=inf,f[0]=Inf;
    dp(1,0);
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值