CSP-S 模拟 19/10/24

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-02 06:47:35

Tom
在这里插入图片描述
首先,早香肠和晚香肠一定把树分成两个联通块,并且 aab-b 联通
于是可以枚举 (a,b)(a,-b) 的边,看一下 sizesize 的大小是不是 aabb
然后对于两边 dfsdfs 一遍,需要让每一个点比它的儿子小,按 dfsdfs 序编号即可


#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 2e5 + 5;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)) { ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch - '0'), ch = getchar();
	return cnt * f;
}
int n, a, b;
int first[N], nxt[N], to[N], tot;
void add(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y; }
int siz[N], du[N], col[N];
int fx, fy;
void dfs(int u, int fa){
	siz[u] = 1;
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa) continue;
		dfs(t, u); siz[u] += siz[t];
	} if(siz[u] == a || siz[u] == b){ fx = fa; fy = u; }
}
int dfn[N], sign;
void Get(int u, int fa){
	dfn[u] = ++sign;
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa) continue;
		Get(t, u);
	} 
}
int flg = 0;
void calc(){
	if(sign == a && !flg){ flg = 1;
		for(int i = 1; i <= n; i++) if(dfn[i]) col[i] = a - dfn[i] + 1;
	} else{
		for(int i = 1; i <= n; i++) if(dfn[i]) col[i] = - (b - dfn[i] + 1);
	}
}
int main(){
	n = read(), a = read(), b = read();
	for(int i = 1; i < n; i++){
		int x = read(), y = read();
		add(x, y); add(y, x); ++du[x]; ++du[y];
	} dfs(1, 0); 
	
	if(!fx && !fy){ puts("-1"); return 0; }
	sign = 0; memset(dfn, 0, sizeof(dfn)); Get(fx, fy); calc();
	sign = 0; memset(dfn, 0, sizeof(dfn)); Get(fy, fx); calc();
	for(int i = 1; i <= n; i++) cout << col[i] << " ";
	return 0;
}

Jerry
在这里插入图片描述
好题啊!解法很多
首先有 n3n^3 的区间 dpdpfl,r,0/1f_{l,r,0/1} 表示区间 [l,r][l,r] 的最大,最小,枚举断点转移即可
对于每个数分开考虑,即考虑是加上它的贡献还是减去它的贡献
首先正数前面括号没有用,负数前面的括号可以让后面都反号
发现一个点取正取负只跟前面括号的奇偶性有关
fi,jf_{i,j} 表示到 ii,有 jj 个左括号的最优值,按奇偶性转移即可
考虑到只跟奇偶性有关,那么是不是可以把多个括号消成更少的括号使得与原来等价
发现一个性质:最多两层嵌套,可以感性理解,也可以手动拆一下括号
于是状态被压到了 fi,0/1/2f_{i,0/1/2}
如何转移,分类讨论,注意要用右括号消掉前端的左括号
++:显然不能填括号
fi,0=max(fi1,0,fi1,1,fi1,2)+aif_{i,0}=max(f_{i-1,0},f_{i-1,1},f_{i-1,2})+a_i
fi,1=max(fi1,1,fi1,2)aif_{i,1}=max(f_{i-1,1},f_{i-1,2})-a_i
fi,2=fi1,2+aif_{i,2}=f_{i-1,2}+a_i
-:强行钦定填一个左括号,如果本来不填括号也会被下一次右括号给消回去
fi,1=max(fi1,0,fi1,1,fi1,2)aif_{i,1}=max(f_{i-1,0},f_{i-1,1},f_{i-1,2})-a_i
fi,2=max(fi1,1,fi1,2)+aif_{i,2}=max(f_{i-1,1},f_{i-1,2})+a_i


