AtCoder Grand Contest 048

链接

C. Penguin Skating

题解

容易发现企鹅之间的相对顺序永远不会变,令 \(a_i'=a_i-i,b_i'=b_i-i\),那么可以看做每次让 \(a_i=a_{i+1}\) 或者 \(a_i=a_{i-1}\)

这样最后一定是某一段区间取一个值,然后为了达到这个值需要让某些点先运动一次。具体来说假设当前连续相同 \(b\) 区间是 \([x,y]\)\(a_i\) 值与其相同的区间是 \([l,r]\),那么 \([x,l)\) 区间的企鹅需要运动一次,而 \((r,y]\) 区间的企鹅需要先垫一下。

直接双指针,复杂度 \(O(n)\)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
using namespace std;
int a[N],b[N];
int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]-=i;
    for(int i=1;i<=n;i++) scanf("%d",&b[i]),b[i]-=i;
    m-=n,a[n+1]=m,b[0]=0;
    long long ans=0;
    for(int i=1,p=1,l=0,r=0;i<=n;i=p+1)
    {
        for(p=i;p<n && b[p+1]==b[p];p++);
        while(l<=n+1 && a[l]<b[i]) l++;
        while(r<=n && a[r+1]<=b[i]) r++;
        if(l>r){puts("-1");return 0;}
        ans+=max(l-i,0)+max(p-r,0);
    }
    printf("%lld\n",ans);
    return 0;
}

D. Pocky Game

题解

很妙的题。

首先容易证明,当左方先手时,最左边一堆的石子一定是越多越有利,右方同理。

考虑 dp,设 \(f_{l,r}/g_{l,r}\) 表示左先/右先情况下,要赢得 \([l,r]\) 区间的胜利,最左/右端石子至少要多少个。

容易发现,每个人的决策实际上只有取 \(1\) 个石子和取完一堆石子。不妨考虑 \(f_{l,r}\) 的转移:

  • 如果 \(g_{l+1,r}>a_r\),即取完 \(a_l\) 后右方必败,那么只要 \(a_l\) 有石子即可获胜。即 \(f_{l,r}=1\)
  • 否则只能一个一个取。考虑此时右方的决策,因为左方想尽可能减少 \(f_{l,r}\),那么右方就应该尽可能增大 \(f_{l,r}\),所以他会一直取一个石子直到 \(g_{l+1,r}\) 为止。因为如果此时继续取,那么左方直接取完整堆石子就获胜了。所以之后右方会取走 \(a_r\) 的所有石子,变成到 \(f_{l,r-1}\)
    所以 \(f_{l,r}=a_r-g_{l+1,r}+f_{l,r-1}+1\)

\(g_{l,r}\) 同理。复杂度 \(O(n^2)\)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 110
using namespace std;
typedef long long ll;
int a[N];ll l[N][N],r[N][N];
int main()
{
    int t;
    scanf("%d",&t);
    while(t --> 0)
    {
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=n-1;i;i--)
        {
            l[i][i+1]=a[i+1]+1,r[i][i+1]=a[i]+1;
            for(int j=i+2;j<=n;j++)
            {
                if(r[i+1][j]>a[j]) l[i][j]=1;
                else l[i][j]=(a[j]-r[i+1][j])+l[i][j-1]+1;
                if(l[i][j-1]>a[i]) r[i][j]=1;
                else r[i][j]=(a[i]-l[i][j-1])+r[i+1][j]+1;
            }
        }
        puts(l[1][n]<=a[1]?"First":"Second");
    }
    return 0;
}

E. Strange Relation

题解

神仙题。

考虑一个 \(x_i\) 的必要条件:如果存在 \(i\) 满足 \(A_i+Tx_i\in(A_1-T,A_1]\),那么找到满足该条件最靠左的点,将它的 \(x_i\) 增加 \(1\),然后调整它后面的权值。容易证明这样总是合法的。

如果不存在这样的值,那么考虑将 \(A_1\) 删去,将所有 \(>A_1\)\(x_i\)\(1\),容易发现这样得到的 \(x'\) 是符合要求的。可以证明,\(\{x_i\}\) 字典序最大当且仅当 \(\{x_i'\}\) 的字典序最大,而 \(\{x_i'\}\) 的处理方式可以递归得到。最后如果 \(A\) 只剩一个元素,显然 \(x_1\)\(0\)。这样反向构造上来,得到的 \(\{x_i\}\) 一定是字典序最小的。

考虑 dp。对每个位置的每个值单独考虑 \(x_i=j\) 的方案数。具体来说考虑从后往前 dp,容易发现无论怎么取值最后总是存在合法的构造,所以只需要记录当前 \(x_i\) 的值。如果往前枚举的值 \(l\) 满足 \(A_l-T<A_i+Tx_i\),那么让 \(x_i\) 加一。

复杂度 \(O(n^3k^2)\)

代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=60,mod=1000000007;
int a[N][N],f[N][N];
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
int main()
{
    int n,k,T;scanf("%d%d%d",&n,&k,&T);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=k;j++) scanf("%d",&a[i][j]);
    for(int i=1;i<=n;i++)
    {
        int s=1,ans=0;
        for(int j=i+1;j<=n;j++) s=1ll*s*k%mod;
        for(int j=1;j<=k;j++)
        {
            memset(f[i],0,sizeof(f[i]));
            f[i][0]=s;
            for(int l=i-1;l;l--)
            {
                memset(f[l],0,sizeof(f[l]));
                for(int y=0;y<i;y++) if(f[l+1][y])
                    for(int w=1;w<=k;w++)
                    {
                        if(a[l][w]-T<a[i][j]+y*T) add(f[l][y+1],f[l+1][y]);
                        else add(f[l][y],f[l+1][y]);
                    }
            }
            for(int w=0;w<i;w++) add(ans,1ll*f[1][w]*w%mod);
        }
        printf("%d\n",ans);
    }
    return 0;
}

F. 01 Record

不会。

posted @ 2022-06-28 14:18  Flying2018  阅读(32)  评论(0)    收藏  举报