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\) 的数字填进去。然后不存在任意一个数是行里的最小值也是列里的最大值。
赛时思路 & 正解
考试的时候想到从容斥定理的角度考虑,因为如果正着填的话会遇到很多限制条件,如果反着填的话限制条件比较多。
既然反着做,那么把行里的最小值和列里的最大值叫做坏的元素。
这里给出一个结论:
只可能有一个坏的元素
为什么,非常的简单,但是昨天太拉了,没有想着去证明。
明显有两个方格矛盾了。
那怎么推柿子呢?
明显发现,每个格子都是等价的,所以这里假设第一个格子就是坏的元素。这里对答案的乘法贡献是 \(n^2\)
假如说第一个格子填的是 \(x\) ,那么这样的话行里面可以填的数字数量就是 \(\dbinom{n^2-x}{n-1}\) ,列里可以填的数字数量就是 \(\dbinom{x-1}{n-1}\) 。然后行里面可以交换位置,列里面可以交换位置。先把到目前柿子列出来 :
不要让战斗停下来,我们还少了一点东西,就是除了我们填完的这一行一列我们还有一些东西可以交换,就是剩下的。他们对答案的贡献是 \((n^2-2*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 了。
-
对 \(X\) 这个人
\([0,X-1]\) 坏状态。
\([X,X+Y-1]\) 好状态,这样都可以一步到达对方的坏状态
\([X+Y,2X+Y-1]\) 坏状态。
\([2X+Y,2X+2Y-1]\) 好状态。 -
对 \(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]\) 的状态呢?那么好像这是一个坏状态。因为这样操作只能让所有的东西变成坏状态了。这个出大问题,因为之前讨论的坏状态是我动不了,但是对方说不定能动,但是这个我可以操作一下后面那个, 前面那个坏状态对于对方来说不一定是坏状态。
怎么办?直观的感觉好像是一个人的每次拿的数更小更优一点,现在我们就这样讨论一下
-
如果 \(X>Y\) 。
我现在只有唯一的一种操作,就是把一个 \([0,X-1]\) 和一个 \([0,Y-1]\) 的状态丢给他。那么第二个对他来说是坏状态,而第一个是好状态。如果第一个是 \([0,Y-1]\) 的状态,那么先手必胜,如果是一个 \([Y,X-1]\) 的状态,那么后手必胜。 -
如果 \(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.