• 2017-2-26福建四校联考


    哎我好菜啊 从来没打过表的萌新这次想打个表结果打太多了长度超限了(后来发现根本没必要打表)

    ---------我是分割线

    A.矩形

    给定一个2*n的矩形,每个位置有一个正权值,你要把它恰好分成m个矩形,使得所有矩形的和的最大值最小并求出最小的最大值。

    n<=100000 m<=100

    题解:

    首先很显然m只是一个附加条件,如果你能分<m段,那么你一定能分m段。

    看到最大值最小,最小值最大的问题,很自然想到二分答案。

    然后我们用一个dp来check。用f[i]表示前i*2的矩形至少要分几段。

    每次转移的时候,我们考虑分一个2*x的矩形还是分成多个1*x的矩形

    可以预先二分处理好每个点在两种情况下最远从哪里更新,计算分f[i]时首先跳一次,表示分一个2*x的矩形

    否则考虑分1*x的矩形,用两个指针分别表示两行,每次选择坐标大的指针向前跳,然后用两个指针最大值那里的dp值更新答案。

    由于最多分m段,所以我们每个点最多跳m次就可以收工了。

    复杂度n*(n+m)*logn

    #include<iostream>
    #include<cstdio>
    #define INF 200000000
    #define ll long long
    using namespace std;
    inline ll read()
    {
        ll 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;
    ll maxn=0;
    ll s[100005],s1[100005],s2[100005];
    int f[100005];
    int l1[100005],l2[100005],l3[100005];
    
    int getp(ll*S,int l,int r,ll lim)
    {
        int mid,ans,num=r;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(S[num]-S[mid]>lim) l=mid+1;
            else r=mid-1,ans=mid;
        }
        return ans;
    }
    
    bool check(ll lim)
    {
        for(int i=1;i<=n;i++)
        {
            l1[i]=getp(s,0,i,lim);
            l2[i]=getp(s1,0,i,lim);
            l3[i]=getp(s2,0,i,lim);
            f[i]=INF;
        }
        for(int i=1;i<=n;i++)
        {
            f[i]=min(f[i],f[l1[i]]+1);
            for(int j=i,k=i,l=1;l<=m&&(j||k);l++)
            {
                if(j<k)k=l3[k];
                else j=l2[j];
                f[i]=min(f[i],f[max(j,k)]+l);
            }
        }
        return f[n]<=m;
    }
    
    int main()
    {
        freopen("rec.in","r",stdin);
        freopen("rec.out","w",stdout);
        n=read();m=read();
        for(int i=1;i<=n;++i) s1[i]=read(),maxn=max(maxn,s1[i]);
        for(int i=1;i<=n;++i) s2[i]=read(),maxn=max(maxn,s2[i]);
        for(int i=1;i<=n;++i)
        {
            s1[i]=s1[i]+s1[i-1];s2[i]=s2[i]+s2[i-1];
            s[i]=s1[i]+s2[i];
        }
        ll l=maxn,r=1e15,mid,ans;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid))ans=mid,r=mid-1;
            else l=mid+1;
        }
        cout<<ans;
        return 0;
    }

    2.数列

    给定n,k定义一个正整数数列的贡献为所有数的乘积,求所有数列和为n,项数不超过k的数列的贡献之和。

    n<=10^9,k<=30000

    看到这题我就打表观察了一下,乱差分一下发现是一个多项式,然而多项式的一套理论和算法我都不会,所以我就继续乱搞,突然发现可以用组合数来表示!!!

    然后发现没办法算阶乘(我傻,其实不用) 就分段打表阶乘...结果一不小心打多了....(gg)

    出题人题解:

    简单做法:可以用组合数。我是打表乱推的, 所以引用一下ditoly大佬的解释。

    所以就是这样  复杂度O(k)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define mod 998244353
    #define ll long long
    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;
    ll f[60005],v[60005];
    ll ans=0;
    
    ll pow(int x,int p)
    {
        ll sum=1;
        for(ll i=x;p;p>>=1,i=(i*i)%mod)
            if(p&1) sum=sum*i%mod;
        return sum;
    }
    
    int main()
    {
        freopen("seq.in","r",stdin);
        freopen("seq.out","w",stdout);
        n=read();m=read();f[0]=1;if(n<m) m=n;
        for(int i=1;i<m<<1;i++) f[i]=f[i-1]*i%mod;
        for(int i=1;i<m<<1;i++) v[i]=pow(f[i],mod-2);
        ll x=n;
        for(int i=1;i<=m;i++)
        {
            ans=(ans+x*v[(i<<1)-1]%mod)%mod;
            x=x*(n-i)%mod*(n+i)%mod;
        }
        cout<<ans;
        return 0;
    }

    3.奇怪的题,没人做,也没数据,假装只有两道题。

  • 相关阅读:
    bzoj3653: 谈笑风生
    bzoj1858: [Scoi2010]序列操作
    bzoj1857: [Scoi2010]传送带
    bzoj1856: [Scoi2010]字符串
    bzoj1855: [Scoi2010]股票交易
    bzoj1854: [Scoi2010]游戏
    bzoj1853: [Scoi2010]幸运数字
    斜堆,非旋转treap,替罪羊树
    NOI2003 文本编辑器
    A_star poj2449 k短路
  • 原文地址:https://www.cnblogs.com/FallDream/p/20170226fjoi.html
Copyright © 2020-2023  润新知