ARC171 - sol
感觉难度并不大,但是就是做不起,呜呜呜。
中等题练少了是这样的。
AtCoder Regular Contest 171 - AtCoder
A - No Attacking
There is a chessboard with
rows and columns. Let denote the square at the -th row from the top and the -th column from the left.
You will now place pieces on the board. There are two types of pieces, called rooks and pawns.
A placement of pieces is called a good arrangement when it satisfies the following conditions:
- Each square has zero or one piece placed on it.
- If there is a rook at
, there is no piece at for all where . - If there is a rook at
, there is no piece at for all where . - If there is a pawn at
and , there is no piece at . Is it possible to place all
rooks and pawns on the board in a good arrangement? You are given
test cases; solve each of them.
。
简单题,但是这里注意兵是可以放在第一排的啊。
感觉确实没什么说的,自己推一下就可以了,直接放上代码。
#include <bits/stdc++.h>
using namespace std;
int n,a,b,T;
bool sol(){
if(a>n) return false;
n-=a;
if(n<=a) return b<=n*n;
return b<=a*n+((n-a+1)/2)*n;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>T;
while(T--){
cin>>n>>a>>b;
sol()?cout<<"Yes\n":cout<<"No\n";
}
return 0;
}
B - Chmax
For a permutation
of , we define by the following procedure:
There is a sequence
.
As long as there is an integersuch that , perform the following operation:
- Let
be the smallest integer that satisfies . Then, replace with . Define
as the at the end of this process. (It can be proved that the process terminates after a finite number of steps.) You are given a sequence
of length . How many permutations of satisfy ? Find the count modulo .
。
推一下就可以发现这里面的性质,每一次一定是往大走而不是往小走,
所以这样会构成一些链,而每一条链的结尾都是
容易发现这也反应了一个性质:
而构成了许多链之后,发现可以单独排序的数其实只有链头了,
而可以随便选
于是这就直接做就可以了,时间复杂度是线性的。
其实模拟一下样例就都会了。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
const int N=2e5+5;
const ll mod=998244353;
int n,a[N],cnt=0,p[N],nw=0;
ll ans=1;
vector<int> G[N];
bool chk(){
for(int i=1;i<=n;i++) if(a[i]<i) return true;
return false;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
if(chk()) cout<<0,exit(0);
for(int i=n;i>=1;i--) G[a[i]].pb(i);
for(int i=1;i<=n;i++) if(!G[i].empty()) p[++cnt]=G[i].back();
sort(p+1,p+cnt+1);
for(int i=1,j=0;i<=n;i++){
if(G[i].empty()) continue;
while(j<cnt&&p[j+1]<=i) ++j;
if(G[i][0]!=i) cout<<0,exit(0);
ans=1ll*(j-nw)*ans%mod;++nw;
}
cout<<ans;
return 0;
}
C - Swap on Tree
There is a tree with
vertices numbered to . The -th edge connects vertices and .
Additionally, there arepieces numbered to . Initially, piece is placed on vertex .
You can perform the following operation any number of times, possibly zero:
- Choose one edge. Let vertices
and be the endpoints of the edge, and swap the pieces on vertices and . Then, delete the chosen edge. Let
be the piece on vertex . How many different possible sequences exist when you finish performing the operation? Find the count modulo .
。
乍一看非常像直接 Gym 中的题目,想到了是一个 dp,但后面逐渐想偏了,就不知道什么状态了。/kk
容易发现一个子树内只会有一个数不是内部的,但是那个数是什么并不重要!!!
所以我们没必要去记录那个数是什么,只需要知道 断了多少条边 了,到父亲的边有没有断 即可!!!
于是设
那么初始状态就是:
接着,会发现转移是非常好写的,这里我们令
这里最后的
于是直接这样转移就可以了,最后的答案非常显然。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
const int N=3e3+5;
const ll mod=998244353;
int n;
ll g[N][2],f[N][N][2],dp[N][2];
vector<int> G[N];
ll adc(ll a,ll b){return (a+b>=mod)?a+b-mod:a+b;}
void add(ll &a,ll b){a=adc(a,b);}
void dfs(int u,int fa){
f[u][0][0]=1,f[u][1][1]=(u!=1);
for(auto v:G[u]) if(v!=fa){
dfs(v,u);
for(int i=0;i<=(int)G[u].size();i++)
dp[i][0]=adc(f[u][i][0]*g[v][0]%mod,i?1ll*f[u][i-1][0]*i%mod*g[v][1]%mod:0),
dp[i][1]=adc(f[u][i][1]*g[v][0]%mod,i?1ll*f[u][i-1][1]*i%mod*g[v][1]%mod:0);
for(int i=0;i<=(int)G[u].size();i++)
f[u][i][0]=dp[i][0],f[u][i][1]=dp[i][1];
}
g[u][0]=g[u][1]=0;
for(int i=0;i<=(int)G[u].size();i++)
add(g[u][0],f[u][i][0]),add(g[u][1],f[u][i][1]);
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1,u,v;i<n;i++) cin>>u>>v,G[u].pb(v),G[v].pb(u);
dfs(1,0);
cout<<g[1][0];
return 0;
}
D - Rolling Hash
You are given non-negative integers
and . Here, is prime, and .
For a sequence of non-negative integers, the hash value is defined as follows.
You are given
pairs of integers .
Is there a sequence of non-negative integersof length that satisfies the condition below?
- For all
, the following condition holds:
- Let
be the sequence obtained by taking the -th to the -th elements of . Then, .
。
很典的思路,但是我不知道。(WC2024 的 T3 暴力也是这个思路,但是我也不知道)/fn/fn
对于这种区间问题,我们尝试把它转化到点上面的关系!
于是我们记
如果这个东西
为了满足条件,我们就需要
于是这里我们就可以抽象出图论模型了,每次将
那么我们的目的就是去染色,使得每一条边的 两个端点的颜色不同。
而去判断可不可以,我们不如算所需要的最小颜色数,设状态
那么最后判断
考虑转移,发现其实我们想把一个同色连动块找出来,而这个集合
而一个集合是不是独立集是可以先用
于是对于满足条件的集合
这样的时间复杂度就是
#include <bits/stdc++.h>
using namespace std;
const int N=17,M=(1<<17)+1;
int n,m,P,B,f[M];
bool G[N][N],g[M];
bool val(int s,int i){return s&(1<<i);}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>P>>B>>n>>m;
for(int i=1,l,r;i<=m;i++) cin>>l>>r,G[l-1][r]=G[r][l-1]=1;
if(P>n) cout<<"Yes",exit(0);
const int U=(1<<(n+1))-1;
for(int s=0;s<=U;s++){
g[s]=1;
for(int u=0;u<=n;u++) if(val(s,u))
for(int v=u+1;v<=n;v++) if(val(s,v))
if(G[u][v]){g[s]=0;break;}
}
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int s=1;s<=U;s++)
for(int t=s;t;t=(t-1)&s) if(g[t]) f[s]=min(f[s],f[s^t]+1);
cout<<(f[U]<=P?"Yes":"No");
return 0;
}
E - Rookhopper's Tour
Problem Statement
There is a grid with
rows and columns. Let denote the cell at the -th row from the top and the -th column from the left. Additionally, there is one black stone and white stones.
You will play a single-player game using these items.Here are the rules. Initially, you place the black stone at
. Then, you place each of the white stones on some cell of the grid. Here:
- You cannot place a white stone at
. - You can place at most one white stone per row.
- You can place at most one white stone per column.
Then, you will perform the following operation until you cannot do so:
- Assume the black stone is at
. Perform one of the four operations below:
- If there is a white stone at
where , remove that white stone and move the black stone to . - If there is a white stone at
where , remove that white stone and move the black stone to . - If there is a white stone at
where , remove that white stone and move the black stone to . - If there is a white stone at
where , remove that white stone and move the black stone to .
- Here, if the cell to which the black stone is to be moved does not exist, such a move cannot be made.
The following figure illustrates an example. Here,
B
reprfesents the black stone,W
represents a white stone,.
represents an empty cell, andO
represents a cell to which the black stone can be moved...O... ..W... ...... ...... ..B.WO ......
You win the game if all of the following conditions are satisfied when you finish performing the operation. Otherwise, you lose.
- All white stones have been removed from the grid.
- The black stone is placed at
. In how many initial configurations of the
white stones can you win the game by optimally performing the operation? Find the count modulo .
。
感觉非常有意思的题目,一眼会感觉到有很多性质,但是又不太能具体出来。/kk
首先容易发现走的路线一定只有两种:
- 沿着行
沿着列 沿着行 沿着列 - 沿着列
沿着行 沿着列 沿着行
于是容易发现,这里我们要求
而会发现,我们的这两种路线不会同时成立,于是我们可以分开来讨论。
证明感觉可以感性理解,因为如果同时成立,那么我们每一次跳一定只跳两格,这与定义中的每行每列只能一个不符合,
而又一定不会存在一种路径可以正反各走一次。
所以我们只需要先考虑第一种情况,第二种是同理的(最终答案其实乘个
这时候我们考虑把每一次变化用数学语言表示出来了,
我们假设他先往下走,那么原来在
这时我们再假设它往右边跳,要去跳
于是就有了一个性质:发现这里的两个石头是分别在第
而类似分析一下,发现列也是同样有这样的性质,就是所有石子一定两两相邻!!!
有了这个性质似乎就可以直接用组合数计算了,枚举一下你是从左边来的还是从右边来的就可以了,
而行列其实本质相同,互相独立且互不干扰,
由于我们最后要回到
于是其实排除掉分类讨论,其实随便排的只有
所以具体实现中,我们枚举一下当前这种情况它左边有多少组,右边有多少组,直接组合计数就可以了。
这样就做完了,实现出来是
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5,mod=998244353;
int n,m,A,B,fac[N],inv[N];
int qpow(int a,int b){
int res=1;
while(b){if(b&1) res=1ll*res*a%mod;a=1ll*a*a%mod;b>>=1;}
return res;
}
void init(){
fac[0]=1;
for(int i=1;i<N;i++) fac[i]=1ll*fac[i-1]*i%mod;
inv[N-1]=qpow(fac[N-1],mod-2);
for(int i=N-1;i>=1;i--) inv[i-1]=1ll*inv[i]*i%mod;
}
int binom(int n,int m){
if(n<m||m<0) return 0;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int sol(int L,int R){
int res=0;
for(int l=0,r;l<=m;l++){
r=m-l;
if(2*l>L||2*r>R) continue;
if(l&&2*l+1<=L) res=(res+1ll*binom(L-l-1,l)*binom(R-r,r)%mod*l%mod)%mod;
if(r&&2*r+1<=R) res=(res+1ll*binom(L-l,l)*binom(R-r-1,r)%mod*r%mod)%mod;
}
return 1ll*res*fac[m-1]%mod;
}
int main(){
cin>>n>>m>>A>>B;init();
if((m&1)||m==2) cout<<0,exit(0);
m=(m>>1)-1;
cout<<(1ll*sol(A-1,n-A)*sol(B-1,n-B)%mod*2ll%mod);
return 0;
}
F 题相当抽象,先咕了。
Conclusion
- 做题时注意无解情况的判断,WA 部分点很有可能就是这种情况。(B)
- dp 题目一定要考虑那些东西是 重要的,分清主次才能更好地写出 dp 状态。(C)
- 有关一个 区间 的关系我们可以通过类似于差分的方法转化成 点与点 之间的关系,从而能够更好的建图。(D)
- 判断是否在一定限制内完成,我们可以转化成求 完成的最小次数。(D)
- 问方案数的题目要尝试把抽象的性质通过一步一步的数学语言表示转化成可以简单易懂的性质,这样就可以用组合数解决。(E)
本文作者:H_W_Y
本文链接:https://www.cnblogs.com/H-W-Y/p/18011821/arc171
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步