Practice on Codeforces and Atcoder in April
1811D
性质:
证明:数学归纳法:\(F_n·F_{n+1}=F_{n}(F_n+F_{n-1})=F_n^2+F_nF_{n-1}……\)
题目等价于是确定了 \(F_0\) 的位置,是否可以分割出其他。
考虑类似螺旋的分割方式,从大到小分割(\(F_n=F_{n-1}+F_{n-2}\)),每一次贪心地放最大的正方形,以达到缩小问题规模即可。
当然这里利用了对称性,直接迭代实现。
#define int long long
int n,a,b,f[55];
signed main(){
f[0]=f[1]=1;
for(int i=2;i<=45;i++)f[i]=f[i-1]+f[i-2];
int t;cin>>t;
while(t--){
cin>>n>>a>>b;int tag=0;swap(a,b);
for(int i=n;i;--i){
if(a>f[i])a-=f[i];
else if(a>f[i-1])tag=1;
swap(a,b);
}
if(tag)cout<<"No\n";
else cout<<"Yes\n";
}
}
1819B
考的时候想歪了。
题目意思即为给定一些小矩形,这些小矩形是由大矩形每一次进行横切/纵切得到(切为两部分后其中一部分就不动了,矩形不可旋转翻转)
引理1:解的个数最多只有二
证明:小矩形中长和宽的最大值分别对应答案矩形的长宽,且面积一定
这是一个重要的性质,问题化为判定答案的合法性
考虑模拟这个过程,显然不会超时
使用队列进行优化。
设答案矩阵长宽为 \((h,w)\),每一个矩阵的长宽为 \((h_i,w_i)\)
我们现在讨论 \(\exist i\in[1,n],h_i=h\) 的情况
设 \(x=\sum_{i=1}^n[h_i=h]w_i\),则表示这些矩阵都是一次性砍掉的
那么问题就化为判断 \((h,w-x)\) 的合法性,设 \(w'=w-x\)
再设 \(x'=\sum_{i=1}^n[w_i=w']h_i\),则问题化为判断 \((h-x',w')\) 的合法性
容易发现这是一个递归的过程,有解的充要条件是最后某维度化为0,且过程中 \(x>0\)
从这里可以看出:核心在于将求解答案化为判定答案,再以题目所给方式倒推证明
1821D
1770D
题意:
Koxia 和 Mahiru 正在玩一个游戏。游戏使用 \(a,b,c\) 三个长度为 \(n\) 的数组,共进行 \(n\) 轮。
每一轮中,Koxia 先在 \(a_i,b_i,c_i\) 中选择一个数字,Mahiru 再从未选择的两个数字中选择一个。
如果 \(n\) 轮后 Mahiru 选择的数字刚好包含 \(1\) 至 \(n\) 中每个数字各一个(即为 \(1\) 至 \(n\) 的一个排列),则 Koxia 获胜。否则,Mahiru 获胜。
假设双方都采取最优策略,给定 \(a,b\) 数组,求使得 Koxia 必胜的 \(c\) 数组的个数。
题解:
因为两人都足够聪明,所以想赢就只有一种可能——先手可以限制后手怎么走
回到这个问题,如果后手方案确定,当且仅当剩下两个数是一样的,这样后手就会被先手牵着鼻子走
那么进行分类讨论可以确定必要条件:
- \(a_i=b_i\) 此时 \(c_i\) 有 \(n\) 种取法
- \(a_i\neq b_i\) 此时 \(c_i\) 有两种取法,分别对应 \(a_i,b_i\)
但这个条件不够充分。
对于这类或关系的处理,图论建模是一个很好的方法。
我们将一对\(a_i,b_i\)进行连边,问题化为每条边都必须指定端点的其中之一,不重不漏。考虑什么条件下是合法的。
下面我们证明有且仅有环是合法的,我们分连通块来讨论这个问题,显然不同连通块是 不会互相影响的。
先证充分性:按着环跑一圈一定是合法的
再证必要性:
- 假设它是一棵树,则 \(|E|<|V|\),必定存在点连不到(树根),答案变0
- 假设它连通,但 \(|E|>|V|\),则必然会取到重复的,答案直接变0
统计\(|V|>1\)环的个数(并查集即可),记为 \(c\),\(|V|=1\) 环的个数记为\(s\)
\(ans=2^cn^s\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 205050
int f[N],num,a[N],b[N],n,m,ans=1,dfn[N],low[N],dcc,cnt[N],scc[N],tot=1,vis[N],siz[N],V[N];
int get(int x){
return x==f[x]?x:f[x]=get(f[x]);
}
void solve(){
for(int i=1;i<=n;i++){
if(i!=f[i]||V[i])continue;
if(siz[i]==cnt[i]&&vis[i]){
ans=1ll*ans*n%998244353;continue;
}
ans=1ll*ans*(siz[i]==cnt[i])*2%998244353;
}
}
void clear(){
for(int i=1;i<=n;i++)siz[i]=cnt[i]=V[i]=vis[i]=0;
dcc=num=0,tot=ans=1;
}
int main(){
int t;cin>>t;
while(t--){
clear();
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];f[i]=i;siz[i]=1;
}
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=1;i<=n;i++){
int u=get(a[i]),v=get(b[i]);
if(u==v){
if(a[i]==b[i])vis[u]=1;
cnt[v]++;continue;
}
f[u]=v,cnt[v]+=cnt[u]+1,siz[v]+=siz[u],vis[v]|=vis[u];
}
solve();
cout<<ans<<"\n";
}
}