@一句话题解 - 2020.04@

省选貌似已经定在了 5 月 31 日,WC 貌似已经流水。感觉少经历了一次WC有点可惜。

不过现在来看 THUWC2020 是真的逃过一劫。还好没有真的在 2020 年举办。

加油吧。

codeforces - 528C:(本题的难点在于理解题意)要求定向后每个点出度和入度都为偶数,只需要满足(1)定向前每个点度数为偶数(2)整张图有偶数条边。前一个条件直接把奇点两两相连,后一个条件随便找个点连个自环。然后求出 dfs 树从下往上调整。注意处理自环重边。

#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 100000;
const int MAXM = 300000;

struct edge{
	int to; edge *nxt, *rev;
}edges[2*MAXM + 5], *adj[MAXN + 5], *ecnt = edges;
void addedge(int u, int v) {
	if( u != v ) {
		edge *p = (++ecnt), *q = (++ecnt);
		p->to = v, p->nxt = adj[u], adj[u] = p;
		q->to = u, q->nxt = adj[v], adj[v] = q;
		p->rev = q, q->rev = p;
	}
	else {
		edge *p = (++ecnt);
		p->to = v, p->nxt = adj[u], adj[u] = p;
	}
}

int dfn[MAXM + 5], dcnt;
vector<pair<int, int> >ans;
int dfs(int x, edge *f) {
	int cnt = 0; dfn[x] = (++dcnt);
	for(edge *p=adj[x];p;p=p->nxt) {
		if( p == f ) continue;
		if( !dfn[p->to] ) {
			if( dfs(p->to, p->rev))
				ans.push_back(make_pair(x, p->to));
			else ans.push_back(make_pair(p->to, x)), cnt ^= 1;
		}
		else if( dfn[p->to] <= dfn[x] )
			ans.push_back(make_pair(p->to, x)), cnt ^= 1;
	}
	return cnt;
}

int deg[MAXN + 5];
int main() {
	int n, m; scanf("%d%d", &n, &m);
	for(int i=1;i<=m;i++) {
		int a, b; scanf("%d%d", &a, &b);
		deg[a]++, deg[b]++, addedge(a, b);
	}
	int lst = 0;
	for(int i=1;i<=n;i++) {
		if( deg[i] & 1 ) {
			if( lst == 0 ) lst = i;
			else addedge(lst, i), lst = 0;
		}
	}
	if( dfs(1, NULL) ) ans.push_back(make_pair(1, 1));
	
	printf("%d\n", ans.size());
	for(int i=0;i<ans.size();i++)
		printf("%d %d\n", ans[i].first, ans[i].second);
}

atcoder - AGC029E:记 mx[x] 表示 x 到 1(除了 x)的最大点,再记 f(x, k) 表示从 x 出发经过 <= k(除了 x)的点的点数。则 ans[x] = ans[mx[x]] + f(x, mx[x] - 1),即我们一旦走过 mx[x] 就不会回头。但是这样算 f(mx[x], mx[mx[x]] - 1) 可能会算重。因此如果算重了,需要再减去 f(x 到 mx[x] 的路径上距离 mx[x] 最近的点, mx[mx[x]] - 1)。f 并查集离线算。

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

const int MAXN = 200000;

vector<int>G[MAXN + 5], q1[MAXN + 5], q2[MAXN + 5];
void addedge(int u, int v) {
	G[u].push_back(v), G[v].push_back(u);
}

int mx[MAXN + 5], pos[MAXN + 5];
void dfs1(int x, int f, int m, int p) {
	mx[x] = m, pos[x] = p;
	if( x > m ) {
		for(int i=0;i<G[x].size();i++)
			if( G[x][i] != f ) dfs1(G[x][i], x, x, G[x][i]);
	}
	else {
		for(int i=0;i<G[x].size();i++)
			if( G[x][i] != f ) dfs1(G[x][i], x, m, p);
	}
	if( x != 1 ) {
		q1[mx[x] - 1].push_back(x);
		if( pos[x] == x && mx[mx[x]] > x )
			q2[mx[mx[x]] - 1].push_back(x);
	}
}
int ans[MAXN + 5], g[MAXN + 5];
void dfs2(int x, int f) {
	if( x != 1 ) {
		if( pos[x] == x ) g[x] += ans[mx[x]];
		ans[x] += g[pos[x]];
	}
	for(int i=0;i<G[x].size();i++)
		if( G[x][i] != f ) dfs2(G[x][i], x);
}

