首页 > 代码库 > 2017-2-26福建四校联考

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.奇怪的题,没人做,也没数据,假装只有两道题。

 

2017-2-26福建四校联考