2020 ICPC·小米邀请赛 决赛 M 题 Rikka with Employees
原题链接:牛客
这个构造题实在是太神了!
算法标签:树链剖分,分治
首先题目描述比较欺诈。“放假“可以理解成选择点,然后采访一个员工必须要除了它子树内的点其它全部被选择。
分治经常用来处理这个问题。
这里用到了树链剖分的一个性质:沿着重链剖开,形成的子树一定(leq size/2),也就是最多(log)层递归。
则每次选择重链剖开,然后分治算子树。
代码写起来非常短:
/*
{
######################
# Author #
# Gary #
# 2021 #
######################
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
const int MAXN=100000+20;
int n;
vector<int> g[MAXN];
int siz[MAXN];
void paint(int now){
printf("+%d",now);
for(auto it:g[now]) paint(it);
}
void undo(int now){
rb(i,1,siz[now]){
printf("-");
}
}
void prework(int now){
siz[now]=1;
for(auto it:g[now]) prework(it),siz[now]+=siz[it];
}
void get(int now,vector<int> & smll,vector<int> & hvy_chain){
printf("=%d+%d",now,now);
hvy_chain.PB(now);
int hvy=-INF;
for(auto it:g[now]) check_max(hvy,siz[it]);
for(auto it:g[now]) if(siz[it]==hvy){
hvy=-it;
}
else{
smll.PB(it);
paint(it);
}
if(!g[now].empty())
get(-hvy,smll,hvy_chain);
for(auto it:g[now]) if(it!=-hvy){
undo(it);
}
printf("-");
}
void div(vector<int> v);
void work(int now){
vector<int> sml,chain;
get(now,sml,chain);
for(auto it:chain){
printf("+%d",it);
}
div(sml);
rb(i,1,chain.size()){
printf("-");
}
}
void div(vector<int> v){
if(v.empty()) return;
if(v.size()==1){
work(v[0]);
return ;
}
int totsiz=0;
mp best=II(INF,INF);
for(auto it:v) totsiz+=siz[it];
int tmp=0;
rep(i,v.size()){
tmp+=siz[v[i]];
if(abs(tmp-totsiz/2)<best.FIR){
best=II(abs(tmp-totsiz/2),i);
}
}
vector<int> l,r;
rb(i,0,best.SEC) l.PB(v[i]);
rb(i,best.SEC+1,v.size()-1) r.PB(v[i]);
for(auto it:l) paint(it);
div(r);
for(auto it:l) rep(j,siz[it]) printf("-");
for(auto it:r) paint(it);
div(l);
for(auto it:r) rep(j,siz[it]) printf("-");
}
int main(){
scanf("%d",&n);
rb(i,2,n){
int pi;
scanf("%d",&pi);
g[pi].PB(i);
}
prework(1);
work(1);
printf("!");
return 0;
}
顺便送大家一个checker来检验自己的程序:
/*
{
######################
# Author #
# Gary #
# 2021 #
######################
*/
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
//inline int read(){
// int x=0;
// char ch=getchar();
// while(ch<'0'||ch>'9'){
// ch=getchar();
// }
// while(ch>='0'&&ch<='9'){
// x=(x<<1)+(x<<3)+(ch^48);
// ch=getchar();
// }
// return x;
//}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int n;
vector<int> g[100000+20];
bool holi[100000+20];
bool dfs2(int now,int to){
if(to==now) return 1;
bool ok=holi[now];
for(auto it:g[now]){
ok&=dfs2(it,to);
}
return ok;
}
bool dfs1(int now){
bool ok=!holi[now];
for(auto it:g[now]){
ok&=dfs1(it);
}
return ok;
}
bool check(int now){
return dfs1(now)&dfs2(1,now);
}
int main(){
scanf("%d",&n);
rb(i,2,n){
int pi;
scanf("%d",&pi);
g[pi].PB(i);
}
char c;
stack<int> sta;
int z=0;
while(cin>>c){
if(c=='!') break;
++z;
int a;
if(c=='+'){
cin>>a;
if(holi[a]){
cout<<"Er1"<<endl;
return 0;
}
sta.push(a);
holi[a]=1;
}
if(c=='-'){
if(sta.empty()) {
cout<<"Er2"<<endl;
return 0;
}
int now=sta.top();
holi[now]=0;
sta.pop();
}
if(c=='='){
cin>>a;
if(!check(a)){
cout<<"!"<<a<<endl;
rb(j,1,n) cout<<holi[j];
cout<<endl;
cout<<"Er3"<<endl;
return 0;
}
}
}
cout<<z<<endl;
cout<<"Ac"<<endl;
return 0;
}