A. Immobile Knight
思路:其实不知道是怎么做的,但是找规律做的,比如(5,5)以上或者(1,1)都能走到,所以输出自己,另外的情况就会到不了,所以输出(n-1, m-1)
代码:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;cin>>t;
while(t--){
int n,m;cin>>n>>m;
if(n>=5&&m>=5||n==1||m==1)cout<<n<<" "<<m<<endl;
else cout<<n-1<<" "<<m-1<<endl;
}
return 0;
}
B. Array Recovery
很模拟的一道题,注意a[i]=0的情况
代码:
#include<bits/stdc++.h>
using namespace std;
int sum[103]={0};
int a[103];
int main()
{
int t;cin>>t;
while(t--){
memset(sum,0,sizeof(sum)); memset(a,0,sizeof(a));
int n;cin>>n;bool fla=0;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+a[i];
if(i!=1&&sum[i-1]>=a[i]&&a[i]!=0){fla=1;cout<<-1;break;}
}
if(!fla)for(int i=1;i<=n;i++)cout<<sum[i]<<" ";cout<<endl;
}
return 0;
}
C. Card Game
题意:A和B手中有1~n的卡,每人拿n/2张卡。两人轮流出卡,如果有人出的卡对方出不了比他大的卡,那么对方输了
思路:
1. A有最大的卡,A胜利,此时A的卡的种类数为
2.A没有最大的卡,A有次大的卡。这时A出了次大的卡,B出了最大的卡,安全度过。
2.1 A有次次大的卡,如果没有就输了
2.1.1 A有次次次大卡,这时无论B出了什么,A都能赢,此时A的卡的种类数为 (减3是因为有三张卡已经固定了,分别是次大卡和次次大卡)
2.1.2 A没有次次次打卡,B有次次次大卡,于是又是平安度过,轮回到1,此时n=n-4
代码:
#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
const int mod=998244353;
long long binpow(long long a, long long b, long long m) {
a %= m;
long long res = 1;
while (b > 0) {
if (b & 1) res = (__int128)res * a % m;
a = (__int128)a * a % m;
b >>= 1;
}
return res;
}
ll C(ll n,ll m){
ll shang=1,xia=1;
for(int i=1;i<=m;i++){
shang=shang*(n-m+i)%mod;
shang=shang*binpow(i,mod-2,mod)%mod;
}
return shang;
}
signed main()
{
int T;cin>>T;
while(T--){
int n;cin>>n;int ans=0;
int i=0;
while(n-4*i>0){
if(n-1-4*i>0)ans=(ans+C(n-1-4*i,(n-4*i)/2-1)%mod)%mod;
if(n-4*(i+1)>0)ans=(ans+C(n-4*(i+1),(n-4*i)/2-3)%mod)%mod;
i++;
}
cout<<ans<<" "<<(C(n,n/2)-ans-1+mod)%mod<<" "<<1<<endl;
}
return 0;
}
D. Reset K Edges
给定一棵根节点为 1 的树。可以执行下面的操作不超过 k 次:
选择一条边 ( v , u ) 删掉,v 是 u 的父节点;
增加一条边 ( 1 , u ) ,即让 u 与根节点相连。
根节点深度为 0,求树的高度最小为多少?
思路:可以用二分做,二分树的深度。
那么关于怎么check。
一开始想遍历n,在超过mid的地方就把它接在1的下面,但是假如3是2的结点,那么就错了,所以应该用层序遍历;
但是问题又来了,层序遍历从高往下的话,假如我改完了一次,那新改动的部分还要再重复会被改动;
所以答案中 树是从下到上的,从最底层向上,假如有一个点超出了二分深度,那么就把它移到上面来,这样就不会重复处理了。
在check的代码层面上:
预处理:先把每个点(也可以除了1)的cnt都记为1,因为除了1的每个点都会有一个父亲,(如果父亲是1的话cnt就是当前子树的深度),所以深度都设为1,有了儿子后,自身的深度还会加1。
判断:
1 若x是1,那么不适用这个cnt的设计,所以参加判断的一定不是1。
2 若cnt大于等于二分的mid且父亲不是1,那么把它移走,cnt设为0,代表这一部分已经完全不用理了,即使是x的父亲求深度的时候也不会有任何影响。
3 若cnt等于二分的mid且父亲是1,没影响。
4 若cnt大于二分的mid且父亲是1,不存在这个情况,因为在等于的时候情况2就已经判断过了。
代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> g[200005];
int cnt[200005],f[200005];
struct node{
int id;
int h;
}a[200005];
int n,k;
bool cmp(node &a,node &b){return a.h>b.h;}
void init()
{
a[1].id=1;a[1].h=1;
for(int i=1;i<=n;i++){
for(auto x:g[i]){
a[x].h=a[i].h+1;a[x].id=x;
}
}
}
bool check(int mid)
{
for(int i=1;i<=n;i++)cnt[i]=1;
int cou=0;
for(int i=1;i<=n;i++){
int x=a[i].id;
for(auto q:g[x]){cnt[x]=max(cnt[x],cnt[q]+1);}
if(cnt[x]>=mid&&x!=1&&f[x]!=1){
cou++;cnt[x]=0;//这样父节点遍历的时候就还是1了
}
if(cou>k)return 0;
}
return 1;
}
int main()
{
int T;cin>>T;
while(T--){
cin>>n>>k;
for(int i=1;i<=n;i++) g[i].clear();
for(int i=2;i<=n;i++){
int fa;cin>>fa;g[fa].push_back(i);f[i]=fa;
}
init();
sort(a+1,a+1+n,cmp);
int l=1,r=n;int ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
cout<<ans<<'\n';
}
return 0;
}