ARC143 题解

太弱小了,仅 A 的一题还是在后辈 ly 的指导下才成功的。\(\sout{我被单调队列了}\)

\(\sout{tnnd,刚才忘记保存了重新写一遍。}\)

T1 Three Integers

题意

有三个数 \(A,B,C\) , 有两种操作

Operation 1:选择个数并 \(-1\)
Operation 2:选择个数并 \(-1\)

使得这三个数全变成 \(0\) ,如果不行请输出 \(-1\)

做时思路

因为肯定 Operation 2 肯定更优一点,所以对于大数据肯定有很多 Operation 2 。然后猜大数据是小数据的 Operation 2 的变化过来的。

但是错了

题解

首先说一个推论:

每次操作肯定操作到最大值。

为什么?思考一下什么时候不会操作到最大值。就是对于 Operation 1 操作最小的两个数。显然不优。

ly 的推断办法:

假如说 \(A ,B, C\) 已经排好顺序了。根据 \(\sout{相对论}\) 只有一种操作,就是选一个数 \(+1\) 这样的话肯定不可能给 \(C\) 加。

代码

#include <bits/stdc++.h>
#define debug puts("I love Newhanser forever!!!!!");
#define pb push_back
using namespace std;
template <typename T>inline void read(T& t){
    t=0; register char ch=getchar(); register int fflag=1;
    while(!('0'<=ch&&ch<='9')){if(ch=='-') fflag=-1;ch=getchar();}
    while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;
}
template <typename T,typename... Args> inline void read(T& t, Args&... args){read(t);read(args...);}
const int MAXN=800;
long long A,B,C,ans;
long long min(long long A,long long B){return (A<B)?A:B;}
int main(){
    read(A,B,C);
    long long a[]={A,B,C};
    sort(a,a+3);
    if(a[0]+a[1]<a[2]) puts("-1");
    else cout<<a[2]<<endl;
    return 0;
}
//Welcome back,Chtholly.

T2 Counting Grids

赛时猜到正解,代码写错了,误以为是错解。

题意

就是一个 \(n\times n\) 的网格,你要把 \(1-n\times n\) 的数字填进去。然后不存在任意一个数是行里的最小值也是列里的最大值。

赛时思路 & 正解

考试的时候想到从容斥定理的角度考虑,因为如果正着填的话会遇到很多限制条件,如果反着填的话限制条件比较多。

既然反着做,那么把行里的最小值和列里的最大值叫做坏的元素。

这里给出一个结论:

只可能有一个坏的元素

为什么,非常的简单,但是昨天太拉了,没有想着去证明。

jVIRln.png

明显有两个方格矛盾了。

那怎么推柿子呢?

明显发现,每个格子都是等价的,所以这里假设第一个格子就是坏的元素。这里对答案的乘法贡献是 \(n^2\)

假如说第一个格子填的是 \(x\) ,那么这样的话行里面可以填的数字数量就是 \(\dbinom{n^2-x}{n-1}\) ,列里可以填的数字数量就是 \(\dbinom{x-1}{n-1}\) 。然后行里面可以交换位置,列里面可以交换位置。先把到目前柿子列出来 :

\[n^2 \times ((n-1)!)^2 \times \sum\limits _{i=1}^{n^2} \dbinom{n^2-i}{n-1} \dbinom{i-1}{n-1} \]

不要让战斗停下来,我们还少了一点东西,就是除了我们填完的这一行一列我们还有一些东西可以交换,就是剩下的。他们对答案的贡献是 \((n^2-2*n+1)!\)

答案就是

\[((n-1)^2)! \times (n!)^2 \times \sum\limits _{i=1}^{n^2} \dbinom{n^2-i}{n-1} \dbinom{i-1}{n-1} \]

\(n^2\)\((n-1)^2\) 合起来了。

因为 \(\dbinom{n}{k} = \frac{n!}{(n-k)!k!}\) 组合数还是比较大的, exgcd 又比较的 ex ,所以这里用欧拉定理求逆元啦。

因为预处理了逆元等等东西,所以总的时间复杂度是 \(\mathbb{O}(n^2)\)

代码