#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)) { ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch - '0'), ch = getchar();
	return cnt * f;
}
cs int N = 2e5;
typedef long long ll;
int T, n; ll a[N];
int op[N];
ll f[N][3];
int main(){
	T = read();
	while(T--){
		n = read();
		a[1] = read(); op[1] = 1;
		for(int i = 2; i <= n; i++){
			char c = getchar();
			if(c == '-') op[i] = -1;
			else op[i] = 1;
			a[i] = read(); 
		}
		f[0][0] = 0;
		f[0][1] = f[0][2] = -1e18;
		for(int i = 1; i <= n; i++){
			if(op[i] == 1){ 
				f[i][2] = f[i - 1][2] + a[i];
				f[i][1] = max(f[i - 1][1] - a[i], f[i - 1][2] - a[i]);
				f[i][0] = max(max(f[i - 1][0], f[i - 1][1]), f[i - 1][2]) + a[i];
			}
			if(op[i] == -1){ 
				f[i][2] = max(f[i - 1][1] + a[i], f[i - 1][2] + a[i]);
				f[i][1] = max(max(f[i - 1][0], f[i - 1][1]), f[i - 1][2]) - a[i];
				f[i][0] = -1e18;
			}
		} cout << max(max(f[n][0], f[n][1]), f[n][2]) << '\n';
	} return 0;
}

解法2:
首先 ++ 号连通块可以缩掉,于是分类讨论,只有以下 3 种情况
后面的对前面的没有影响,考虑令 fif_i 表示 i 到最后的最大值
ab-a-b
a+bc-a+b-c
aba-b
对于第一种情况,如果是 abc-a-b-c 的话可以填成 (abc)-(a-b-c),如果是 ab+c-a-b+c 的话,可以填成 (a(b+c))-(a-(b+c)),对于 cc 后面的同理,fi=ai+j=i+1ajf_i=-a_i+\sum_{j=i+1} a_j
对于第二种情况,考虑 aa 填不填括号:
(a+bc...)-(a+b-c...),同第一种,可以将后面的全部构造成正, fi=aiai+1+j=i+2ajf_i=-a_i-a_{i+1}+\sum_{j=i+2}a_j
a+b...-a+b...fi=fi+1aif_i=f_{i+1}-a_i
对于第三种情况,发现 fi=fi+1+aif_i=f_{i+1}+a_i

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)) { ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch - '0'), ch = getchar();
	return cnt * f;
}
cs int N = 2e5;
typedef long long ll;
int T, n; ll a[N];
int op[N];
ll f[N], sum[N];
int main(){
	T = read();
	while(T--){
		n = read(); int cnt = 0;
		a[++cnt] = read(); op[0] = 1;
		for(int i = 2; i <= n; i++){
			char c = getchar();
			if(c == '-') op[cnt] = -1;
			else op[cnt] = 1;
			if(op[cnt] == 1 && op[cnt - 1] == 1) a[cnt] += read();
			else a[++cnt] = read(); 
		} sum[cnt + 1] = 0;
		for(int i = cnt; i >= 1; i--) sum[i] = sum[i + 1] + a[i];
		f[cnt] = a[cnt] * op[cnt - 1];
		for(int i = cnt - 1; i >= 1; i--){
			if(op[i - 1] == 1) f[i] = f[i + 1] + a[i];
			else{
				if(op[i] == -1) f[i] = -a[i] + sum[i + 1];
				else  f[i] = max(f[i + 1] - a[i], - a[i] - a[i + 1] + sum[i + 2]);
			}
		} cout << f[1] << '\n';
	} return 0;
}