bool tag[MAXN + 5]; int fa[MAXN + 5], siz[MAXN + 5];
int find(int x) {return fa[x] = (fa[x] == x ? x : find(fa[x]));}
void unite(int x, int y) {
	int fx = find(x), fy = find(y);
	fa[fx] = fy, siz[fy] += siz[fx];
}
void add(int &sum, int x) {
	x = find(x);
	if( !tag[x] ) {
		tag[x] = true;
		sum += siz[find(x)];
	}
}
void erase(int x) {tag[find(x)] = false;}

int main() {
	int N; scanf("%d", &N);
	for(int i=1;i<N;i++) {
		int u, v; scanf("%d%d", &u, &v);
		addedge(u, v);
	}
	dfs1(1, 0, 0, 1);
	for(int i=1;i<=N;i++) fa[i] = i, siz[i] = 1;
	for(int i=0;i<=N;i++) {
		for(int j=0;j<G[i].size();j++)
			if( G[i][j] <= i ) unite(i, G[i][j]);
		for(int j=0;j<q1[i].size();j++) {
			int x = q1[i][j], s = 0;
			for(int p=0;p<G[x].size();p++)
				if( G[x][p] <= i ) add(s, G[x][p]);
			add(s, x), ans[x] += s;
			
			for(int p=0;p<G[x].size();p++)
				if( G[x][p] <= i ) erase(G[x][p]);
			erase(x);
		}
		for(int j=0;j<q2[i].size();j++) {
			int x = q2[i][j], s = 0;
			for(int p=0;p<G[x].size();p++)
				if( G[x][p] <= i ) add(s, G[x][p]);
			add(s, x), g[x] -= s;
			
			for(int p=0;p<G[x].size();p++)
				if( G[x][p] <= i ) erase(G[x][p]);
			erase(x);
		}
		
	}
	ans[1] = 1; dfs2(1, 0);
	for(int i=2;i<=N;i++)
		printf("%d%c", ans[i] - 1, (i == N ? '\n' : ' '));
}

codeforces - 1039D:最大长度为 i 的链剖分等价于最大长度 >= i 的链剖分。可以类比 noip2018d1t3 的贪心,能够子树内部匹配的就不往子树外部匹配,O(n) 贪心一波。注意到答案单调递减,且对于 i 答案上界为 n/i,因此当 > \(O(\sqrt{n\log n})\) 时二分找相同答案右端点可以做到 \(O(n\sqrt{n\log n})\)。略卡常,将递归换成扫 dfs 序。

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;

const int MAXN = 100000;
const int SQRT = 1100;

vector<int>G[MAXN + 5];
void addedge(int u, int v) {
	G[u].push_back(v), G[v].push_back(u);
}

int dfn[MAXN + 5], fa[MAXN + 5], dcnt;
void dfs(int x, int f) {
	dfn[++dcnt] = x, fa[x] = f;
	for(int i=0;i<G[x].size();i++)
		if( G[x][i] != f ) dfs(G[x][i], x);
}

int mx[MAXN + 5], smx[MAXN + 5], f[MAXN + 5], g[MAXN + 5], n;

int solve(int x) {
	for(register int i=1;i<=n;i++) mx[i] = smx[i] = f[i] = 0;
	for(register int i=n;i>=1;i--) {
		int k = dfn[i];
		if( mx[k] + smx[k] + 1 >= x )
			f[k]++, g[k] = 0;
		else g[k] = mx[k] + 1;
		
		if( fa[k] ) {
			f[fa[k]] += f[k];
			if( g[k] > mx[fa[k]] )
				smx[fa[k]] = mx[fa[k]], mx[fa[k]] = g[k];
			else if( g[k] > smx[fa[k]] )
				smx[fa[k]] = g[k];
		}
	}
	return f[1];
}

