• 2020 ICPC·小米邀请赛 决赛 M 题 Rikka with Employees 题解


    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;
    }
    
  • 相关阅读:
    [JLOI2011]飞行路线 不同的算法,不同的悲伤
    洛谷 [USACO17OPEN]Bovine Genomics G奶牛基因组(金) ———— 1道骗人的二分+trie树(其实是差分算法)
    P3203 [HNOI2010]弹飞绵羊 —— 懒标记?分块?
    tarjan 题目汇总(含解析)
    P1262 间谍网络 (tarjan缩点 水过去)
    #1117. 编码 ( 字典树版 ) 题解分析
    一道并查集的(坑)题:关闭农场closing the farm
    四重解法---P1047 校门外的树
    一道搜索题【2013 noip提高组 DAY2 t3】华容道
    ●BZOJ 1499 [NOI2005]瑰丽华尔兹
  • 原文地址:https://www.cnblogs.com/gary-2005/p/14292925.html
Copyright © 2020-2023  润新知