ARC225
赛时写了 A~D,然后第二天早上发现号没了。被 dq 了。。。尝试了申诉,但是已经准备换号了。。
一场比赛四个结论题如何评价。
A
发现限制等价于对于点 \(1,2\):
- \(\vert x_1-x_2\vert=1\),则 \(\vert y_1-y_2\vert\geq2\)。
- \(\vert x_1-x_2\vert\geq2\),则 \(\vert y_1-y_2\vert=1\)。
\(y_i\) 的取值仅仅与 \(x_i\) 有关,考虑构造一组颜色映射 \(x_i\rightarrow y_i\)。尝试一下即可构造得到:
\[1\rightarrow2\\
2\rightarrow4\\
3\rightarrow1\\
4\rightarrow3
\]
回忆代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=500,f[5]={0,2,4,1,3};
int n,x[N+1][N+1];
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>x[i][j];
cout<<f[x[i][j]]<<' ';
}
cout<<'\n';
}
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}
B
显然有用的就是若干段连续的 \(1\)。
手玩发现:
- \(1\):先手必胜。
- \(2\):后手必胜。
- \(3\):先手必胜。
- \(4\):先手必胜。
然后猜测只有连续段的长度为 \(2\) 时,才是后手必胜,否则先手必胜。
因为可以三个三个断开,全部变成长度为 \(2\) 的段,所以得证。
回忆代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=2e5;
int n,a[N+1];
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
vector<int>p;
int cnt=0;
for(int i=1;i<=n;i++){
if(a[i]){
cnt++;
}else{
p.push_back(cnt);
cnt=0;
}
}
p.push_back(cnt);
bool ans=false;
for(int x:p){
if(x!=2&&x!=0){
ans=true;
break;
}
}
if(ans){
cout<<"Alice\n";
}else{
cout<<"Bob\n";
}
}
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}
C
假设答案已知,那么把剩下的 \(0\) 边加入后,生成树仍然在这张图里。然后再考虑删掉多余的 \(0\) 边。说废话。
因此肯定会有一些 \(1\) 边是必须的,去掉之后无论加多少 \(0\) 边都无法形成生成树。先跑一遍最小生成树得到这些 \(1\) 边,就确定了生成树里的部分 \(1\) 边。
之后随便加 \(1\) 边直到加满 \(k\) 条,再加入 \(0\) 边使其成为生成树即可。
回忆代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
#include<unordered_set>
using namespace std;
constexpr const int N=2e5,M=2e5;
struct edge{
int u,v,w,id;
}g[M+1];
int n,m,k;
struct dsu{
int f[N+1],size[N+1];
int find(int x){
if(f[x]!=x){
return f[x]=find(f[x]);
}
return x;
}
void merge(int x,int y){
x=find(x),y=find(y);
if(size[x]<size[y]){
f[x]=y;
size[y]+=size[x];
}else{
f[y]=x;
size[y]+=size[x];
}
}
void build(int n){
for(int i=1;i<=n;i++){
f[i]=i;
}
}
}dsu;
vector<int> Kruskal1(){
sort(g+1,g+m+1,[](edge a,edge b){
return a.w<b.w;
});
dsu.build(n);
vector<int>ans;
for(int i=1;i<=m;i++){
auto [u,v,w,id]=g[i];
if(dsu.find(u)==dsu.find(v)){
continue;
}
dsu.merge(u,v);
if(w){
ans.push_back(id);
}
}
return ans;
}
void Kruskal2(vector<int>&ans){
sort(g+1,g+m+1,[](edge a,edge b){
return a.w>b.w;
});
dsu.build(n);
unordered_set<int>s;
for(int i:ans){
s.insert(i);
}
for(int i=1;i<=m;i++){
auto [u,v,w,id]=g[i];
if(s.count(id)){
dsu.merge(u,v);
}
}
k-=ans.size();
if(k<0){
ans={-1};
}
for(int i=1;i<=m;i++){
auto [u,v,w,id]=g[i];
if(dsu.find(u)==dsu.find(v)){
continue;
}
if(w){
if(k>0){
k--;
dsu.merge(u,v);
ans.push_back(id);
}
}else{
dsu.merge(u,v);
ans.push_back(id);
}
}
if(k!=0){
ans={-1};
}
}
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;
cin>>T;
while(T--){
cin>>n>>m>>k;
for(int i=1;i<=m;i++){
cin>>g[i].u>>g[i].v>>g[i].w;
g[i].id=i;
}
vector<int>ans=Kruskal1();
Kruskal2(ans);
for(int i:ans){
cout<<i<<' ';
}
cout<<'\n';
}
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}
D
发现交换 \(i,j\) 的贡献为 \(\vert i-j\vert\),因此等价于两两交换的贡献。
要把 \(a_i\) 换到 \(i\) 的位置,操作次数为 \(\vert a_i-i\vert\)。每个点都会计算两次贡献,所以最终答案为:
\[\dfrac12\sum_{i=1}^n\vert a_i-i\vert
\]
回忆代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
typedef long long ll;
constexpr const int N=5e5;
int n,a[N+1];
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
ll ans=0;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]!=i){
ans+=abs(a[i]-i);
}
}
ans/=2;
cout<<ans<<'\n';
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}
E
设逆序对个数为 \(\operatorname{inv}(a)\),则答案为:
\[\sum_{i=1}^n\vert a_i-i\vert-\operatorname{inv}(a)
\]
不会证。
赛后代码
//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
typedef long long ll;
constexpr const int N=5e5;
int n,a[N+1];
struct bit{
int t[N+1];
int lowbit(int x){
return x&-x;
}
void add(int x,int k){
while(x){
t[x]+=k;
x-=lowbit(x);
}
}
int query(int x){
int ans=0;
while(x<=N){
ans+=t[x];
x+=lowbit(x);
}
return ans;
}
}t;
int main(){
/*freopen("test.in","r",stdin);
freopen("test.out","w",stdout);*/
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n;
ll ans=0;
for(int i=1;i<=n;i++){
cin>>a[i];
if(a[i]!=i){
ans+=abs(a[i]-i);
}
}
for(int i=1;i<=n;i++){
ans-=t.query(a[i]);
t.add(a[i],1);
}
cout<<ans<<'\n';
cout.flush();
/*fclose(stdin);
fclose(stdout);*/
return 0;
}

浙公网安备 33010602011771号