int ans[MAXN + 5];
int main() {
	scanf("%d", &n);
	for(int i=1;i<n;i++) {
		int u, v; scanf("%d%d", &u, &v);
		addedge(u, v);
	}
	dfs(1, 0); int p;
	for(p = 1; p <= SQRT && p <= n; p++)
		ans[p] = solve(p);
	for(; p <= n;) {
		int x = solve(p), l = p, r = n;
		while( l < r ) {
			int m = (l + r + 1) >> 1;
			if( solve(m) >= x )
				l = m;
			else r = m - 1;
		}
		for(; p <= l; p++) ans[p] = x;
	}
	
	for(int i=1;i<=n;i++) printf("%d\n", ans[i]);
}

hdu - 5306:segment tree beats 模板题。我也不知道为啥跑得很慢。

#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long ll;

const int MAXN = 1000000;

int read() {
    int x = 0; char ch = getchar();
    while( ch > '9' || ch < '0' ) ch = getchar();
    while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
    return x;
}

void write(ll x, bool _=false) {
    if( !x ) {
        if( !_ ) putchar('0');
        return ;
    }
    write(x / 10, true), putchar(x % 10 + '0');
}

struct type{
    int mx, smx, mc; type() {}
    type(int _mx, int _smx, int _mc) : mx(_mx), smx(_smx), mc(_mc) {}
    friend type merge(const type &a, const type &b) {
        if( a.mx == b.mx ) return type(a.mx, max(a.smx, b.smx), a.mc + b.mc);
        else if( a.mx > b.mx ) return type(a.mx, max(a.smx, b.mx), a.mc);
        else return type(b.mx, max(a.mx, b.smx), b.mc);
    }
};

int a[MAXN + 5], n, m;

namespace segtree{
    #define lch (x << 1)
    #define rch (x << 1 | 1)
    
    int le[4*MAXN + 5], ri[4*MAXN + 5];
    type t[4*MAXN + 5]; ll s[4*MAXN + 5];
    
    void maintain(int x, int k) {
        if( k < t[x].mx )
            s[x] -= 1LL*(t[x].mx - k)*t[x].mc, t[x].mx = k;
    }
    void pushdown(int x) {maintain(lch, t[x].mx), maintain(rch, t[x].mx);}
    void pushup(int x) {s[x] = s[lch] + s[rch], t[x] = merge(t[lch], t[rch]);}
    void build(int x, int l, int r) {
        le[x] = l, ri[x] = r;
        if( l == r ) {
            t[x] = type(a[l], -1, 1); s[x] = a[l];
            return ;
        }
        int m = (l + r) >> 1;
        build(lch, l, m), build(rch, m + 1, r);
        pushup(x);
    }
    void modify(int x, int l, int r, int k) {
        if( l <= le[x] && ri[x] <= r && t[x].smx < k ) {
            maintain(x, k);
            return ;
        }
        if( l > ri[x] || r < le[x] || k >= t[x].mx ) return ;
        pushdown(x);
        modify(lch, l, r, k), modify(rch, l, r, k);
        pushup(x);
    }
    int query(int x, int l, int r) {
        if( l <= le[x] && ri[x] <= r ) return t[x].mx;
        if( l > ri[x] || r < le[x] ) return -1;
        pushdown(x); return max(query(lch, l, r), query(rch, l, r));        
    }
    ll sum(int x, int l, int r) {
        if( l <= le[x] && ri[x] <= r ) return s[x];
        if( l > ri[x] || r < le[x] ) return 0;
        pushdown(x); return sum(lch, l, r) + sum(rch, l, r);
    }
}


