• 2019-2020 ICPC Asia Hong Kong Regional Contest


    C. Constructing Ranches

    题意:

    一棵树上每个节点有权值(a_i),问你有多少路径满足以下条件:路径经过的每个点权作为边长,可以构成一个多边形。

    解法:

    三条边能构成三角形的条件是两边之和大于第三边,四边形是三边之和大于第四边,以此类推。转化一下条件就是(sum>2*max)
    考虑点分治。对于每个选定的重心,我们假设(sum_i)表示i到重心的路径上的点权和,(mx_i)表示i到重心路径上最大点权。
    那么经过重心x符合条件的一条路径就有(sum_u+sum_v-a_x>2*max(mx_u,mx_v))
    那么我们对当前处理的所有结点按照(mx_i)排序一下,另外一个结点的sum就是定值了,用树状数组可以维护个数。

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    
    const int maxn = 2e5;
    
    ll ans;
    int root,minn,all;
    bool vis[maxn + 11] = {false};
    vector <pair<ll,ll> > v;
    vector <ll> s;
    int a[maxn + 11],siz[maxn + 11],f[maxn + 11];
    int bit[maxn + 11];
    vector <int> edge[maxn + 11];
    
    int lowbit(int x) { return x & (-x); }
    int query(int x) { int ans = 0; for (; x; x -= lowbit(x)) ans += bit[x]; return ans; }
    void update(int x,int val,int n) { for (; x <= n; x += lowbit(x)) bit[x] += val; }
    
    void dfs(int x,int fa) {
    	siz[x] = 1; f[x] = 0;
    	for (auto v : edge[x]) {
    		if (v == fa || vis[v]) continue;
    		dfs(v , x);
    		siz[x] += siz[v];
    		f[x] = max(f[x] , siz[v]);
    	}
    	f[x] = max(f[x] , all - siz[x]);
    	if (f[x] < minn) { minn = f[x]; root = x; }
    }
    
    void find(int x,int fa,ll sum,int mx) {
    	v.push_back({max(mx , a[x]) , sum + a[x]});
    	for (auto v : edge[x]) {
    		if (v == fa || vis[v]) continue;
    		find(v , x , sum + a[x] , max(mx , a[x]));
    	}
    }
    
    ll calc(int x,ll val) {
    	find(x , 0 , val , val);
    	sort(v.begin() , v.end());
    	ll cnt = 0;
    	ll add = val ? val : a[x];
    	for (auto pi : v) s.push_back(pi.second);
    	sort(s.begin() , s.end());
    	s.erase(unique(s.begin() , s.end()) , s.end());
    	for (int i = 0; i < v.size(); i++) {
    		pair <ll,ll> pi = v[i];
    		int pos = upper_bound(s.begin() , s.end() , 2 * pi.first + add - pi.second) - s.begin();
    		cnt += i - query(pos);
    		pos = lower_bound(s.begin() , s.end() , pi.second) - s.begin() + 1;
    		update(pos , 1 , s.size());
    	}
    	for (auto pi : v) {
    		int pos = lower_bound(s.begin() , s.end() , pi.second) - s.begin() + 1;
    		update(pos , -1 , s.size());
    	}
    	return cnt;
    }
    
    void solve(int x) {
    	vis[x] = true;
    	ans += calc(x , 0); v.clear(); s.clear();
    	for (auto V : edge[x]) {
    		if (vis[V]) continue;
    		ans -= calc(V , a[x]); v.clear(); s.clear();
    	}
    	for (auto v : edge[x]) {
    		if (vis[v]) continue;
    		minn = all = siz[v]; root = 0;
    		dfs(v , x);
    		solve(root);
    	}
    }
    
    int main(){
    	int t;
    	scanf("%d" , &t);
    	while (t--) {
    		int n;
    		scanf("%d" , &n);
    		for (int i = 1; i <= n; i++) scanf("%d" , &a[i]);
    		for (int i = 1; i < n; i++) {
    			int u,v;
    			scanf("%d %d" , &u,&v);
    			edge[u].push_back(v);
    			edge[v].push_back(u);
    		}
    		root = 0; minn = all = n; ans = 0;
    		dfs(1 , 0);
    		solve(root);
    		printf("%lld
    " , ans);
    		for (int i = 1; i <= n; i++) siz[i] = f[i] = 0,vis[i] = false,edge[i].clear();
    	}
    } 
    
    

    H. Hold the Line

    题意:

    一个区间[1,n],两种操作。第一种操作将位置i的值设为x,第二种操作求[l,r]这个区间内与h最相近的权值。操作具有先后顺序,每个位置只能赋值一次。

    解法

    考虑离线做法。每次枚举右端点r=1,2,3...来依次修改,查询操作。线段树维护[a,b]表示h在[a,b]之间的<pos,time>。
    因为位置是从左向右依次插入的,我们考虑(pos_jleq pos_k),如果(time_j>time_k),那么j这个值是没有意义的。因为在同一高度区间内,我们询问时需要的是在某个时间之前插入的且位置处于[l,r]之间的,如果一个值,位置更靠左且时间更靠后,那么这个点显然是无用的。所以我们对于线段树上的每个结点用一个单调队列进行维护即可。

    #include <bits/stdc++.h>
    #define all(x) (x).begin(),(x).end()
    #define lson rt << 1
    #define rson rt << 1 | 1
    using namespace std;
     
    const int inf = 2e9;
    const int maxn = 1e6;
     
    struct Query{
    	int l,h,id;
    };
     
    vector <pair <int,int> > que[4 * maxn + 11];
    int ans[maxn + 11];
    pair <int,int> ins[maxn + 11] = make_pair(0 , 0);
    vector <int> v;
    vector <Query> query[maxn + 11];
     
    void update(int rt,int l,int r,int h,int pos,int time) {
    	if (l > h || r < h) return;
    	while (!que[rt].empty() && que[rt].back().second > time) que[rt].pop_back(); que[rt].push_back({pos , time});
    	if (l == r) return;
    	int mid = (l + r) >> 1;
    	update(lson , l , mid , h , pos , time);
    	update(rson , mid + 1 , r , h , pos , time);
    }
     
    int find_min(int rt,int l,int r,int pos,int time) {
    	if (l == r) return l;
    	int mid = (l + r) >> 1;
    	if (que[rson].empty() || que[rson].back().first < pos) return find_min(lson , l , mid , pos , time);
    	int ind = lower_bound(all(que[rson]) , make_pair(pos , -1)) - que[rson].begin();
    	if (que[rson][ind].second < time) return find_min(rson , mid + 1 , r , pos , time);
    	return find_min(lson , l , mid , pos , time);
    }
     
    int query_min(int rt,int l,int r,int al,int ar,int pos,int time) {
    	if (l > ar || r < al || que[rt].empty()) return 0;
    	if (l >= al && r <= ar) {
    		if (que[rt].back().first < pos) return 0;
    		int ind = lower_bound(all(que[rt]) , make_pair(pos , -1)) - que[rt].begin();
    		if (que[rt][ind].second > time) return 0;
    		return find_min(rt , l , r , pos , time);
    	}
    	int mid = (l + r) >> 1;
    	int ret = query_min(rson , mid + 1 , r , al , ar , pos , time);
    	if (!ret) return query_min(lson , l , mid , al , ar , pos , time);
    	return ret;
    }
     
    int find_max(int rt,int l,int r,int pos,int time) {
    	if (l == r) return l;
    	int mid = (l + r) >> 1;
    	if (que[lson].empty() || que[lson].back().first < pos) return find_max(rson , mid + 1 , r , pos , time);
    	int ind = lower_bound(all(que[lson]) , make_pair(pos , -1)) - que[lson].begin();
    	if (que[lson][ind].second < time) return find_max(lson , l , mid , pos , time);
    	return find_max(rson , mid + 1 , r , pos , time);
    }
     
    int query_max(int rt,int l,int r,int al,int ar,int pos,int time) {
    	if (l > ar || r < al || que[rt].empty()) return inf;
    	if (l >= al && r <= ar) {
    		if (que[rt].back().first < pos) return inf;
    		int ind = lower_bound(all(que[rt]) , make_pair(pos , -1)) - que[rt].begin();
    		if (que[rt][ind].second > time) return inf;
    		return find_max(rt , l , r , pos , time);
    	}
    	int mid = (l + r) >> 1;
    	int ret = query_max(lson , l , mid , al , ar , pos , time);
    	if (ret == inf) return query_max(rson , mid + 1 , r , al , ar , pos , time);
    	return ret;
    }
     
    int main(){
    	int t;
    	scanf("%d" , &t);
    	while (t--) {
    		int n,m;
    		scanf("%d %d",&n,&m);
    		int o = m;
    		for (int i = 1; i <= m; i++) {
    			ans[i] = -1;
    			int op;
    			scanf("%d" , &op);
    			if (op == 0) {
    				int x,h;
    				scanf("%d %d",&x,&h);
    				ins[x] = {h , i};
    				v.push_back(h);
    			}
    			else {
    				int l,r,h;
    				scanf("%d %d %d",&l,&r,&h);
    				query[r].push_back({l , h , i});
    				v.push_back(h);
    			}
    		}
    		sort(all(v)); v.erase(unique(all(v)) , v.end());
    		m = v.size();
    		for (int i = 1; i <= n; i++) {
    			if (ins[i].first) {
    				int H = lower_bound(all(v) , ins[i].first) - v.begin() + 1;
    				update(1 , 1 , m , H , i , ins[i].second);
    			}
    			for (auto pi : query[i]) {
    				ans[pi.id] = inf;
    				int H = lower_bound(all(v) , pi.h) - v.begin() + 1;
    				if (H > 1) { 
    					int l = query_min(1 , 1 , m , 1 , H - 1 , pi.l , pi.id);
    					if (l != 0) ans[pi.id] = min(ans[pi.id] , pi.h - v[l - 1]);
    				}
    				int r = query_max(1 , 1 , m , H , m , pi.l , pi.id);
    				if (r != inf) ans[pi.id] = min(ans[pi.id] , v[r - 1] - pi.h);
    			}
    		}
    		for (int i = 1; i <= o; i++) {
    			if (ans[i] == -1) continue;
    			if (ans[i] == inf) printf("-1
    ");
    			else printf("%d
    " , ans[i]);
    		}
    		v.clear();
    		for (int i = 1; i <= n; i++) query[i].clear(),ins[i] = make_pair(0 , 0);
    		for (int i = 1; i <= 4 * m; i++) que[i].clear();
    	}
    } 
    
  • 相关阅读:
    关于 Delphi 中流的使用(3) 通过内存流读取文件
    Java线程池进阶
    平台化建设思路浅谈
    从MVC到DDD的架构演进
    数据库分区、分表、分库、分片
    树上行走(牛客)
    矩阵矩阵矩(牛客)
    2017第八届蓝桥杯大赛个人赛省赛(软件类)真题 C大学A组
    至多删除三个字符(天梯赛)
    DoubleSum(牛客)
  • 原文地址:https://www.cnblogs.com/Embiid/p/12334015.html
Copyright © 2020-2023  润新知