zoukankan      html  css  js  c++  java
  • APIO 2016

    我好菜啊都不会

    T1.boats

    题目大意:给你N段区间,按顺序决定每段区间可以选一个数或不选,若选则选的这个数必须大于所有在这之前选的数,求有多少种方案。(N<=500,区间在[1,1e9]范围内)

    思路:我的做法比较奇怪……根据DP的思路,题目可以转化为一个数列一开始0的位置是1其他都是0,然后每次把一个区间里的数全部变成原来数列的前缀和,然后我按权值分了个块,把区间左右端点离散后如果相邻的差超过k就分成相差不超过k的几块,预处理出一开始都为1的各个长度的区间经过若干次前缀和后和为多少,我们就可以把一个区间的和表示成有几种初始值,每种进行若干次前缀和后的总和,每次操作把一个新的值插入离散出来的区间里,利用预处理的信息算答案(有点难说清楚,感兴趣的参见代码实现……),复杂度O(kn+(1e9/k+n)*n^2),k为块大小,子任务制得分58/100(最后一个T),后来发现多次前缀和就是组合数,于是复杂度变成O(k+(1e9/k+n)*n^2),好像就能过了。

    暴力算前缀和

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    inline int read()
    {
        int x;char c;
        while((c=getchar())<'0'||c>'9');
        for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
        return x;
    }
    #define MN 500
    #define MC 11000
    #define K 100000
    #define MOD 1000000007
    int l[MN+5],r[MN+5],c[MC+5],cn;
    int f[MN+5][K+5],s[MC+5],t[MC+5],p[MC+5][MN+5];
    inline int mod(int x){return x>=MOD?x-MOD:x;}
    int main()
    {
        int n=read(),i,j,k,x,v;
        for(i=1;i<=K;++i)f[1][i]=i;
        for(i=2;i<=n;++i)for(j=1;j<=K;j+=4)
            f[i][j  ]=mod(f[i][j-1]+f[i-1][j  ]),
            f[i][j+1]=mod(f[i][j  ]+f[i-1][j+1]),
            f[i][j+2]=mod(f[i][j+1]+f[i-1][j+2]),
            f[i][j+3]=mod(f[i][j+2]+f[i-1][j+3]);
        for(i=1;i<=n;++i)c[++cn]=l[i]=read()-1,c[++cn]=r[i]=read();
        sort(c+1,c+cn+1);
        for(i=1,j=0;i<=cn;++i)if(c[i]!=c[j])c[++j]=c[i];cn=j;
        for(i=1,j=cn;i<j;++i)for(k=c[i];c[i+1]-k>K;)c[++cn]=k+=K;
        sort(c+1,c+cn+1);
        for(i=1;i<=n;++i)for(j=x=1;j<=cn;++j)
            if(c[j-1]>=l[i]&&c[j]<=r[i])
            {
                p[j][t[j]++]=x;x=mod(x+s[j]);v=c[j]-c[j-1];
                for(k=s[j]=0;k<t[j];++k)
                    s[j]=(s[j]+1ll*p[j][k]*f[t[j]-k][v])%MOD;
            }
            else x=mod(x+s[j]);
        for(i=1,x=0;i<=cn;++i)x=mod(x+s[i]);
        printf("%d",x);
    }
    View Code

    组合数

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    inline int read()
    {
        int x;char c;
        while((c=getchar())<'0'||c>'9');
        for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
        return x;
    }
    #define MN 500
    #define MC 1100
    #define K 10000000
    #define MOD 1000000007
    int l[MN+5],r[MN+5],c[MC+5],cn;
    int f[K*2+5],vf[K*2+5],s[MC+5],t[MC+5],p[MC+5][MN+5];
    inline int mod(int x){return x>=MOD?x-MOD:x;}
    inline int inv(int x)
    {
        int r=1,y=MOD-2;
        for(;y;y>>=1,x=1LL*x*x%MOD)if(y&1)r=1LL*r*x%MOD;
        return r;
    }
    int main()
    {
        int n=read(),i,j,k,x,v;
        for(i=f[0]=1;i<=K<<1;++i)f[i]=1LL*f[i-1]*i%MOD;
        for(vf[i=K<<1]=inv(f[K<<1]);i--;)vf[i]=1LL*vf[i+1]*(i+1)%MOD;
        for(i=1;i<=n;++i)c[++cn]=l[i]=read()-1,c[++cn]=r[i]=read();
        sort(c+1,c+cn+1);
        for(i=1,j=0;i<=cn;++i)if(c[i]!=c[j])c[++j]=c[i];cn=j;
        for(i=1,j=cn;i<j;++i)for(k=c[i];c[i+1]-k>K;)c[++cn]=k+=K;
        sort(c+1,c+cn+1);
        for(i=1;i<=n;++i)for(j=x=1;j<=cn;++j)
            if(c[j-1]>=l[i]&&c[j]<=r[i])
            {
                p[j][t[j]++]=x;x=mod(x+s[j]);v=c[j]-c[j-1];
                for(k=s[j]=0;k<t[j];++k)
                    s[j]=(s[j]+1LL*p[j][k]*f[t[j]-k+v-1]%MOD*vf[t[j]-k]%MOD*vf[v-1])%MOD;
            }
            else x=mod(x+s[j]);
        for(i=1,x=0;i<=cn;++i)x=mod(x+s[i]);
        printf("%d",x);
    }

    T2.fireworks

    题目大意:给出一棵带边权的树,调整树上边长的消耗是调整后的减去调整前的绝对值,求使根节点到所有叶节点距离相等的最小花费,边长必须大等0。(叶节点个数M<=300,000,每个非叶节点除连向父亲外至少连2条边)

    思路:不会,打了个中位数乱贪心,好像会把边权弄成负的所以挂了,捆绑测试只有7分……

    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #include<iostream>
    using namespace std;
    #define ll long long
    inline int read()
    {
        int x=0;char c;
        while((c=getchar())<'0'||c>'9');
        for(;c>='0'&&c<='9';c=getchar())x=(x<<3)+(x<<1)+c-'0';
        return x;
    }
    #define MN 300000
    struct edge{int nx,t,w;}e[MN*2+5];
    int n,h[MN+5],en;
    ll f[MN+5],l[MN+5],r[MN+5];
    vector<ll> v[MN+5];
    inline void ins(int x,int y,int w)
    {
        e[++en]=(edge){h[x],y,w};h[x]=en;
        e[++en]=(edge){h[y],x,w};h[y]=en;
    }
    inline ll z(ll x){return x<0?-x:x;}
    void dp(int x,int fa)
    {
        if(x>n)return;
        for(int i=h[x];i;i=e[i].nx)if(e[i].t!=fa)
        {
            dp(e[i].t,x);
            f[x]+=f[e[i].t];
            v[x].push_back(l[e[i].t]+e[i].w);
            v[x].push_back(r[e[i].t]+e[i].w);
            f[x]-=r[e[i].t]-l[e[i].t];
        }
        sort(v[x].begin(),v[x].end());
        l[x]=v[x][v[x].size()/2-1];r[x]=v[x][v[x].size()/2];
        for(int i=0;i<v[x].size();++i)f[x]+=z(l[x]-v[x][i]);
    }
    int main()
    {
        int m,i,x;
        n=read();m=read();
        for(i=2;i<=n+m;++i)x=read(),ins(x,i,read());
        dp(1,0);
        cout<<f[1]/2;
    }
    View Code

    正解:用f[i][j]表示子树i内所有叶节点到i距离为j的最小花费,则易知每个f[i]均为下凸函数且可以由各儿子的f合并得到,f[i]具有以下性质:1.f[i][0]=子树i内边权和;2.f[i][0]处函数斜率为负的子树内叶节点个数;3.j足够大时f[i][j]斜率为1。这样我们只要知道f[i][0]之后何时函数斜率增大即可表示这个函数,我们可以记下每个拐点,每个拐点表示在这个点函数斜率增加1(拐点可重),这样儿子的f相加直接把所有拐点合并即可。儿子的f并入父亲的f前先要考虑连到父亲的这条边对儿子的f的影响,分各类情况讨论,我们有:假设凸包H(x)最小值在L~R处取到,这条边长为w,新凸包为H’(x),则:x<=L时,H’(x)=H(x)+w;L<=x<=L+w时,H’(x)=H(L)+w-(x-L);L+w<=x<=R+w时,H’(x)=H(L);R+w<=x时,H’(x)=H(L)+(x-R)-w。即每次我们删掉拐点L和拐点R,并加入拐点L+w和R+w,然后删掉所有使斜率大于1的拐点即可。用可并堆维护拐点,最后把堆中元素全部取出计算答案,复杂度O(MlogM)。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define ll long long
    inline int read()
    {
        int x;char c;
        while((c=getchar())<'0'||c>'9');
        for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
        return x;
    }
    #define MN 600000
    int fa[MN+5],w[MN+5],r[MN+5],cnt;
    ll p[MN*2+5];
    struct heap
    {
        heap*l,*r;ll p;int d;
        heap(ll p):p(p){l=r=0;d=1;}
        friend inline int dis(heap*a){return a?a->d:0;}
        friend heap*merge(heap*a,heap*b)
        {
            if(!a)return b;if(!b)return a;
            if(a->p<b->p)swap(a,b);
            a->r=merge(a->r,b);
            if(dis(a->l)<dis(a->r))swap(a->l,a->r);
            a->d=dis(a->r)+1;
            return a;
        }
    }*rt[MN+5];
    int main()
    {
        int n,m,i,j;ll a,b,s=0;
        n=read();n+=m=read();
        for(i=2;i<=n;++i)++r[fa[i]=read()],s+=w[i]=read();
        for(i=n;i>1;--i)
        {
            if(!rt[i])rt[i]=new heap(0);
            for(j=a=0;j<=r[i];++j)
            {
                if(j<r[i])a=rt[i]->p;
                else b=rt[i]->p;
                rt[i]=merge(rt[i]->l,rt[i]->r);
            }
            rt[fa[i]]=merge(rt[fa[i]],merge(rt[i],merge(new heap(a+w[i]),new heap(b+w[i]))));
        }
        while(rt[1])p[++cnt]=rt[1]->p,rt[1]=merge(rt[1]->l,rt[1]->r);
        for(i=cnt;m;--m,--i)s-=m*(p[i]-p[i+1]);
        printf("%lld",s);
    }

    T3.gap

    题目大意:交互题,有一个N个数的上升序列,不具体给出序列,仅支持一个操作:MinMax(l,r),会给你数值大小在[l,r]内的序列中元素的最大值和最小值,要求你求出序列中相邻元素的最大差。subtask1:每次MinMax的代价为1,使用的代价不得超过(N+1)/2;subtask2:每次MinMax的代价为[l,r]间元素个数加1,使用的代价不得超过3N。(N<=100,000,序列中元素在[0,1e18]内)

    思路:我只会subtask1,先询问[0,1e18],得出最小值x最大值y,再询问[x+1,y-1],以此类推,就能(N+1)/2次询问得出整个序列,得分30/100。subtask2做法:先询问[0,1e18],得出最大最小值,则答案必然不小于(max-min)/(n-1),我们在[min+1,max-1]内每(max-min)/(n-1)分一块,显然块内不存在答案,答案只可能是每一块的最大值与下一块最小值的差分,于是每一块询问一次就可以了,简单的计算后知道这么做最大的代价是3N-1。

    #include<cstdio>
    #include<algorithm>
    #include"gap.h"
    #define ll long long
    using namespace std;
    #define MN 100000
    #define INF 1e18
    ll a[MN+5];
    ll find1(int n)
    {
        ll l=0,r=INF;int i,j;
        for(i=1,j=n;i<=j;l=a[i++]+1,r=a[j--]-1)MinMax(l,r,a+i,a+j);
        for(i=1,r=0;i<n;++i)r=max(r,a[i+1]-a[i]);
        return r;
    }
    ll findGap(int t,int n)
    {
        if(t==1)return find1(n);
        ll p,i;int cnt=1;
        MinMax(0,INF,a+1,a);
        p=(a[0]-a[1]+n-2)/(n-1);
        for(i=a[1];i<a[0]-1;i+=p)
        {
            MinMax(i+1,min(i+p,a[0]-1),a+cnt+1,a+cnt+2);
            if(a[cnt+1]>0)if(++cnt,a[cnt+1]!=a[cnt])++cnt;
        }
        a[++cnt]=a[0];
        for(i=1;i<cnt;++i)p=max(p,a[i+1]-a[i]);
        return p;
    }
  • 相关阅读:
    iOS 开发网络篇—监测网络状态
    再杀掉app之后 删除NSUserDefault存在本地的数据
    iOS开发之duplicate symbols for architecture x86_64错误
    iOS中UITextField输入判断小数点后两位
    ios 适配iOS11&iPhoneX的一些坑
    iOS UITextView 设置 NSLinkAttributeName 属性,点击链接跳转
    iOS- UITextView与键盘回收与键盘遮挡输入框
    iOS 实现单个页面支持横竖屏,其他页面只能竖屏
    iOS最新Mac OS X 10.11之后 安装cocoapods及使用详解
    一种简单的登录加密方案
  • 原文地址:https://www.cnblogs.com/ditoly/p/APIO-2016.html
Copyright © 2011-2022 走看看