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;
    }
  • 相关阅读:
    javascript教程系列-10.DOM(下)
    javascript教程系列-9.DOM(上)
    javascript教程系列-8.BOM
    javascript教程系列-7.Date对象
    javascript教程系列-6.String类型
    javascript教程系列-5.数组
    Python字符串、元组、列表、字典互相转换的方法
    python 中函数
    python中的字符串 列表 字典
    python 基础 知识
  • 原文地址:https://www.cnblogs.com/FallDream/p/apio2016.html
Copyright © 2011-2022 走看看