在这里插入图片描述
首先不会走回头路,也不会走得很上去或很下去,最优的走法一定是扒到边边走,过了这一个后延平行 xx 轴的线直到碰到下一个,在延着下一个的边边走
如果令 fi,0/1f_{i,0/1} 表示 从 i 的上下端点走到终点的最短路
那么 ii 只可能从沿着 x 走到的下一个转移过来,线段树维护一下下一个是什么
就是区间覆盖,单点插颜色

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)) { ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch - '0'), ch = getchar();
	return cnt * f;
}
cs int N = 5e5 + 5;
int n, f[N][2], ed; 
cs int inf = 1e9;
void Mi(int &a, int b){ if(a > b) a = b; }
struct matrix{
	int a, b, c, d;
	matrix(int _a = 0, int _b = 0, int _c = 0, int _d = 0){ a = _a; b = _b; c = _c; d = _d; }
}t[N];
vector<matrix> v[N];
int Dx[N << 1], sx, Dy[N << 1], sy; 
struct segmentree{
	int col[N << 3], tg[N << 3];
	#define mid ((l+r)>>1)
	void pushnow(int x, int v){ tg[x] = col[x] = v; }
	void pushdown(int x){ if(tg[x]) pushnow(x<<1, tg[x]), pushnow(x<<1|1, tg[x]), tg[x] = 0; }
	int ask(int x, int l, int r, int p){
		if(l == r) return col[x]; pushdown(x);
		if(p <= mid) ask(x << 1, l, mid, p);
		else ask(x << 1 | 1, mid + 1, r, p);
	}
	void modify(int x, int l, int r, int L, int R, int v){
		if(l == r) { pushnow(x, v); return; } pushdown(x);
		if(L <= mid) modify(x << 1, l, mid, L, R, v);
		if(R > mid) modify(x << 1 | 1, mid + 1, r, L, R, v);
	}
	int query(int x){ return ask(1, 1, sy, x); }
	void ins(int l, int r, int v){ modify(1, 1, sy, l, r, v); }
}seg;
void Dic(){
	sort(Dx + 1, Dx + sx + 1); sx = unique(Dx + 1, Dx + sx + 1) - (Dx + 1);
	sort(Dy + 1, Dy + sy + 1); sy = unique(Dy + 1, Dy + sy + 1) - (Dy + 1);
	for(int i = 1; i <= n; i++){
		t[i].a = lower_bound(Dx + 1, Dx + sx +1, t[i].a) - Dx;
		t[i].b = lower_bound(Dy + 1, Dy + sy +1, t[i].b) - Dy;
		t[i].d = lower_bound(Dy + 1, Dy + sy +1, t[i].d) - Dy;
	} 
}

int main(){
	freopen("speike.in","r",stdin);
	freopen("speike.out","w",stdout);
	n = read(); ed = read();
	if(n == 0){ cout << ed << '\n'; return 0;} 
	for(int i = 1; i <= n; i++){
		t[i].a = read(), t[i].b = read(), t[i].c = read(), t[i].d = read();
		Dx[++sx] = t[i].a;
		Dy[++sy] = t[i].b; Dy[++sy] = t[i].d;
		if(t[i].a > t[i].c) swap(t[i].a, t[i].c);
		if(t[i].b > t[i].d) swap(t[i].b, t[i].d);
	} 
	Dy[++sy] = 0; Dic();
	int fi = lower_bound(Dy + 1, Dy + sy + 1, 0) - Dy;
	
	for(int i = 1; i <= n; i++) v[t[i].a].push_back(t[i]);
	
	int idx = n;
	for(int i = 1; i <= sx; i++){
		int nx = idx; 
		for(int j = 0; j < v[i].size(); j++){
			matrix now = v[i][j];
			t[nx] = now;
			int nxt = seg.query(now.d);
			if(!nxt) f[nx][0] = ed - Dx[now.a] + abs(Dy[now.d]);
			else{
				f[nx][0] = f[nxt][0] + Dy[t[nxt].d] - Dy[now.d];
				Mi(f[nx][0], f[nxt][1] + Dy[now.d] - Dy[t[nxt].b]);
				f[nx][0] += Dx[t[nxt].a] - Dx[now.a];
			}
			nxt = seg.query(now.b);
			if(!nxt) f[nx][1] = ed - Dx[now.a] + abs(Dy[now.b]);
			else{
				f[nx][1] = f[nxt][0] + Dy[t[nxt].d] - Dy[now.b];
				Mi(f[nx][1], f[nxt][1] + Dy[now.b] - Dy[t[nxt].b]);
				f[nx][1] += Dx[t[nxt].a] - Dx[now.a];
			} --nx;
		} nx = idx;
		for(int j = 0; j < v[i].size(); j++){
			matrix now = v[i][j];
			seg.ins(now.b, now.d, nx);
			--nx;
		} idx = nx;
	}
	int ans = inf;
	int nxt = seg.query(fi);
	Mi(ans, Dx[t[nxt].a] + abs(Dy[t[nxt].b]) + f[nxt][1]);
	Mi(ans, Dx[t[nxt].a] + abs(Dy[t[nxt].d]) + f[nxt][0]);
	cout << ans; return 0;
}

