zoukankan      html  css  js  c++  java
  • [APIO2016]

    2016的题貌似是韩国棒子出的,好丧啊.... 看了题解还想了好久......

    -------------------------------------------------

    A.Boat

    有n个数,每个数字可取[li,ri]内的任意整数si,但是要求对于任意i<j,都有si<sj,求方案数  n<=500,l,r<=10^9

    题解:首先离散,然后不同区间的方案数很好转移,我们考虑相同区间的方案数,发现是一个差分了多次的数列,如果区间长度是l,选m个这样的区间,那么方案数是11111...差分m次后的第l项。然后我们可以发现这个其实是一个组合数,等于C(m,m+l)。我们用f[i][j]表示第i个选第j个区间的方案数,然后我们把f数组前缀和之后推出公式,

    f[i][j]=∑C(i-i'-1,i-i'+l-1) * f[i'-1][j-1]

    复杂度n^3

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #define mod 1000000007
    #define MAXN 1000
    #define int long long
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
     
    int tot=-1,n,L[2*MAXN+5];
    int l2[MAXN*2+5],l[MAXN+5],r[MAXN+5];
    int f[MAXN+5][MAXN*2+5];
    int inv[MAXN+5],p[MAXN+5];
     
    main()
    {
        p[0]=inv[0]=p[1]=inv[1]=1;
        for(int i=2;i<=MAXN;i++)
        {
            p[i]=1LL*p[i-1]*i%mod;
            inv[i]=1LL*(mod-mod/i)*inv[mod%i]%mod;
        }
        //for(int i=2;i<=MAXN;i++) inv[i]=1LL*inv[i]*inv[i-1]%mod;
        n=read();
        for(int i=1;i<=n;i++)
        {
            l[i]=l2[i*2-1]=read();
            r[i]=l2[i<<1]=read();l2[i<<1]+=1;
        }
        sort(l2+1,l2+n*2+1);
        for(int j=1;j<=n*2;j++)if(l2[j]!=l2[j-1])
            l2[++tot]=l2[j];  tot++;
        for(int i=1;i<tot;i++) L[i]=l2[i]-l2[i-1]; 
        for(int i=1;i<=n;i++)
        {   
            l[i]=upper_bound(l2,l2+tot,l[i])-l2;
            r[i]=upper_bound(l2,l2+tot,r[i])-l2;
        //  cout<<l[i]<<" "<<r[i]<<endl;
        }
        for(int i=0;i<tot;i++) f[0][i]=1;
        for(int i=1;i<=n;i++)
        {
            f[i][0]=1;
            for(int j=l[i];j<=r[i];j++)
            {
                f[i][j]=(long long)L[j]*f[i-1][j-1]%mod;//cout<<f[i][j]<<endl;
                int now=1;long long c=L[j]-1;
                for(int k=i-1;k;--k)
                    if(l[k]<=j&&j<=r[k])
                    {
                        now++;
                        c=c*(long long)(L[j]+now-2)%mod*inv[now]%mod;
                        if(!c)break;
                        f[i][j]=(f[i][j]+(long long)f[k-1][j-1]*c)%mod;
                    }
            }
            for(int j=1;j<tot;j++)
                f[i][j]=((long long)f[i][j]+f[i-1][j]+f[i][j-1]-f[i-1][j-1]+mod)%mod;
        //  for(int j=1;j<tot;j++)
            //  cout<<i<<" "<<j<<" "<<f[i][j]<<endl;
        }
        cout<<(long long)(f[n][tot-1]-1+mod)%mod;
        return 0;
    }

    B.给定一棵n个非叶节点,m个叶节点的树,有边权,定义修改边权的费用为前后边权的差的绝对值,你要让所有叶节点到根节点的距离相同,但又不能把边权改成负数,求最小费用。

    n,m<=300000

    题解:我们用f[i][j]表示第i个点,子树中的叶节点到它的距离都是j的最小费用,那么f[i][0]=∑Wjk   (jk都在子树i中)。

    很显然,f函数是一个下凸的函数,并且存在一些拐点,拐点前后斜率变化是1,但是拐点可以重在某一个点上。只有一个叶节点时,拐点有两个,且都为于0,凸壳形状像一个绝对值函数。

    所以我们只要知道所有拐点,就可以知道这个函数啦。

    我们考虑向一个子树添加边时候的影响,由于w可以无限增大,在一定大小后只修改这一条边一定最优,斜率肯定都是1,所以对于斜率大于1的部分我们都可以舍去,即弹掉所有原来斜率大等0的拐点。

    这样一次合并我们实际上只把它向右平移了一下。需要删除和合并操作,所以写一个可并堆就好啦。

    复杂度(n+m)log(n+m)

    #include<iostream>
    #include<cstdio>
    #define MN 600000
    #define ll long long
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
     
    int n,m,fa[MN+5],in[MN+5];
    ll w[MN+5],ans=0;
     
    struct Heap{
        Heap *l,*r;
        ll p;int d;
        Heap(ll _p):p(_p),l(0),r(0),d(1){};
        inline friend int dis(Heap*x){return x?x->d:0;}
        friend Heap* Merge(Heap*x,Heap*y)
        {
            if(!x) return y;if(!y) return x;
            if(x->p<y->p) swap(x,y);
            x->r=Merge(x->r,y);
            if(dis(x->r)>dis(x->l)) swap(x->l,x->r);
            x->d=dis(x->r)+1;
             
            return x;
        }
    }*s[MN+5],*a,*b;
     
    int main()
    {
        n=read();m=read();n+=m;
        for(int i=2;i<=n;i++)
            {++in[fa[i]=read()];ans+=(w[i]=read());}
        for(int i=n;i>1;i--)
        {
            if(!s[i]) s[i]=Merge(new Heap(0),new Heap(0));
            for(int j=1;j<in[i];j++)
                s[i]=Merge(s[i]->l,s[i]->r);
            a=new Heap(s[i]->p+w[i]);s[i]=Merge(s[i]->l,s[i]->r);
            b=new Heap(s[i]->p+w[i]);s[i]=Merge(s[i]->l,s[i]->r);
            s[fa[i]]=Merge(s[fa[i]],Merge(s[i],Merge(a,b)));
        }
        int top=0;
        while(s[1])w[++top]=s[1]->p,s[1]=Merge(s[1]->l,s[1]->r);w[top+1]=0;
        for(int i=top;m;m--,i--) ans-=(w[i]-w[i+1])*m;
        printf("%lld
    ",ans);
        return 0;
    }

    C.Gap

    给定一个长度为n的严格递增数列,你每次可以询问一个数字区间的最大值,最小值,求最大差分。n<=100000

    题解:对于subtask1,询问次数不超过(n+1)/2,我们枚举左右节点查,然后缩短这个区间就好啦。

    对于subtask2,询问的区间含有k个数时费用是k+1,要让费用不超过3n。我们先求最大值x和最小值y,显然答案不会低于(y-x)/(n-1),所以我们把数字分块,每块内不存在答案,都询问一次就行了。

    #include "gap.h"
    #include<algorithm>
    #include<iostream>
    using namespace std;
    #define ll long long
    #define INF 1000000000000000000LL
    ll s[200005];
    ll ans=0;
    int cnt=0;
    
    ll solve(int x)
    {
        ll l,r;
        MinMax(0,INF,s+1,s+x);cnt=2;int i=2,j=x-1;
        for(l=s[1]+1,r=s[x]-1;i<=j;l=s[i++]+1,r=s[j--]-1)
        {
            MinMax(l,r,s+i,s+j);
        }
        for(int i=2;i<=x;i++)
            ans=max(ans,s[i]-s[i-1]);
        return ans;
    }
    
    ll findGap(int T, int N)
    {
        if(T==1) return solve(N);
        if(N==1) return 0;
        MinMax(0,INF,s+1,s+2);cnt=2;
        ll p=(s[2]-s[1]-1)/(N-1)+1;
        for(ll i=s[1]+1;i<=s[2]-1;i+=p)
        {
            MinMax(i,min(i+p-1,s[2]-1),s+cnt+1,s+cnt+2);
            if(s[cnt+1]>0)cnt+=2;
        }
        sort(s+1,s+cnt+1);
        for(int i=2;i<=cnt;i++)
            ans=max(ans,s[i]-s[i-1]);
        return ans;
    }
  • 相关阅读:
    Java学习开篇
    《我的姐姐》
    世上本无事,庸人自扰之
    这48小时
    补觉
    淡定
    es java api 设置index mapping 报错 mapping source must be pairs of fieldnames and properties definition.
    java mongodb groupby分组查询
    linux 常用命令
    mongodb too many users are authenticated
  • 原文地址:https://www.cnblogs.com/FallDream/p/apio2016.html
Copyright © 2011-2022 走看看