noi.ac及牛客网NOIP模拟赛部分题目记录

牛客网NOIP赛前集训营-提高组(第二场):

T2:分糖果
这个题完全不会啊。考虑序列的情况,容斥就是考虑有多少对相等关系,也就是有多少对等号,那么设 f [ i ] f[i] f[i]表示考虑到 i i i所有情况带上容斥系数的和,有 f [ i ] = ∑ j i − 1 f [ j ] × min ⁡ ( a [ j + 1... i ] ) f[i]=\sum_j^{i-1}f[j]\times \min(a[j+1...i]) f[i]=ji1f[j]×min(a[j+1...i]),那么有可以做到 O ( n 2 ) O(n^2) O(n2),用单调栈优化可以做到 O ( n ) O(n) O(n) f [ n ] f[n] f[n]就是答案。但是现在是环,还要考虑头和尾一样的贡献,直接搞不好考虑,因为不知道这一段的最小值,所以我们可以把最小值放到第一位,把这个环转一下,然后就可以算贡献了。

代码:

#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=1000010;
const int inf=2147483647;
const int mod=1000000007;
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<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,a[Maxn],b[Maxn],mn=inf,pos,f[Maxn],sum[Maxn],sta[Maxn],top;
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
    {
        b[i]=read();
        if(b[i]<mn)mn=b[i],pos=i;
    }
    for(int i=1;i<=n;i++)
    {
        a[i]=b[pos++];
        if(pos==n+1)pos=1;
    }
    sta[top=1]=1;f[1]=a[1];sum[1]=a[1]-1;sum[0]=-1;
    for(int i=2;i<=n;i++)
    {
        if(a[i]>=a[sta[top]])
        {
            f[i]=mod-f[i-1];
            f[i]=(f[i]+(LL)f[i-1]*a[i]%mod+mod)%mod;
        }
        else
        {
            while(top&&a[i]<a[sta[top]])top--;
            if(top)
            {
                f[i]=(LL)(sum[i-1]-sum[sta[top]-1]+mod)%mod*a[i]%mod;
                if(sta[top]&1)f[i]=(f[i]-f[sta[top]]+mod)%mod;
                else f[i]=(f[i]+f[sta[top]])%mod;
            }
            else f[i]=(LL)(1+sum[i-1])%mod*a[i]%mod;
            if(i&1)f[i]=mod-f[i];
        }
        sta[++top]=i;
        if(i&1)sum[i]=(sum[i-1]+f[i])%mod;
        else sum[i]=(sum[i-1]-f[i]+mod)%mod;
    }
    int ans;
    if(n&1)ans=mod-a[1];
    else ans=a[1];
    for(int i=1;i<=n;i++)
    {
        if((n-i)&1)ans=(ans-f[i]+mod)%mod;
        else ans=(ans+f[i])%mod;
    }
    printf("%d",ans);
}

牛客网NOIP赛前集训营-提高组(第四场):

T3:灭虫
考虑DP。先按照 p p p排序, f [ i ] [ j ] f[i][j] f[i][j]表示到第 i i i个点,右端点为 j j j(离散化后)的最大覆盖距离。考虑转移,从 i i i转移到 i + 1 i+1 i+1,如果 i + 1 i+1 i+1是覆盖右边的,那么贡献十分简单,最主要是向左覆盖的贡献的计算。假如现在是 f [ i ] [ j ] f[i][j] f[i][j],更新某个 f [ k ] [ x ] f[k][x] f[k][x],要求 p k − l k p_k-l_k pklk至少覆盖到 p i + 1 p_{i+1} pi+1,而 i + 1 i+1 i+1 k − 1 k-1 k1的全部都向右覆盖,这个贡献是可以算出来的,要是覆盖的太远, j j j之前的贡献没有算上也没有关系,因为这样的已经在前面被算了,这样就可以做到 O ( n 3 ) O(n^3) O(n3),优化一下就是 O ( n 2 ) O(n^2) O(n2)了。看到这种比较复杂的不要害怕,只需要按部就班走就行了。

代码:

#include
using namespace std;
const int Maxn=3010;
const int inf=2147483647;
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<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n;
struct Node{int p,l,r;}a[Maxn];
bool cmp(Node a,Node b){return a.p<b.p;}
int b[Maxn*3],lb=0,c=0;
int to[Maxn*3];
map<int,int>id;
int f[Maxn][Maxn*3],ans=-inf,mx1[Maxn][Maxn*3],mx2[Maxn][Maxn*3];
void cmax(int &x,int y){x=max(x,y);}
int main()
{
    memset(mx1,-63,sizeof(mx1));
    memset(mx2,-63,sizeof(mx2));
    memset(f,0,sizeof(f));
    n=read();
    for(int i=1;i<=n;i++)
    {
        a[i].p=read(),a[i].l=read(),a[i].r=read();
        b[++lb]=a[i].p-a[i].l;
        b[++lb]=a[i].p;
        b[++lb]=a[i].p+a[i].r;
    }
    sort(a+1,a+1+n,cmp);
    sort(b+1,b+1+lb);
    for(int i=1;i<=lb;i++)
    {
        if(i==1||b[i]!=b[i-1])c++;
        to[c]=b[i];id[b[i]]=c;
    }
    a[0].p=0;
    for(int i=0;i<=n;i++)
    {
        if(!i)
        {
            f[1][id[a[1].p+a[1].r]]=a[1].r;
            f[1][id[a[1].p]]=a[1].l;
        }
        int mx=a[i].p;int ID=id[a[i].p-a[i].l];
        for(int j=i-1;j>=0;j--)
        {
            if(a[i].p-a[i].l<=a[j+1].p)
            {
                cmax(f[i][id[mx]],mx+mx2[j][ID]);
                cmax(f[i][id[mx]],mx+mx1[j][ID]-(a[i].p-a[i].l));
            }
            cmax(mx,a[j].p+a[j].r);
        }
        for(int j=0;j<=c;j++)
        if(j)mx1[i][j]=max(mx1[i][j-1],f[i][j]);
        else mx1[i][j]=f[i][j];
        for(int j=c;j>=0;j--)
        mx2[i][j]=max(mx2[i][j+1],f[i][j]-to[j]);
        for(int j=0;j<=c;j++)
        {
            int p=id[a[i+1].p+a[i+1].r];
            if(p>j)cmax(f[i+1][p],f[i][j]+a[i+1].p+a[i+1].r-max(a[i+1].p,to[j]));
            cmax(f[i+1][j],f[i][j]);
        }
    }
    for(int i=1;i<=n;i++)
    for(int j=1;j<=c;j++)
    cmax(ans,f[i][j]);
    printf("%d",ans);
}

你可能感兴趣的:(其他)