火锅盛宴
考场真的是傻逼!
考场解法:对于一个食物,它熟的时间是 T+siT+s_i,我的想法是把它插到线段树,维护区间 minmin
操作 1 就可以在线段树上二分,操作 2 就是线段树单点查,叶子结点维护一个 setset 就可以了
操作 3 与 操作 0,1,2 独立,将操作 0,1,2 对 3 的影响,即插入删除一些权值为时间的点,然后查询区间权值 T\le T 的个数,cdqcdq 解决,卡常只剩 88
mdzzmdzz 打了 160160

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)) { ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch - '0'), ch = getchar();
	return cnt * f;
}
cs int N = 1e5 + 5;
cs int Q = 5e5 + 5;
cs int inf = 2e9;
int T;
int n, s[N];
int m;

int typ[Q];
int ans[Q];

struct data{
	int op, Time, pos, id, typ; 
	data(int _op = 0, int t = 0, int p = 0, int i = 0, int _typ = 0){
		op = _op; Time = t; pos = p; id = i; typ = _typ;
	}
}q[Q << 1], A[Q << 1], B[Q << 1];
int tot, b[Q << 1];
bool cmpt(data a, data b){ return a.Time < b.Time; }

struct segmentree{
	set<int> S[N << 2];  
	int mi[N << 2];
	#define mid ((l+r)>>1)
	int Get(int x){ if(S[x].size()) return *S[x].begin(); return inf; }
	void build(int x, int l, int r){
		mi[x] = inf; if(l == r){ S[x].clear(); return; }
		build(x<<1, l, mid); build(x<<1|1, mid+1, r);
	}
	void pushup(int x){ mi[x] = min(mi[x<<1], mi[x<<1|1]); }
	void modify(int x, int l, int r, int p, int v){
		if(l == r){ 
			S[x].insert(v);
			mi[x] = Get(x);
			return; 
		}
		if(p <= mid) modify(x << 1, l, mid, p, v);
		else modify(x << 1 | 1, mid+1, r, p, v);
		pushup(x);
	}

	int qTime(int x, int l, int r, int Time){
		if(l == r){ 
			q[++tot] = data(1, *S[x].begin(), l, 0, -1);
			S[x].erase(S[x].begin()); 
			mi[x] = Get(x); return l;
		}
		int ans = 0;
		if(mi[x << 1] <= Time) ans = qTime(x << 1, l, mid, Time);
		else ans = qTime(x << 1 | 1, mid+1, r, Time);
		pushup(x); return ans;
	}
	int askmi(int Time){ if(mi[1] > Time) return -1; return qTime(1, 1, n, Time); }
	
	int qId(int x, int l, int r, int p, int Time){
		if(l == r){ 
			if(S[x].empty()) return -1;
			if(*S[x].begin() <= Time){
				q[++tot] = data(1, *S[x].begin(), l, 0, -1);
				S[x].erase(S[x].begin());
				mi[x] = Get(x);
				return 0;
			} return *S[x].begin() - Time;
		}
		int ans = 0;
		if(p <= mid) ans = qId(x << 1, l, mid, p, Time);
		else ans = qId(x << 1|1, mid+1, r, p, Time); 
		pushup(x); return ans;
	}
	int askid(int p, int Time){ return qId(1, 1, n, p, Time); }
	#undef mid
}seg;

