牛客2025多校 R2
牛客2025多校 R2
I:
题目大意:
void solve(){
LL x,y;
cin>>x>>y;
if (x==1||y==1) cout<<-1<<endl;
else cout<<1<<endl;
}
当 \(k=1,(x,y)>1\) 时,\(H(x)=H(y)=1\) 显然成立,如果存在 \(x=1\) 那么 \(H(x)=0\) ,又因为 \(x\ne y\) ,所以当且仅当 \(x,y\) 中不存在 \(1\) 时,\(k=1\) 为特解,否则无解
B:
题目大意:
void solve(){
int n;
cin>>n;
vector<LL> a(n);
for (auto &x:a) cin>>x;
if (n>60) cout<<"NO"<<endl;
else{
for (int i=0;i<n;i++){
for (int j=i+1;j<n;j++){
if ((a[i]^a[j])<a[i]||(a[i]^a[j])<a[j]){
cout<<"NO"<<endl;
return;
}
}
}
cout<<"YES"<<endl;
}
}
当 \(n>\log_2(10^{18})\) 时,显然数组中存在某一位上有重复的 \(1\) ,异或后一定比原来两个数小,那么之间输出 NO
否则,暴力枚举一下 \(n\le \log_2(10^{18})\) 的情况,时间复杂度很低
A:
题目大意:
const LL mod= 998244353;
LL ksm(LL a,LL b,LL p){
LL res=1;
while (b){
if (b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
void solve(){
int n;
cin>>n;
vector<int> a(n);
for (int i=0;i<n;i++) cin>>a[i];
int k=count(a.begin(),a.end(),-1);
LL ans=0;
if (a[0]==1) ans+=ksm(2,k,mod);
else if (a[0]==-1) ans+=ksm(2,k-1,mod);
for (int i=1;i<n;i++){
LL sum=0;
if (a[i-1]==0){
if (a[i]==1) sum=ksm(2,k,mod);
else if (a[i]==-1) sum=ksm(2,k-1,mod);
}else if (a[i-1]==-1){
if (a[i]==1) sum=ksm(2,k-1,mod);
else if (a[i]==-1) sum=ksm(2,k-2,mod);
}
ans=(ans+sum)%mod;
}
cout<<ans<<endl;
}
只有出现这样的情况时被视为过了一天:\([0,1,0]\) ,在给定的数组前加上一个 \(0\) ,那么判断条件就能被简化为 \([0,1]\) 段
当前枚举的 \([a_{i-1},a_{i}]=[0,1]\) 时,显然其他地方的 \(-1\) 取任意值都不影响这一段对答案的贡献,所以这一段的贡献为 \(2^k\)
\([a_{i-1},a_i]=[0,-1]\) 时,只有当 \(a_i=1\) 时有贡献,其余地方的 \(-1\) 任意填,那么贡献为 \(2^{k-1}\)
同理可以推出 \([a_{i-1},a_i]=[-1,1],[-1,-1]\) 的情况,最后累加总贡献即可
F:
题目大意:
void solve(){
int n,t;
cin>>n>>t;
string a;
cin>>a;
int pre=0,suf=0;
for (int i=0;i<a.size();i++)
if (a[i]=='0') pre++;
else break;
for (int i=a.size()-1;i>=0;i--)
if (a[i]=='0') suf++;
else break;
vector<int> x;
x.push_back(pre+suf);
int st=0,cnt=0;
for (int i=pre;i<a.size()-suf;i++){
if (a[i]=='1'&&st==0){
st=1;
cnt=0;
}else if (a[i]=='1'&&st==1){
x.push_back(cnt);
st=0;
i--;
}
if (a[i]=='0'&&st==1) cnt++;
}
sort(x.begin(),x.end());
int sum=max(0,x.back()-1-t);
if (sum==0){
cout<<0<<endl;
return;
}
for (int i=0;i<x.size()-1;i++)
sum+=max(0,x[i]-2*t);
cout<<sum<<endl;
}
火的蔓延可以看作在环上的点向两侧扩张的情况,记录下每一段连续的 \(0\) ,这些子段构成了未着火的区域
因为我们有一次建立隔离带的机会,那么显然有贪心的想法是让拥有更多森林的部分不被点燃更多
对于一个未着火区域,在没有被建立隔离带的情况下每一时刻会损失掉 \(2t\) 的部分,如果一开始建立了隔离带,那么只会损失 \(t\) 的部分
所以对子段按照长度进行排序后,计算剩余的部分即可
L:
题目大意:
const int N=5e5+10;
const int mod=998244353;
int e[N];
bool vis[N];
int siz[N],idx;
void dfs(int x){
if (vis[x]==1) return;
vis[x]=1;
dfs(e[x]);
siz[idx]++;
}
LL ksm(LL a,LL b,LL p){
LL res=1;
while (b){
if (b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
void init(int n){
for (int i=0;i<=n;i++)
siz[i]=vis[i]=e[i]=0;
idx=0;
}
void solve(){
int n;
cin>>n;
for (int i=1;i<=n;i++)
cin>>e[i];
for (int i=1;i<=n;i++){
if (!vis[i]){
idx++;
dfs(i);
}
}
int odd=0,cnt=0;
for (int i=1;i<=idx;i++){
if (siz[i]%2) odd++;
else if (siz[i]%2==0&&siz[i]!=2) cnt++;
}
if (odd!=0&&odd!=2){
cout<<0<<endl;
init(n);
return;
}
if (odd){
LL ans=1;
for (int i=1;i<=idx;i++){
if (siz[i]%2) ans=ans*siz[i]%mod;
else if (siz[i]!=2)ans=ans*2%mod;
}
cout<<ans<<endl;
}else{
LL ans=0;
for (int i=1;i<=idx;i++){
if (siz[i]!=2)
ans=(ans+1ll*siz[i]*siz[i]%mod*ksm(4,mod-2,mod)%mod*ksm(2,cnt-1,mod))%mod;
else
ans=(ans+ksm(2,cnt,mod))%mod;
}
cout<<ans<<endl;
}
init(n);
}
题目给定的序列可以构成一张图,且每个点有且仅有一个出度和一个入度,构成多个环
对这张图做一遍 DFS ,可以将搜索到的环分类为奇环和偶环,因为我们要删去两个节点,且删去后还能形成 \(\frac{n}{2}-1\) 个配对
那么奇环至多存在两个(包括自环点),当存在奇环时:
选取删去的两个点在不同的奇环(\(a,b\))上,那么存在的方案数为 \({\rm{siz}}_a\times {\rm{siz}}_b\) (\({\rm{siz}}\) 表示环上点的数量),剩下的偶环每个偶环只能提供出两种方案,那么总方案数为:
特别的,如果一个偶环的大小为 \(2\) ,那么只能提供一种方案
当不存在奇环时:我们只能够删去一个偶环上的两个点,且必须满足删去这两个点后剩下环上的点能够形成足够的配对
可以计算出对这样的一个偶环 \(x\) ,存在 \(\frac{{\rm{siz}}_x^2}{4}\) 种方案。对于每一个偶环我们都能够考虑删去他上面的两个点,那么总方案数为这些不同的方案相加: