2018.9.21 ACM训练总结 CCPC2017 哈尔滨

题目质量很高的一场ccpc:

最后只过了6题 并且罚时超高 M题精度爆炸,贯穿全场的14发罚时。

几何特别是圆的部分写的太少,cf几何题要跟着写啊!!

还有二分答案的思想。这场比赛运用很多。先确定一个答案,作为依据来check。
我们队有三点需要特别注意
1. 坚持到最后!不能放纵自己,让自己懒惰!无论心情如何,头脑如何,累或是烦躁,都要强迫自己静下心来,这一点我做得尤其不好
2. 不要去开无意义的题。我经常在没有写题或者调不出来题的时候去读没人过的题。在考场上,这样的行为几乎是浪费时间。
3. 想出来或者有人正在写的题不等于我们过了的题。一定要踏踏实实,稳扎稳打的一道一道过,先写好写的。想清楚再写。才能做到想出来就能写出来!

我们好多天没有更新trac,一定要把训练记录下来,题解先放在博客上
A: manacher +主席树。统计后一个回文中心最长向左延伸区间中能向右延伸覆盖它的前一个回文中心数量

B:二分答案,把a[i] >= ans的数看成1,其余看成0
再看有多少个区间第k大>=ans

D:结论题,ans=两个x-men的最大距离/2。因为距离最远的人一定在靠近

J: 期望。推公式。看到这种题不要怕,算总方案数和总贡献。挺好推的。题解

K:0/1分数规划。二分答案后转化成选一些区间覆盖整个线段,代价最小。
f[i] = min(f[j] +w[k](l[k] - 1 <= j <= r[k] - 1) 按线段右端点排序,负代价的线段先选。再dp,用线段树优化即可

L:**经典问题。**树上点染色。限制是i的子树内和子树外分别至少染x,y个 求总共最少染多少个。
二分答案+将子树外的限制转化为子树内最多有多少个。在树形dp求出当前子树内至少和至多分别需多少个,看是否矛盾
key:1. 1要特判,看ans是否比全树至多还多,
2. 每次check都要树形dp,不能只看当前点,一定要把子树内的答案统计取min(因为当前子树允许选的点可能更少)

细节要想清楚

#include<bits/stdc++.h>
using namespace std;
#define maxn 100020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define pb push_back
#define prev prev_
#define stack stack_
#define inf 0x3f3f3f3f

typedef long long ll;

struct node{
	int next,to;
}e[maxn * 2];
int head[maxn],cnt;
int a[maxn],b[maxn],sz[maxn],f[maxn],g[maxn],n,T,A,B;

void clear(){
	rep(i,1,n) f[i] = a[i] = b[i] = sz[i] = head[i] = 0;
	cnt = 0;
}
inline void adde(int x,int y){
	e[++cnt].to = y;
	e[cnt].next = head[x];
	head[x] = cnt;
}
void dfs(int x,int fa){
	sz[x] = 1;
	b[x] = max(b[x],b[fa]);
	fore(i,x){
		if ( e[i].to == fa ) continue;
		dfs(e[i].to,x);
		sz[x] += sz[e[i].to];
		f[x] += f[e[i].to];
	}
	f[x] = max(f[x],a[x]);
}
void dfs(int x,int fa,int mid){
	g[x] = 1;
	fore(i,x){
		if ( e[i].to == fa ) continue;
		dfs(e[i].to,x,mid);
		g[x] += g[e[i].to];
	}
	g[x] = min(g[x],mid - b[x]);
}

bool check(int mid){
	rep(i,1,n) g[i] = 0;
	dfs(1,0,mid);
	rep(i,1,n) if ( f[i] > g[i] ) return 0;
	if ( mid >= f[1] && mid <= g[1] ) return 1;
	return 0;
}
int main(){
	freopen("input.txt","r",stdin);
	scanf("%d",&T);
	while ( T-- ){
		scanf("%d",&n);
		clear();
		rep(i,1,n - 1){
			int x,y;
			scanf("%d %d",&x,&y);
			adde(x,y) , adde(y,x);
		}
		scanf("%d",&A);
		rep(i,1,A){ int x,y; scanf("%d %d",&x,&y); a[x] = max(a[x],y); }
		scanf("%d",&B);
		rep(i,1,B){ int x,y; scanf("%d %d",&x,&y); b[x] = max(b[x],y); }
		dfs(1,0);
		int l = 0 , r = n , ans = -1 , bl = 1;
		rep(i,1,n) if ( f[i] > sz[i] || n - sz[i] < b[i] ){ bl = 0; break; }
		if ( !bl ){ printf("-1\n"); continue; }
		while ( l <= r ){
			int mid = (l + r) >> 1;
			if ( check(mid) ) ans = mid , r = mid -1 ;
			else l = mid + 1;
		}
		printf("%d\n",ans);
	}

}

M:求一个圆,使至少n/2个点在圆上,保证有解。
直接随机就好了,每次错误概率 7 / 8,大概随个60次就是3/10000
然而精度真的感人。

猜你喜欢

转载自blog.csdn.net/weixin_42484877/article/details/82831866