void Deal(){
	for(int i = 1; i <= m; i++){
		int Time = read(), op = read(); typ[i] = op;
		if(op == 0){
			int id = read();
			seg.modify(1, 1, n, id, Time + s[id]);
			q[++tot] = data(1, Time + s[id], id, i, 1);
		}
		if(op == 1){
			ans[i] = seg.askmi(Time);
		}
		if(op == 2){
			int id = read();
			ans[i] = seg.askid(id, Time); 
		}
		if(op == 3){
			int l = read(), r = read();
			q[++tot] = data(2, Time, l - 1, i, -1);
			q[++tot] = data(2, Time, r, i, 1);
		}
	}
}

struct BIT{
	int c[N];
	void add(int x, int v){ for(;x<=n;x+=x&-x) c[x] += v;}
	int ask(int x){ int ans = 0; for(;x;x-=x&-x) ans += c[x]; return ans;} 
}bit;

void cdq(int l, int r){
	if(l == r) return;
	int mid = (l+r) >> 1;
	int L = 0, R = 0;
	for(int i = l; i <= mid; i++) if(q[i].op == 1) A[++L] = q[i];
	for(int i = mid + 1; i <= r; i++) if(q[i].op == 2) B[++R] = q[i];
	sort(A + 1, A + L + 1, cmpt);
	sort(B + 1, B + R + 1, cmpt);
	for(int i = 1, j = 1; j <= R; j++){
		while(i <= L && A[i].Time <= B[j].Time){
			bit.add(A[i].pos, A[i].typ);
			++i;
		}
		ans[B[j].id] += B[j].typ * bit.ask(B[j].pos);
	}
	for(int i = 1; i <= L; i++) if(A[i].Time <= B[R].Time) bit.add(A[i].pos, -A[i].typ);
	cdq(l, mid); cdq(mid + 1, r);
}

void Solve(){
	n = read(); seg.build(1, 1, n);
	for(int i = 1; i <= n; i++) s[i] = read();
	m = read();
	Deal();
	cdq(1, tot);
	for(int i = 1; i <= m; i++){
		if(typ[i] == 1){
			if(~ans[i]) cout << ans[i] << '\n';
			else puts("Yazid is angry.");
		}
		if(typ[i] == 2){
			if(ans[i] == 0) puts("Succeeded!");
			else if(ans[i] == -1) puts("YJQQQAQ is angry.");
			else cout << ans[i] << '\n';
		}
		if(typ[i] == 3) cout << ans[i] << '\n';
	}
}

void Init(){
	memset(ans, 0, sizeof(ans));
	tot = 0;
}
int main(){
	freopen("b.in","r",stdin);
	freopen("b.out","w",stdout);
	T = read();
	while(T--) Solve(), Init();
	return 0;
}

