A. Immobile Knight
给出一个n*m的棋盘,如果存在一个点使得马可以向任何方向走,输出满足条件的任意一点;否则输出棋盘上任意一点。
思路:必须满足长大于3,宽大于2才存在满足条件的点,该种条件下输出(m+1)/2,(n+1)/2,否则任意输出一点。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N=105;
int t;
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin>>t;
while(t--){
int n,m;
std::cin>>n>>m;
int a=std::max(n,m);
int b=std::min(n,m);
if(a>3&&b>2) std::cout<<1<<' '<<1<<'\n';
else std::cout<<(n+1)/2<<' '<<(m+1)/2<<'\n';
}
return 0;
}
B. Array Recovery
给出一个数组a,构造一个数组d,满足d[1]=a[1],d[i]=abs(a[i]-a[i-1]),如果这个数组d是唯一的,构造出这个数组,否则输出-1。
思路:明显考差前缀和。对于a求前缀和数组,对于前缀和数组中的每个元素dif[i],如果它大于a[i+1],那说明下一个数可以是dif[i]+a[i+1],也可以是dif[i]-a[i+1],即不唯一,则输出-1。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N=105;
int t,n;
int a[N],dif[N];
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin>>t;
while(t--){
std::cin>>n;
for(int i=1;i<=n;i++){
std::cin>>a[i];
dif[i]=dif[i-1]+a[i];
}
bool flag=true;
for(int i=1;i<n;i++){
if(dif[i]>=a[i+1]&&a[i+1]){
flag=false;
break;
}
}
if(!flag){
std::cout<<-1<<'\n';
continue;
}
for(int i=1;i<=n;i++){
std::cout<<dif[i]<<" \n"[i==n];
}
}
return 0;
}
C. Card Game
给出偶数n张牌,每张牌上写着1~n中一个数字且不重复,将这些牌分给A和B两人,每一轮先手出牌,下一个人出牌的数字要大于前一个,否则输,每一轮互换先手,问分牌的方案数,使得:A赢,B赢,平局。(平局是指按照游戏规则出牌,最后两人正好出完所有手中牌)
思路:首先平局为1,其他情况必存在谁赢谁输的情况,所以我们可以先计算先手必胜,用全部的情况减去先手必胜和平局的这一场。考虑先手必胜时,这个题可以分成两个一组和四个一组的情况,这里我采用的是四个一组的情况。先考虑有八张牌的时候,对于5 6 7 8四张牌的分配来说,如果最大牌在A手中,则先手必胜,这样的话其他的牌可以随机分开,用组合数计算很容易;如果567在A手中,则只需要再在1~4选择一张牌;如果在5~8四张牌中打成平局,即A拿到67两张牌,那该种情况下先手必胜的方案数等于在四张牌局面下的方案数,即1~4四张牌时。所以我们只需要初始化在2张牌和4张牌时的方案数,递推得到答案。
AC Code:
#include <bits/stdc++.h>
#define int long long
typedef long long ll;
const int mod=998244353;
const int N=105;
int t,n;
int fact[N],infact[N],f[N];
int pmod(int a,int b){
int res=1;
while(b){
if(b&1) res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
void init(){
fact[0]=infact[0]=1;
for(int i=1;i<=N-3;i++){
fact[i]=fact[i-1]*i%mod;
infact[i]=infact[i-1]*pmod(i,mod-2)%mod;
}
}
int C(int n,int x){
return fact[n]*infact[n-x]%mod*infact[x]%mod;
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
init();
f[2]=1,f[4]=3;
for(int i=6;i<=60;i+=2){
f[i]=(C(i-1,i/2)+C(i-4,i/2-3)+f[i-4])%mod;
}
std::cin>>t;
while(t--){
std::cin>>n;
int sum=C(n,n/2);
int a=f[n];
int b=((sum-a-1)%mod+mod)%mod;
int c=1;
std::cout<<a<<' '<<b<<' '<<c<<'\n';
}
return 0;
}
os:一到组合数学就爆炸了,更何况这个还有博弈呜呜呜
D. Reset K Edges
给出一棵树,以1为根节点,可以进行k次操作,每次可以删除一条边,将子节点连到根节点上,问经过k次操作,得到最小的树高是多少。
思路:显然二分(bushi,对于每次操作,一定是删除最深的点最优,所以二分的是最小的树深,可以在每次check时对于mid深度以下的叶节点都删掉。具体见代码。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N=2e5+5;
int t,n,k,idx,cnt;
int e[N<<1],next[N<<1],head[N<<1],fa[N];
void add_edge(int u,int v){
e[idx]=v;
next[idx]=head[u];
head[u]=idx++;
}
int DFS(int u,int father,int mid){
int max=0;
for(int i=head[u];~i;i=next[i]){
int j=e[i];
if(j==father) continue;
max=std::max(max,DFS(j,u,mid));
}
max++;
if(father==1||u==1) return 0;
if(max>=mid){
cnt++;
return 0;
}
else return max;
}
bool check(int mid){
cnt=0;
DFS(1,0,mid);
return cnt<=k;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin>>t;
while(t--){
std::cin>>n>>k;
idx=0;
// memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++){
head[i]=-1;
}
for(int i=2;i<=n;i++){
int p;
std::cin>>p;
fa[i]=p;
add_edge(i,p),add_edge(p,i);
}
int l=1,r=n;
while(l<r){
int mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
std::cout<<l<<'\n';
}
return 0;
}