AtCoder Grand Contest 049

链接

C. Robots

题解

首先考虑判断 \(0\)。容易发现,对于每个 \(b_i\geq a_i\) 即会踩到 \(0\) 的机器人,都需要提前被其他满足 \(a_i<b_i\) 的机器人踩掉。这样直接从右往左判断即可。

考虑替换的过程可以看做删除后再加入。注意到最优策略中,我们最多只会让一个机器人从 \(a_i\geq b_i\) 减到 \(a_i<b_i\),否则考虑只保留最右端的仍然合法。其余的可以直接用右边的点去踩它。

复杂度 \(O(n)\)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200010
using namespace std;
int a[N],b[N],c[N],ct;
int main()
{
    int n; scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    int mn=1e9,ans=1e9;
    for(int i=n;i;i--)
    {
        if(a[i]>b[i]){mn=min(mn,a[i]-b[i]);continue;}
        ans=min(ans,max(ct,b[i]-a[i]+1));
        if(a[i]<mn) ++ct;
    }
    ans=min(ans,ct);
    printf("%d\n",ans);
    return 0;
}

D. Convex Sequence

题解

假设我们找到最低的位置(有多个取最左边的一个),从这个位置开始往左往右斜率都是递增,即差分数组是单调递增的。

单调递增的数组总是可以通过若干次后缀加得到,转换到原数组就是选择一个后缀,依次加 \(1,2,3,\cdots\)

这样可以看做有无限个重量为 \(1,3,6,\cdots\) 的物品,以及重量为 \(n\) 的物品。物品数量只有 \(O(\sqrt m)\) 个。同时最低位置移动的同时只有最多 \(2\) 个物品会发生变化。移动一次复杂度 \(O(m)\)

复杂度 \(O(m\sqrt m)\)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100010,mod=1000000007;
int f[N];
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){x=(x-y<0?x-y+mod:x-y);}
int main()
{
    int n,m; scanf("%d%d",&n,&m);
    f[0]=1;
    for(int i=1;i<=n;i++)
    {
        int w=i*(i+1)/2;if(w>m) break;
        for(int s=w;s<=m;s++) add(f[s],f[s-w]);
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        int w=i*(i-1)/2;
        if(w>m) break;
        if(1ll*(n-i+1)*(n-i+2)/2<=m)
        {
            int q=(n-i+1)*(n-i+2)/2;
            for(int s=m;s>=q;s--) dec(f[s],f[s-q]);
        }
        if(w) for(int s=w;s<=m;s++) add(f[s],f[s-w]);
        for(int t=0;t*n+w<=m;t++) add(ans,f[m-t*n-w]);
    }
    printf("%d\n",ans);
    return 0;
}

E. Increment Decrement

题解

首先考虑 \(B_{i,j}\in[0,1]\) 的情况。考虑假设 \(k=1\),容易列出 dp 方程:\(f_i=\max_{f_{i-1},p_i-p_{i-1}-c+f_{i-2}}\)。容易发现 \(f_i\) 的决策只和 \(f_{i-1}-f{i-2}\) 的值有关。对于 \(k>1\),不妨记录这个值为 \(j\),那么可以枚举上一位填了什么,然后从 \(f_{i-1,j,0/1}\) 转移到 \(f_{i,j,0/1}\)

对于 \(B_{i,j}\) 大的情况。可以证明,对于每一个 \(x\),把 \(B_{i,j}\geq x\) 的看作 \(1\),否则看作 \(0\),所有 \(x\) 的结果之和就是答案。具体证明不太会。

复杂度 \(O(n^3kc)\)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=60,mod=1000000007;int n,c,k;
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
int a[N][N],f[N][N],g[N][N],b[N*N],bt,ans,sm;
int main()
{
    scanf("%d%d%d",&n,&c,&k);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=k;j++) scanf("%d",&a[i][j]),add(sm,a[i][j]),b[++bt]=a[i][j];
    for(int i=1;i<n;i++) sm=1ll*sm*k%mod;
    sort(b+1,b+bt+1),bt=unique(b+1,b+bt+1)-b-1;
    for(int t=1;t<=bt;t++)
    {
        int res=0;
        memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
        f[0][0]=1;
        for(int i=1;i<=n;i++)
        {
            int p=0;
            for(int j=1;j<=k;j++) if(a[i][j]>=b[t]) ++p;
            for(int j=0;j<=c;j++)
            {
                int w1=min(j+1,c),w0=max(j-1,0);
                add(f[i][w1],1ll*p*f[i-1][j]%mod),add(f[i][w0],1ll*(k-p)*f[i-1][j]%mod);
                add(g[i][w1],1ll*p*g[i-1][j]%mod),add(g[i][w0],1ll*(k-p)*g[i-1][j]%mod);
                if(j==c) add(g[i][c],1ll*p*f[i-1][j]%mod);
            }
        }
        for(int j=0;j<=c;j++) add(res,g[n][j]);
        add(ans,1ll*res*(b[t]-b[t-1])%mod);
    }
    printf("%d\n",(sm-ans+mod)%mod);
    return 0;
}
posted @ 2023-09-20 18:57  Flying2018  阅读(15)  评论(0)    收藏  举报