正解,考虑分别维护煮熟的和没有煮熟的集合
每次需要把新煮熟的插到煮熟的集合,就是把 Time\le Time 的一些点插到另一个集合中
优先队列或者 setset 维护
然后需要支持:
查最小的不为 0 的位置
查一个位置为不为 0
查区间的个数
删除,插入
mdzzmdzz 树状数组,第一个类似倍增跳就好了
如果一个位置为 0,需要求没有熟的最小熟的时间,考虑到小的一定先出,用一个队列维护即可
每次弹优先队列的时候顺便弹一下
妥妥的签到题

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)) { ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch - '0'), ch = getchar();
	return cnt * f;
}
cs int N = 1e5 + 5, Q = 5e5 + 5, inf = 2e9;
int T, n, s[N], m, ans[Q], typ[Q];
queue<int> q[N]; // 维护未熟的食物 
struct node{
	int id, Time; node(int i = 0, int t = 0){ id = i; Time = t; }
	bool operator < (cs node &a) cs{ return Time > a.Time;}
}; priority_queue<node> S;
struct BIT{
	int c[N];
	void add(int x, int v){ for(;x<=n; x+=x&-x) c[x] += v; }
	int askmi(){
		int now = 0;
		for(int i = 18; i >= 0; i--){
			if(now + (1 << i) <= n && c[now + (1 << i)] == 0) now += (1 << i); 
		} if(now == n) return -1; add(now + 1, -1); return now + 1;
	}
	int ask(int x){ int ans = 0; for(;x;x-=x&-x) ans += c[x]; return ans; }
	int askid(int x, int Time){ 
		if(ask(x) - ask(x-1) == 0){ if(q[x].empty()) return -1; return q[x].front() - Time;} 
		add(x, -1); return 0;
	}
}bit;
void Deal(){
	for(int i = 1; i <= m; i++){
		int Time = read(), op = read(); typ[i] = op;
		while(!S.empty() && S.top().Time <= Time){ node x = S.top(); S.pop(); bit.add(x.id, 1); q[x.id].pop(); }
		if(op == 0){ int id = read(); S.push(node(id, Time + s[id])); q[id].push(Time + s[id]); }
		if(op == 1) ans[i] = bit.askmi();
		if(op == 2){ int id = read(); ans[i] = bit.askid(id, Time); }
		if(op == 3){ int l = read(), r = read(); ans[i] = bit.ask(r) - bit.ask(l - 1);}
	}
}
void Solve(){
	n = read(); for(int i = 1; i <= n; i++) s[i] = read(); m = read(); Deal();
	for(int i = 1; i <= m; i++){
		if(typ[i] == 1){ if(~ans[i]) cout << ans[i] << '\n'; else puts("Yazid is angry."); }
		if(typ[i] == 2){ 
			if(ans[i] == 0) puts("Succeeded!"); else if(ans[i] == -1) puts("YJQQQAQ is angry."); 
			else cout << ans[i] << '\n';
		} if(typ[i] == 3) cout << ans[i] << '\n';
	}
}
void Init(){ 
	memset(ans, 0, sizeof(ans)); while(!S.empty()) S.pop();
	for(int i = 1; i <= n; i++) while(!q[i].empty()) q[i].pop();
	memset(bit.c, 0, sizeof(bit.c));
}
int main(){ T = read(); while(T--) Solve(), Init(); return 0; }

