zoukankan      html  css  js  c++  java
  • 2017-2-26四校联考

    T2正解是什么生成函数、插值等等等理论,被我组合数水过去了,T3看了看不会,最后因为出题人数据没做好T3没测,我过了两题,假装AK了。200/300

    T1.矩形

    题目大意:给定一个2*n的数字矩阵,将其划分为m个子矩阵,要求最小化子矩阵和的最大值,求这个值。(n<=100,000,m<=min(100,2*n),矩阵中元素均为正整数)

    思路:最小化最大值,先无脑二分个答案,又发现只要求出最少分几块,不超过m就可行(从一个可行方案中多切一块一定也可行)。f[i]表示前2*i的矩阵分成若干个和不超过当前二分出的答案至少要几块,每次转移有两种情况:1.取宽度为2的子矩阵,前缀和一下二分到能取的最远的就行了;2.取两行宽度为1的拼起来,可以这么做:先预处理出每个格子作为块的末尾向前能走到最远的格子,然后每次转移开两个指针(假设叫j和k),分别表示两列目前选的分别覆盖到第一行的j+1~i和第二行的k+1~i,一开始j和k都设为i,每次取出较大者根据预处理的信息多取一段,并更新f[i]=f[max(j,k)]+目前选的段数,选超过m段就可以退出了。总复杂度O(n(m+logn)log)。

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    #define ll long long
    char B[1<<26],*S=B,C;int X;
    inline int read()
    {
        while((C=*S++)<'0'||C>'9');
        for(X=C-'0';(C=*S++)>='0'&&C<='9';)X=(X<<3)+(X<<1)+C-'0';
        return X;
    }
    #define MN 100000
    #define INF 0x3FFFFFFF
    ll a[MN+5],b[MN+5],c[MN+5];
    int n,m,ta[MN+5],tb[MN+5],fa[MN+5],fb[MN+5],f[MN+5];
    bool check(ll x)
    {
        int i,j,k,l;
        for(i=1;i<=n;++i)
            fa[i]=fa[ta[i]=lower_bound(a,a+i,a[i]-x)-a]+1,
            fb[i]=fb[tb[i]=lower_bound(b,b+i,b[i]-x)-b]+1;
        if(fa[n]>m||fb[n]>m)return false;
        for(i=1;i<=n;++i)
        {
            f[i]=c[i]-c[i-1]>x?INF:f[lower_bound(c,c+i,c[i]-x)-c]+1;
            for(j=k=i,l=1;l<=m&&(j||k);++l)j>k?j=ta[j]:k=tb[k],f[i]=min(f[i],f[max(j,k)]+l);
            if(f[i]>m)return false;
        }
        return true;
    }
    int main()
    {
        freopen("rec.in","r",stdin);
        freopen("rec.out","w",stdout);
        fread(B,1,1<<26,stdin);
        int i;ll l=0,r,x,mid,ans;
        n=read();m=read();
        for(i=1;i<=n;++i)x=read(),l=max(l,x),a[i]=a[i-1]+x;
        for(i=1;i<=n;++i)x=read(),l=max(l,x),b[i]=b[i-1]+x,c[i]=a[i]+b[i];
        for(r=c[n];l<=r;)
            if(check(mid=l+r>>1))ans=mid,r=mid-1;
            else l=mid+1;
        cout<<ans;
        fclose(stdin);fclose(stdout);return 0;
    }

    T2.数列

    题目大意:给出n,k,定义一个数列的权值为各项乘积,求所有和为n且不超过k项的正整数数列的权值和。(n<=10^9,k<=30000)

    思路:考虑到k比较小,我们枚举数列的项数i,把n分成i个数,求各项乘积,相当于求把n个物品分成i段,每段各选出一个物品的方案数,发现只要确定每一段中选的物品在段内前面有多少个不选的物品,后面有多少个不选的物品,就能确定一个方案,先强制每段分1,问题转化为把n-i分成2i个非负整数的方案数,容易得出组合公式,答案即为Σ(i=1~k)C(n+i-1,2i-1)。这个式子可以O(k)计算,注意特判n<k。//出题人的做法复杂度是O(klogk)的好像。

    #include<cstdio>
    #define MOD 998244353
    #define MK 60000
    int f[MK+5],v[MK+5];
    int inv(int x)
    {
        int t=x,y=MOD-3;
        for(;y;t=1LL*t*t%MOD,y>>=1)if(y&1)x=1LL*x*t%MOD;
        return x;
    }
    int main()
    {
        freopen("seq.in","r",stdin);
        freopen("seq.out","w",stdout);
        int n,k,i,ans=0,x,y=1;
        scanf("%d%d",&n,&k);if(k>n)k=n;x=n;
        for(f[0]=i=1;i<k<<1;++i)f[i]=1LL*f[i-1]*i%MOD;
        for(--i,v[i]=inv(f[i]);i--;)v[i]=1LL*v[i+1]*(i+1)%MOD;
        for(i=1;i<=k;++i)
        {
            ans=(ans+1LL*x*y)%MOD;
            x=1LL*x*(n+i)%MOD*(n-i)%MOD;
            y=1LL*y*v[(i<<1)+1]%MOD*f[(i<<1)-1]%MOD;
        }
        printf("%d",ans);
        fclose(stdin);fclose(stdout);return 0;
    }
  • 相关阅读:
    高性能解决线程饥饿的利器 StampedLock
    月初刚拿到美团offer,新鲜的美团现场面试41题(三面技术+HR面)
    这两份Java“并发+异步”编程宝典,堪称编程界的“瑰宝”
    阿里架构师的学习笔记:多线程+JVM+Mysql+Redis+设计模式
    打造多模块+高可用+高扩展Spring Cloud版分布式电商项目源码分享
    想要彻底搞懂微服务架构必先学:SpringBoot+SpringCloud+docker
    厉害了!阿里P8手写《springboot 核心》PDF来了
    阿里P8熬夜总结Spring源码笔记,上线3分钟“全网跪求”
    UFLDL课程学习(二)
    UFLDL课程学习(一)
  • 原文地址:https://www.cnblogs.com/ditoly/p/20170226C.html
Copyright © 2011-2022 走看看