void solve() {
    n = read(), m = read();
    for(int i=1;i<=n;i++) a[i] = read();
    segtree::build(1, 1, n);
    for(int i=1;i<=m;i++) {
        int op = read(), x = read(), y = read(), t;
        if( op == 0 ) t = read(), segtree::modify(1, x, y, t);
        else if( op == 1 ) write(segtree::query(1, x, y)), puts("");
        else write(segtree::sum(1, x, y)), puts("");
    }
}

int main() {int _ = read(); while( _-- ) solve();}

bzoj - 4695:稍微复杂一点的 Segment Tree Beats。可以把区间取 max/min 看作区间内 min/max 值整体加减。因为打 tag 会导致 min/max 值变化(反正我写的代码会变化)所以需要先处理出儿子的 min/max 是否与父亲相同。偷了个 fread 刚好卡过。

#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long ll;

const int INF = int(1E9);
const int MAXN = 500000;

#define gc if(++ip==ie)fread(ip=buf,1,SZ,stdin)
const int SZ=1<<19;
char buf[SZ],*ie=buf+SZ,*ip=ie-1;
inline int read(){
    gc;while(*ip<'-')gc;
    bool f=*ip=='-';if(f)gc;
    int x=*ip&15;gc;
    while(*ip>'-'){x*=10;x+=*ip&15;gc;}
    return f?-x:x;
}
int A[MAXN + 5], N, M;

namespace segtree{
	#define lch (x << 1)
	#define rch (x << 1 | 1)
	
	int le[4*MAXN + 5], ri[4*MAXN + 5]; ll sum[4*MAXN + 5];
	bool ismx[4*MAXN + 5], ismn[4*MAXN + 5];
	int mx[4*MAXN + 5], smx[4*MAXN + 5], cmx[4*MAXN + 5];
	int mn[4*MAXN + 5], smn[4*MAXN + 5], cmn[4*MAXN + 5];
	int stg[4*MAXN + 5], mxtg[4*MAXN + 5], mntg[4*MAXN + 5];
	
	void pushup(int x) {
		sum[x] = sum[lch] + sum[rch];
		
		mx[x] = max(mx[lch], mx[rch]);
		if( mx[lch] == mx[rch] ) smx[x] = max(smx[lch], smx[rch]), cmx[x] = cmx[lch] + cmx[rch];
		else if( mx[lch] < mx[rch] ) smx[x] = max(mx[lch], smx[rch]), cmx[x] = cmx[rch];
		else smx[x] = max(smx[lch], mx[rch]), cmx[x] = cmx[lch];
		ismx[lch] = (mx[lch] == mx[x]), ismx[rch] = (mx[rch] == mx[x]);
		
		mn[x] = min(mn[lch], mn[rch]);
		if( mn[lch] == mn[rch] ) smn[x] = min(smn[lch], smn[rch]), cmn[x] = cmn[lch] + cmn[rch];
		else if( mn[lch] > mn[rch] ) smn[x] = min(mn[lch], smn[rch]), cmn[x] = cmn[rch];
		else smn[x] = min(smn[lch], mn[rch]), cmn[x] = cmn[lch];
		ismn[lch] = (mn[lch] == mn[x]), ismn[rch] = (mn[rch] == mn[x]);
	}
	void maintains(int x, int t) {
		if( t == 0 ) return ;
		stg[x] += t, mx[x] += t, mn[x] += t;
		sum[x] += 1LL * t * (ri[x] - le[x] + 1);
		if( mn[x] != mx[x] ) smn[x] += t, smx[x] += t;
	}
	void maintainmx(int x, int t) {
		if( mn[x] == mx[x] ) maintains(x, t);
		else {
			mxtg[x] += t, sum[x] += 1LL * t * cmx[x];
			if( mx[x] == smn[x] ) smn[x] += t; mx[x] += t;
		}
	}
	void maintainmn(int x, int t) {
		if( mn[x] == mx[x] ) maintains(x, t);
		else {
			mntg[x] += t, sum[x] += 1LL * t * cmn[x];
			if( mn[x] == smx[x] ) smx[x] += t; mn[x] += t;
		}
	}
	void pushdown(int x) {
		if( mxtg[x] ) {
			if( ismx[lch] ) maintainmx(lch, mxtg[x]);
			if( ismx[rch] ) maintainmx(rch, mxtg[x]);
			mxtg[x] = 0;
		}
		
		if( mntg[x] ) {
			if( ismn[lch] ) maintainmn(lch, mntg[x]);
			if( ismn[rch] ) maintainmn(rch, mntg[x]);
			mntg[x] = 0;
		}
		
		if( stg[x] ) maintains(lch, stg[x]), maintains(rch, stg[x]), stg[x] = 0;
	}
	void build(int x, int l, int r) {
		le[x] = l, ri[x] = r, stg[x] = mntg[x] = mxtg[x] = 0;
		if( l == r ) {
			sum[x] = mx[x] = mn[x] = A[l], smn[x] = INF, smx[x] = -INF, cmn[x] = cmx[x] = 1;
			return ;
		}
		int m = (l + r) >> 1;
		build(lch, l, m), build(rch, m + 1, r);
		pushup(x);
	}
	void modifys(int x, int l, int r, int k) {
		if( l > ri[x] || r < le[x] ) return ;
		if( l <= le[x] && ri[x] <= r ) {
			maintains(x, k);
			return ;
		}
		pushdown(x);
		modifys(lch, l, r, k), modifys(rch, l, r, k);
		pushup(x);
	}
	void modifymn(int x, int l, int r, int k) {
		if( mx[x] <= k ) return ;
		if( l > ri[x] || r < le[x] ) return ;
		if( l <= le[x] && ri[x] <= r && smx[x] < k ) {
			maintainmx(x, k - mx[x]);
			return ;
		}
		pushdown(x);
		modifymn(lch, l, r, k), modifymn(rch, l, r, k);
		pushup(x);
	}
	void modifymx(int x, int l, int r, int k) {
		if( mn[x] >= k ) return ;
		if( l > ri[x] || r < le[x] ) return ;
		if( l <= le[x] && ri[x] <= r && smn[x] > k ) {
			maintainmn(x, k - mn[x]);
			return ;
		}
		pushdown(x);
		modifymx(lch, l, r, k), modifymx(rch, l, r, k);
		pushup(x);
	}
	ll querys(int x, int l, int r) {
		if( l > ri[x] || r < le[x] ) return 0;
		if( l <= le[x] && ri[x] <= r ) return sum[x];
		pushdown(x); return querys(lch, l, r) + querys(rch, l, r);
	}
	int querymn(int x, int l, int r) {
		if( l > ri[x] || r < le[x] ) return INF;
		if( l <= le[x] && ri[x] <= r ) return mn[x];
		pushdown(x); return min(querymn(lch, l, r), querymn(rch, l, r));	
	}
	int querymx(int x, int l, int r) {
		if( l > ri[x] || r < le[x] ) return -INF;
		if( l <= le[x] && ri[x] <= r ) return mx[x];
		pushdown(x); return max(querymx(lch, l, r), querymx(rch, l, r));
	}
}

void write(int x) {
	if( x < 0 ) x = -x, putchar('-');
	if( x >= 10 ) write(x / 10);
	putchar(x % 10 + '0');
}

void write(ll x) {
	if( x < 0 ) x = -x, putchar('-');
	if( x >= 10 ) write(x / 10);
	putchar(x % 10 + '0');
}

int main() {
	N = read();
	for(int i=1;i<=N;i++) A[i] = read();
	segtree::build(1, 1, N);
	
	M = read();
	for(int i=1;i<=M;i++) {
		int tp = read(), L = read(), R = read();
		if( tp <= 3 ) {
			int X = read();
			if( tp == 1 ) segtree::modifys(1, L, R, X);
			else if( tp == 2 ) segtree::modifymx(1, L, R, X);
			else segtree::modifymn(1, L, R, X);
		} else {
			if( tp == 4 ) write(segtree::querys(1, L, R)), puts("");
			else if( tp == 5 ) write(segtree::querymx(1, L, R)), puts("");
			else write(segtree::querymn(1, L, R)), puts("");
		}
	}
}