#include <bits/stdc++.h>
#define debug puts("I love Newhanser forever!!!!!");
#define pb push_back
using namespace std;
template <typename T>inline void read(T& t){
    t=0; register char ch=getchar(); register int fflag=1;
    while(!('0'<=ch&&ch<='9')){if(ch=='-') fflag=-1;ch=getchar();}
    while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;
}
template <typename T,typename... Args> inline void read(T& t, Args&... args){read(t);read(args...);}
const int MAXN=250005;
const long long mod=998244353;
typedef long long ll;
ll n,ans=1,A[MAXN],B[MAXN],inv[MAXN],p=1,c=1;
ll fac[MAXN];
ll quick_pow(ll x,ll y){
    ll res=1;
    while(y){
        if(y&1) res=(x*res)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return res;
}
ll C(ll n,ll k){
    if(k==0) return 1;
    if(k>n) return 0;
    return fac[n]*inv[n-k]%mod*inv[k]%mod;
}
int main(){
    read(n);
    fac[0]=inv[0]=1;
    for(int i=1;i<=n*n;++i){
        fac[i]=fac[i-1]*i%mod;
        inv[i]=quick_pow(fac[i],mod-2)%mod;
    }
    p=fac[(n-1)*(n-1)]%mod*fac[n]%mod*fac[n]%mod;
    ans=fac[n*n];
    for(int i=n;i<=n*n-n+1;++i) ans=(ans-p*C(n*n-i,n-1)%mod*C(i-1,n-1)%mod+2*mod)%mod;
    cout<<ans<<endl;
    return 0;
}
//Welcome back,Chtholly.

T3 Piles of Pebbles

题意

现在有 \(n\) 堆东西,现在有两个人玩游戏,直到一个人无法操作的时候他就输了。

只有一种操作:

Operation Only: 选择若干堆( \(\ge 1\) )如果是第一个人,可以拿走 \(X\) 个,如果是第二个人,则可以拿走 \(Y\) 个。

两个人都使用最佳操作,请判断是第一个能拿的人能赢还是第二个能拿的人能赢?

做时思路

其实不算做时思路了,因为看了一眼题解。但是毕竟是英语,理解得费太大功夫,不如自己做一下。

首先定义这个人能赢的状态为好状态,否则为坏状态。很像 Nim 游戏对吧,但是不行,因为两个人的行动不是等价的,所以不是公平组合游戏。

但是好状态和坏状态肯定是能够定义的。

我们先研究一下小状态的情形:如果只有一堆东西。那么好状态有哪些?其实如果我可以通过一步到达对面的坏状态那么就 ok 了。

  1. \(X\) 这个人

    \([0,X-1]\) 坏状态。
    \([X,X+Y-1]\) 好状态,这样都可以一步到达对方的坏状态
    \([X+Y,2X+Y-1]\) 坏状态。
    \([2X+Y,2X+2Y-1]\) 好状态。

  2. \(Y\) 这个人

    \([0,Y-1]\) 坏状态。
    \([Y,X+Y-1]\) 好状态,这样都可以一步到达对方的坏状态
    \([X+Y,X+2Y-1]\) 坏状态。
    \([X+2Y,2X+2Y-1]\) 坏状态。

有点看不清,但是好像有很多 \(X+Y\) 在中间,发现似乎是循环的!所以非常简单,直接对 \(X+Y\) 取模,这样的话,只需要判断第一种情况就 ok 了。

通过归纳,结论就是:

现在的堆数对 \(X+Y\) 取模结果假如为 \(T\) ,如果 \(0\leq T\leq X-1\) 那么第二个人赢,否则第一个人赢。

但是这并不是原来的问题,因为我们现在只是处理一堆的情况,甚至都没有选择的可能性。

现在考虑一下如果有两堆的情况。

两堆似乎出现了选择,但是我们还是用好状态和坏状态来找找规律。

首先肯定还是如果两堆都是 \([0,X-1]\) 范围内的数,那么显然这是一个坏状态。如果一个是 \([0,X-1]\) 范围的数,一个是 \([X,X+Y-1]\) 的状态呢?那么好像这是一个坏状态。因为这样操作只能让所有的东西变成坏状态了。这个出大问题,因为之前讨论的坏状态是我动不了,但是对方说不定能动,但是这个我可以操作一下后面那个, 前面那个坏状态对于对方来说不一定是坏状态。

怎么办?直观的感觉好像是一个人的每次拿的数更小更优一点,现在我们就这样讨论一下

  1. 如果 \(X>Y\)
    我现在只有唯一的一种操作,就是把一个 \([0,X-1]\) 和一个 \([0,Y-1]\) 的状态丢给他。那么第二个对他来说是坏状态,而第一个是好状态。如果第一个是 \([0,Y-1]\) 的状态,那么先手必胜,如果是一个 \([Y,X-1]\) 的状态,那么后手必胜。

  2. 如果 \(X\leq Y\)
    肯定先手赢,因为后手根本动不了这两个对先手来说都是坏状态的状态。

似乎又是先手又比对方小的情况非常好讨论,我们先就这个情况继续讨论下去。

如果现在是两个 \([X,X+Y-1]\) 的情况,那么可以转移到 \(1\) 个或 \(2\) 个的 \([0,Y-1]\) 情况。直接转移两个就完事儿了。

好,现在讨论的就是 \(X>Y\) 的情况了,好像是如果可以把一个对面动不了的情况给到他,先手就能赢。如果不是,那么对方必胜。对方动不了的情况是 \([0,Y-1]\) 那么先手能赢的情况就是 \([X,X+Y-1]\)

哦对了,如果模 \(X+Y\) 都是 \(0\) 的话,那么先手肯定不能赢。

题解

做时做法即正解。

代码

#include <bits/stdc++.h>
#define debug puts("I love Newhanser forever!!!!!");
#define pb push_back
using namespace std;
template <typename T>inline void read(T& t){
    t=0; register char ch=getchar(); register int fflag=1;
    while(!('0'<=ch&&ch<='9')){if(ch=='-') fflag=-1;ch=getchar();}
    while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;
}
template <typename T,typename... Args> inline void read(T& t, Args&... args){read(t);read(args...);}
const int MAXN=200008;
int n,X,Y,a[MAXN];
int main(){
    read(n,X,Y);
    for(int i=1;i<=n;++i) read(a[i]),a[i]%=(X+Y);
    bool Flag=0;
    for(int i=1;i<=n;++i) if(a[i]!=0) Flag=1;
    if(!Flag){
        puts("Second");
        return 0;
    }
    if(X<=Y) puts("First");
    else{
        bool flag=0;
        for(int i=1;i<=n;++i) if(a[i]<X){
            flag=1;
            break;
        }
        if(flag) puts("Second");
        else puts("First");
    }
    return 0;
}
//Welcome back,Chtholly.
posted @ 2022-06-27 20:24  Mercury_City  阅读(93)  评论(0编辑  收藏  举报