【题解】ARC156 A-C
神仙的 ARC。
A.Non-Adjacent Flip
题目分析:
就是一个分类讨论,主要就是讨论一下只有 \(2\) 个 \(1\) 的情况。
自己手模一下应该很好理解。
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+5;
vector<int> v;
char s[N];
int main(){
int T;scanf("%d",&T);
while(T--){
int n;scanf("%d",&n);
scanf("%s",s+1);
for(int i=1; i<=n; i++) if(s[i] == '1') v.push_back(i);
int m = v.size();
if(m & 1) printf("-1\n");
else if(m == 2 && v[0] + 1 == v[1]){
if(n <= 3) printf("-1\n");
else if(n >= 5) printf("2\n");
else{
if(s[1] == '1' || s[4] == '1') printf("2\n");
else printf("3\n");
}
}
else printf("%d\n",m/2);
v.clear();
}
return 0;
}
B.Mex on Blackboard
题目分析:
我们其实可以考虑从小到大依次决定每一个数选择多少,也就是考虑完了前 \(i\) 个数选的情况再考虑 \(i+1\) 这个数,这样显然是没影响的。
这个其实就启发我们直接枚举选择的最大的数是多少,假设为 \(i\) 且为了使得序列存在 \([1,i]\) 还剩余 \(k\) 次操作,那么答案其实就是:
\[\binom{k+i}{i}
\]
但是会出现一个问题,假设我们现在选择的数为 \(x\) 但是序列中原本就存在 \(x+1\),这样就会算重最大为 \(x\) 的情况。但是这个也不用容斥,出现这种情况直接跳过 \(x\) 就好了。
代码:
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MX = 1e6+5;
const int MOD = 998244353;
int cnt[MX*2],a[MX],fac[MX*2+5],inv[MX*2+5];
int mod(int x){
return x % MOD;
}
int power(int a,int b){
int res = 1;
while(b){
if(b & 1) res = mod(res * a);
a = mod(a * a);
b >>= 1;
}
return res;
}
int binom(int n,int m){
return mod(fac[n] * mod(inv[n - m] * inv[m]));
}
signed main(){
int n,k;scanf("%lld%lld",&n,&k);
for(int i=1; i<=n; i++) scanf("%lld",&a[i]),cnt[a[i]]++;
fac[0] = 1;
for(int i=1; i<=MX*2; i++) fac[i] = mod(fac[i-1] * i);
inv[MX*2] = power(fac[MX*2],MOD-2);
for(int i=MX*2-1; i>=0; i--) inv[i] = mod(inv[i+1] * (i+1));
int ans = 0;
for(int i=0; i<=MX; i++){
if(!cnt[i]) k--;
if(k <= 0){
ans++;
break;
}
while(cnt[i+1]) i++;
ans = mod(ans + binom(k+i,i));
}
printf("%lld\n",ans);
return 0;
}
C.Tree and LCS
题目分析:
又是一道我不会的神仙题呢。
考虑对于任意一个排列其最小的权值都至少为 \(1\),因为我们可以选择路径 \((i,P_k)\) 其中满足 \(P_k = i\)。
我们就是考虑每次选择两个一度点 \(x,y\) 然后令 \(P_x = y,P_y = x\),并将 \(x,y\) 从图上删去,最后如果有剩下的点就直接令剩下的点 \(P_i = i\)。
可以证明的是这样的操作对于路径 \((x,y)\) 一定是不劣的,而对于包含 \(x,y\) 的路径,想使得 \(x,y\) 产生大于 \(1\) 的贡献也是不可能的,而 \(x,y\) 只有可能是相互配对才可能产生大于 \(1\) 的贡献,而相互配对也不行,所以只要包含 \(x,y\) 那么 \(x,y\) 就一定不会产生大于 \(1\) 的贡献。
代码:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
struct edge{
int nxt,to;
edge(){}
edge(int _nxt,int _to){
nxt = _nxt,to = _to;
}
}e[2 * N];
int head[N],deg[N],cnt,ans[N];
void add_edge(int from,int to){
e[++cnt] = edge(head[from],to);
head[from] = cnt;
}
int main(){
int n;scanf("%d",&n);
for(int i=1; i<n; i++){
int from,to;scanf("%d%d",&from,&to);
add_edge(from,to);add_edge(to,from);
deg[from]++;deg[to]++;
}
queue<int> q;
for(int i=1; i<=n; i++){
if(deg[i] == 1) q.push(i);
}
while(q.size() > 1){
int x = q.front();q.pop();
int y = q.front();q.pop();
ans[x] = y;ans[y] = x;
for(int i = head[x]; i; i = e[i].nxt){
int to = e[i].to;
deg[to]--;
if(deg[to] == 1) q.push(to);
}
for(int i = head[y]; i; i = e[i].nxt){
int to = e[i].to;
deg[to]--;
if(deg[to] == 1) q.push(to);
}
}
for(int i=1; i<=n; i++) if(!ans[i]) ans[i] = i;
for(int i=1; i<=n; i++) printf("%d ",ans[i]);
return 0;
}