bzoj - 3064:维护标记 max(x + a, b),可以统一题目中的操作,而且可以方便地维护最大值与历史最大值。

#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long ll;

const int MAXN = 100000;
const ll INF = ll(1E12);

struct type{
	ll a, b; type() {}
	type(ll _a, ll _b) : a(_a), b(_b) {}
	friend type operator + (type a, type b) {
		return type(a.a + b.a, max(a.b + b.a, b.b));
	} // max(max(x + a.a, a.b) + b.a, b.b)
	friend type max(type a, type b) {
		return type(max(a.a, b.a), max(a.b, b.b));
	} // max(max(x + a.a, a.b), max(x + b.a, b.b))
}; // max(x + a, b)

int A[MAXN + 5], T;
namespace segtree{
	#define lch (x << 1)
	#define rch (x << 1 | 1)
	
	int le[4*MAXN + 5], ri[4*MAXN + 5];
	type tg[4*MAXN + 5], htg[4*MAXN + 5];
	type mx[4*MAXN + 5], hmx[4*MAXN + 5];
	
	void pushup(int x) {
		mx[x] = max(mx[lch], mx[rch]), hmx[x] = max(hmx[lch], hmx[rch]);
	}
	void maintain(int x, type t, type ht) {
		hmx[x] = max(hmx[x], mx[x] + ht), mx[x] = mx[x] + t;
		htg[x] = max(htg[x], tg[x] + ht), tg[x] = tg[x] + t;
	}
	void pushdown(int x) {
		maintain(lch, tg[x], htg[x]);
		maintain(rch, tg[x], htg[x]);
		tg[x] = htg[x] = type(0, -INF);
	}
	void build(int x, int l, int r) {
		le[x] = l, ri[x] = r, tg[x] = htg[x] = type(0, -INF);
		if( l == r ) {
			mx[x] = hmx[x] = type(-INF, A[l]);
			return ;
		}
		int m = (l + r) >> 1;
		build(lch, l, m), build(rch, m + 1, r);
		pushup(x);
	}
	type query(int x, int l, int r, int t) {
		if( l > ri[x] || r < le[x] ) return type(-INF, -INF);
		if( l <= le[x] && ri[x] <= r ) return (t == 0 ? mx[x] : hmx[x]);
		pushdown(x); return max(query(lch, l, r, t), query(rch, l, r, t));
	}
	void modify(int x, int l, int r, type k) {
		if( l > ri[x] || r < le[x] ) return ;
		if( l <= le[x] && ri[x] <= r ) {
			maintain(x, k, k);
			return ;
		}
		pushdown(x);
		modify(lch, l, r, k), modify(rch, l, r, k);
		pushup(x);
	}
}

char op[2];
int main() {
	scanf("%d", &T);
	for(int i=1;i<=T;i++) scanf("%d", &A[i]);
	segtree::build(1, 1, T);
	
	int E; scanf("%d", &E);
	for(int i=1;i<=E;i++) {
		int X, Y; scanf("%s%d%d", op, &X, &Y);
		if( op[0] == 'Q' ) {
			type ans = segtree::query(1, X, Y, 0);
			printf("%lld\n", max(ans.a, ans.b));
		} else if( op[0] == 'A' ) {
			type ans = segtree::query(1, X, Y, 1);
			printf("%lld\n", max(ans.a, ans.b));
		} else if( op[0] == 'P' ) {
			int Z; scanf("%d", &Z);
			segtree::modify(1, X, Y, type(Z, -INF));
		} else {
			int Z; scanf("%d", &Z);
			segtree::modify(1, X, Y, type(-INF, Z));
		}
	}
}
posted @ 2020-04-03 17:29  Tiw_Air_OAO  阅读(211)  评论(0编辑  收藏  举报