Polygon
送了 50pts50pts
考虑不枚举 3 个点,枚举 2 的点 x,yx,y然后用一个啥数据结构快速得到最后一个点的最优位置
先除去这一条线自己就覆盖了的,就是一个端点在它们上面,两个在它们下面
用一个 vectorvector 存以它为端点的所有三角形,到它的时候插一下
考虑 yy 挪到 y+1y+1 有什么变化,以 y+1y+1 为顶点且另外两个在 [x,y][x,y] 区间的会凉掉,去除
考虑另一个点 zz 的位置会产生什么贡献,新增的那一个贡献就是对 端点为 y+1y+1 的三角形,如果另两个端点 [l,r][l,r][x,y][x,y] 直接,那么对 [l,y][l,y] 有贡献,线段树区间加全局 maxmax

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 1e3 + 5;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)) { ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch - '0'), ch = getchar();
	return cnt * f;
}
int n, ans;
typedef pair<int, int> pi;
#define mp make_pair
#define fi first
#define se second
#define pb push_back
vector<pi> v[N];
bool in(int a, int b, int c){ return a <= b && b <= c; }
void Mx(int &a, int b){ if(a < b) a = b; }
struct segmentree{
	int mx[N << 2], tg[N << 2];
	#define mid ((l+r)>>1)
	void build(int x, int l, int r){
		mx[x] = tg[x] = 0; if(l == r) return; 
		build(x<<1, l, mid); build(x<<1|1, mid+1, r);
	} 
	void pushnow(int x, int v){ mx[x] += v; tg[x] += v; }
	void pushdown(int x){ if(tg[x]) pushnow(x<<1, tg[x]), pushnow(x<<1|1, tg[x]), tg[x] = 0;}
	void modify(int x, int l, int r, int L, int R, int v){
		if(L<=l && r<=R){ pushnow(x, v); return; } pushdown(x);
		if(L<=mid) modify(x<<1, l, mid, L, R, v);
		if(R>mid) modify(x<<1|1, mid+1, r, L, R, v);
		mx[x] = max(mx[x<<1], mx[x<<1|1]);
	}
}seg;
void solve(int x){
	int y = x + 1; seg.build(1, x, n);
	int now = 0;
	while(y + 1 <= n){
		for(int i = 0; i < v[y].size(); i++){
			pi t = v[y][i];
			if(!in(x + 1, t.fi, y - 1) && !in(x + 1, t.se, y - 1)) ++now;
		} ++y;
		for(int i = 0; i < v[y].size(); i++){
			pi t = v[y][i];
			if(in(x, t.fi, y - 1) && in(x, t.se, y - 1)){
				--now;
				seg.modify(1, x, n, t.fi + 1, y - 1, 1);
			}
		} Mx(ans, now + seg.mx[1]);
	}
}
int main(){
	n = read();
	for(int i = 1; i <= n - 2; i++){
		int a[3];
		for(int j = 0; j < 3; j++) scanf("%d", &a[j]), ++a[j];
		sort(a, a + 3);
		v[a[0]].pb(mp(a[1], a[2]));
		v[a[1]].pb(mp(a[0], a[2]));
		v[a[2]].pb(mp(a[0], a[1]));
	} for(int i = 1; i <= n - 2; i++) solve(i);
	cout << ans << '\n'; return 0;
}

有一个结论,就是面与面有公共边则连边,有点像对偶图,那么最后连出来是一棵树
并且两点直接穿过的边对应这树上的路径,画个图发现比较显然
然后 3 跳边的路径对应树上的一个三叉路径,树形 dpdp 求即可

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 1e3 + 5;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)) { ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch - '0'), ch = getchar();
	return cnt * f;
}
int n, ans, now;
int first[N], nxt[N << 1], to[N << 1], tot;
void add(int x, int y){nxt[++tot] = first[x], first[x] = tot, to[tot] = y; }
typedef pair<int, int> pi;
#define mp make_pair
map<pi, int> m;
int d[N];
void dfs1(int u, int fa){
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa) continue;
		dfs1(t, u); d[u] = max(d[u], d[t]);
	} ++d[u];
}
void dfs2(int u, int fa, int val){
	int mx = 0, mxi = 0;
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa) continue;
		if(d[t] > mx){ mxi = mx; mx = d[t]; }
		else if(d[t] > mxi) mxi = d[t];
	} ans = max(ans, mx + mxi + val + 1);
	for(int i = first[u]; i; i = nxt[i]){
		int t = to[i]; if(t == fa) continue;
		if(d[t] != mx) dfs2(t, u, max(val, mx) + 1);
		else dfs2(t, u, max(val, mxi) + 1); 
	}
}
int main(){
	n = read();
	for(int i = 1; i <= n - 2; i++){
		now++;
		int a[3]; scanf("%d%d%d", &a[0], &a[1], &a[2]);
		sort(a, a + 3); 
		for(int j = 0; j <= 2; j++)
			for(int k = j + 1; k <= 2; k++)
				if(m.count(mp(a[j], a[k]))){
					add(now, m[mp(a[j], a[k])]);
					add(m[mp(a[j], a[k])], now);
				}
		for(int j = 0; j <= 2; j++)
			for(int k = j + 1; k <= 2; k++)
				m[mp(a[j], a[k])] = now;
	} dfs1(1, 0); dfs2(1, 0, 0); cout << ans;
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!