@一句话题解 - 2020.06@

6 月 20 日省选???不是吧。。。

bzoj - 1449:设第 i 球队总共打了 di 场,如果胜 xi 场则败 di - xi 场,即收益可表示为 xi 的二次函数。把胜利次数看成“资源”转成资源分配模型,二次代价 x^2 拆开 1 + 3 + 5 + ...。然后最小费用最大流。

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

const int MAXN = 5000;
const int MAXM = 1000;

namespace FlowGraph{
	const int MAXV = 2*MAXN, MAXE = 50*MAXM, INF = (1<<30);
	
	struct edge{
		int to, cap, flow, cost;
		edge *nxt, *rev;
	}edges[MAXE + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt = edges;
	void addedge(int u, int v, int c, int w) {
		edge *p = (++ecnt), *q = (++ecnt);
		p->to = v, p->nxt = adj[u], adj[u] = p;
		p->cap = c, p->flow = 0, p->cost = w;
		q->to = u, q->nxt = adj[v], adj[v] = q;
		q->cap = 0, q->flow = 0, q->cost = -w;
		p->rev = q, q->rev = p;
		
//		printf("! %d %d %d %d\n", u, v, c, w);
	}
	
	int hp[MAXV + 5], f[MAXV + 5], s, t;
	void update(int x, int k) {
		f[x] = k;
		while( x ) {
			hp[x] = x;
			if( (x<<1) <= t && f[hp[x<<1]] < f[hp[x]] )
				hp[x] = hp[x << 1];
			if( (x<<1|1) <= t && f[hp[x<<1|1]] < f[hp[x]] )
				hp[x] = hp[x << 1 | 1];
			x >>= 1;
		}
	}
	int d[MAXV + 5], h[MAXV + 5];
	bool relabel() {
		for(int i=1;i<=t;i++)
			h[i] += d[i], d[i] = f[i] = INF, hp[i] = i, cur[i] = adj[i];
		update(t, d[t] = 0);
		while( f[hp[1]] != INF ) {
			int x = hp[1]; update(x, INF);
			for(edge *p=adj[x];p;p=p->nxt) {
				int c = p->rev->cost + h[x] - h[p->to];
				if( d[p->to] > d[x] + c && p->rev->cap > p->rev->flow )
					update(p->to, d[p->to] = d[x] + c);
			}
		}
		return d[s] != INF;
	}
	bool vis[MAXV + 5];
	int aug(int x, int tot) {
		if( x == t ) return tot;
		vis[x] = true; int sum = 0;
		for(edge *&p=cur[x];p;p=p->nxt) {
			int c = p->cost + h[p->to] - h[x];
			if( d[p->to] + c == d[x] && !vis[p->to] && p->cap > p->flow ) {
				int del = aug(p->to, min(tot - sum, p->cap - p->flow));
				sum += del, p->flow += del, p->rev->flow -= del;
				if( sum == tot ) break;
			}
		}
		vis[x] = false; return sum;
	}
	
	int min_cost_max_flow(int _s, int _t) {
		int cost = 0; s = _s, t = _t;
		while( relabel() ) {
			int del = aug(s, INF);
			cost += del * (d[s] + h[s]);
		}
		return cost;
	}
}

int d[MAXN + 5], n, m;
int w[MAXN + 5], l[MAXN + 5], C[MAXN + 5], D[MAXN + 5];
int pw2(int x) {return x * x;}
int func(int i, int k) {
	return C[i]*pw2(w[i] + k) + D[i]*pw2(l[i] + d[i] - k);
}
int main() {
	scanf("%d%d", &n, &m);
	for(int i=1;i<=n;i++)
		scanf("%d%d%d%d", &w[i], &l[i], &C[i], &D[i]);
	
	int s = n + m + 1, t = n + m + 2;
	for(int i=1,a,b;i<=m;i++) {
		scanf("%d%d", &a, &b), d[a]++, d[b]++;
		FlowGraph::addedge(s, n + i, 1, 0);
		FlowGraph::addedge(n + i, a, 1, 0);
		FlowGraph::addedge(n + i, b, 1, 0);
	}
	
	int ans = 0;
	for(int i=1;i<=n;i++) {
		ans += func(i, 0);
		for(int j=1;j<=d[i];j++)
			FlowGraph::addedge(i, t, 1, func(i, j) - func(i, j - 1));
	}
	
	printf("%d\n", ans + FlowGraph::min_cost_max_flow(s, t));
}

uoj - 455:模拟费用流模板题。扫描到送餐员,只有送餐员会反悔;扫描到餐厅时,两者都可能反悔,此时送餐员反悔的代价全部一样,因此集中处理。关于模拟费用流的博客1博客2

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

typedef long long ll;
typedef pair<ll, int> pli;
#define fi first
#define se second

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

struct node{
	int x, w, c; node() {}
	node(int _x, int _w, int _c) : x(_x), w(_w), c(_c) {}
	friend bool operator < (node a, node b) {return a.x < b.x;}
}a[2*MAXN + 5];

int main() {
	int n, m; ll sum = 0; scanf("%d%d", &n, &m);
	for(int i=1;i<=n;i++) scanf("%d", &a[i].x), a[i].w = 0, a[i].c = -1;
	for(int i=n+1;i<=n+m;i++) scanf("%d%d%d", &a[i].x, &a[i].w, &a[i].c), sum += a[i].c;
	sort(a + 1, a + n + m + 1);
	
	if( sum < n ) {
		puts("-1");
		return 0;
	}
	else {
		ll ans = 0;
		priority_queue<pli, vector<pli>, greater<pli> >q1;
		priority_queue<pli, vector<pli>, greater<pli> >q2;
		q2.push(make_pair(INF, n)); // 必须要插入一个哨兵结点 
		for(int i=1;i<=n+m;i++) {
			if( a[i].c == -1 ) {
				pli p = q2.top(); q2.pop();
				ans += (p.fi + a[i].x); p.se--;
				q1.push(make_pair(-(p.fi + a[i].x) - a[i].x, 1));
				if( p.se ) q2.push(p);
			}
			else {
				int cnt = a[i].c, tmp = 0;
				while( !q1.empty() && q1.top().fi + a[i].w + a[i].x < 0 && cnt ) {
					pli p = q1.top(); int t = min(cnt, p.se); q1.pop();
					
					ans += t * (p.fi + a[i].w + a[i].x);
					tmp += t, cnt -= t, p.se -= t;
					if( p.se ) q1.push(p);
					q2.push(make_pair(-(p.fi + a[i].w + a[i].x) + a[i].w - a[i].x, t));
				}
				if( tmp ) q1.push(make_pair(-(a[i].w + a[i].x), tmp));
				if( cnt ) q2.push(make_pair(a[i].w - a[i].x, cnt));
			}
		}
		printf("%lld\n", ans);
	}
}

loj - 6405:树上模拟费用流模板题。甚至不需要优化(把反悔代价相同的存储在一起,这样做是 O(NlogN))可以直接 O(XlogX) 过。不过可并堆中总结点数需要足够大否则要 RE(理论上好像是 4*X,不过开大点更保险)。

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

typedef long long ll;

const int MAXN = 250000;
const int MAXA = 1000000;
const ll INF = ll(1E12);

namespace heap{
	struct node{
		ll key; int dis;
		node *ch[2];
	}pl[6*MAXA + 5], *NIL = pl, *ncnt = pl;
	node *newnode(ll k) {
		node *p = (++ncnt);
		p->key = k, p->dis = 0;
		p->ch[0] = p->ch[1] = NIL;
		return p;
	}
	node *merge(node *a, node *b) {
		if( a == NIL ) return b;
		if( b == NIL ) return a;
		if( a->key > b->key ) swap(a, b);
		a->ch[1] = merge(a->ch[1], b);
		if( a->ch[0]->dis < a->ch[1]->dis ) swap(a->ch[0], a->ch[1]);
		a->dis = a->ch[1]->dis + 1;
		return a;
	}
	node *insert(node *x, ll k) {return merge(x, newnode(k));}
	node *erase(node *x) {return merge(x->ch[0], x->ch[1]);}
};

struct edge{
	int to, dis;
	edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt = edges;
void addedge(int u, int v, int c) {
	edge *p = (++ecnt);
	p->to = v, p->dis = c, p->nxt = adj[u], adj[u] = p;
	p = (++ecnt);
	p->to = u, p->dis = c, p->nxt = adj[v], adj[v] = p;
}

heap::node *rta[MAXN + 5], *rtb[MAXN + 5];
int a[MAXN + 5], b[MAXN + 5], n;
ll dfs(int x, int f, ll d) {
	ll ret = 0; rta[x] = rtb[x] = heap::NIL;
	if( a[x] > b[x] ) {
		for(int i=1;i<=a[x]-b[x];i++)
			rta[x] = heap::insert(rta[x], d);
	}
	else {
		for(int i=1;i<=b[x]-a[x];i++)
			rtb[x] = heap::insert(rtb[x], -(d + INF - 2*d) + d), ret += d + INF - 2*d;
	}
	for(edge *p=adj[x];p;p=p->nxt) {
		if( p->to == f ) continue;
		ret += dfs(p->to, x, d + p->dis);
		rta[x] = merge(rta[x], rta[p->to]);
		rtb[x] = merge(rtb[x], rtb[p->to]);
		
		while( rta[x] != heap::NIL && rtb[x] != heap::NIL && rta[x]->key + rtb[x]->key - 2*d < 0 ) {
			ll p = rta[x]->key, q = rtb[x]->key; ret += p + q - 2*d;
			rta[x] = heap::erase(rta[x]), rtb[x] = heap::erase(rtb[x]);
			rta[x] = heap::insert(rta[x], -(p + q - 2*d) + p);
			rtb[x] = heap::insert(rtb[x], -(p + q - 2*d) + q);
		}
	}
	
	return ret;
}

int main() {
	scanf("%d", &n);
	for(int i=1,u,v,c;i<n;i++)
		scanf("%d%d%d", &u, &v, &c), addedge(u, v, c);
	for(int i=1;i<=n;i++)
		scanf("%d%d", &a[i], &b[i]);
	printf("%lld\n", dfs(1, 0, 0));
}

loj - 2510:观察到树高不超过 40,不妨以树高为状态设计 dp。定义 dp[p][q][i] 表示以 i 为根的子树,到根的路径上有 p 条红边,q 条绿边的最小代价。枚举保留红边/绿边 O(1) 转移,时间复杂度 O(40^2*n)。

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

typedef long long ll;

const int MAXN = 20000;

int d[2][2*MAXN + 5], fa[2*MAXN + 5], ch[2][2*MAXN + 5];
ll sac[2*MAXN + 5], sbc[2*MAXN + 5], sc[2*MAXN + 5];

ll dp[42][42][2*MAXN + 5];
int main() {
	int n; scanf("%d", &n);
	for(int i=1;i<n;i++) {
		scanf("%d%d", &ch[0][i], &ch[1][i]);
		if( ch[0][i] < 0 ) ch[0][i] = n - ch[0][i] - 1;
		if( ch[1][i] < 0 ) ch[1][i] = n - ch[1][i] - 1;
		fa[ch[0][i]] = fa[ch[1][i]] = i;
	}
	
	ll ans = 0;
	for(int i=n,a,b,c;i<2*n;i++) {
		scanf("%d%d%d", &a, &b, &c);
		ans += 1LL*a*b*c, sac[i] = 1LL*a*c, sbc[i] = 1LL*b*c, sc[i] = c;
	}
	for(int i=1;i<n;i++)
		for(int p=0;p<=1;p++)
			for(int q=0;q<=1;q++)
				d[p][ch[q][i]] = d[p][i] + (p == q);
	for(int i=2*n-1;i>=2;i--)
		sac[fa[i]] += sac[i], sbc[fa[i]] += sbc[i], sc[fa[i]] += sc[i];
	
	for(int i=n-1;i>=1;i--)
		for(int p=d[0][i];p>=0;p--)
			for(int q=d[1][i];q>=0;q--) {
				int c0 = ch[0][i], c1 = ch[1][i];
				ll s1 = sc[c0]*q + sbc[c0] + dp[p + 1][q][c0] + dp[p][q][c1];
				ll s2 = sc[c1]*p + sac[c1] + dp[p][q][c0] + dp[p][q + 1][c1];
				dp[p][q][i] = min(s1, s2);
			}
	printf("%lld\n", ans + dp[0][0][1]);
}

bzoj - 5403:简单费用流建模。源点连向 i,j 同偶的非禁止位置, i,j 同奇的非禁止位置连向相邻危险点,危险点拆点中间连费用为危险值,然后右边类似地连向 i,j 同奇的非禁止位置,再把这些点连向汇点。跑流量 <= m 的最大费用流。

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

const int MAXN = 50;
const int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};

namespace FlowGraph{
	const int MAXV = 2*MAXN*MAXN, MAXE = 20*MAXV, INF = (1 << 30);
	
	struct edge{
		int to, cap, flow, cost;
		edge *nxt, *rev;
	}edges[MAXE + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt = edges;
	
	int d[MAXV + 5], h[MAXV + 5], s, t;
	void addedge(int u, int v, int c, int w) {
		edge *p = (++ecnt), *q = (++ecnt);
		p->to = v, p->cap = c, p->flow = 0, p->cost = w;
		p->nxt = adj[u], adj[u] = p;
		q->to = u, q->cap = 0, q->flow = 0, q->cost = -w;
		q->nxt = adj[v], adj[v] = q;
		p->rev = q, q->rev = p;
//		printf("! %d %d %d %d\n", u, v, c, w);
	}
	
	int f[MAXV + 5], hp[MAXV + 5];
	void update(int x, int k) {
		f[x] = k;
		while( x ) {
			hp[x] = x;
			if( (x<<1) <= t && f[hp[x<<1]] < f[hp[x]] )
				hp[x] = hp[x<<1];
			if( (x<<1|1) <= t && f[hp[x<<1|1]] < f[hp[x]] )
				hp[x] = hp[x<<1|1];
			x >>= 1;
		}
	}
	
	bool relabel() {
		for(int i=1;i<=t;i++)
			h[i] += d[i], f[i] = d[i] = INF, cur[i] = adj[i], hp[i] = i;
			
		update(t, d[t] = 0);
		while( f[hp[1]] != INF ) {
			int x = hp[1]; update(x, INF);
			for(edge *p=adj[x];p;p=p->nxt) {
				int c = p->rev->cost + h[x] - h[p->to];
				if( p->rev->cap > p->rev->flow && d[p->to] > d[x] + c )
					update(p->to, d[p->to] = d[x] + c);
			}
		}
		return d[s] != INF;
	}
	
	bool vis[MAXV + 5];
	int aug(int x, int tot) {
		if( x == t ) return tot;
		vis[x] = true; int sum = 0;
		for(edge *&p=cur[x];p;p=p->nxt) {
			int c = p->cost + h[p->to] - h[x];
			if( d[x] == d[p->to] + c && !vis[p->to] && p->cap > p->flow ) {
				int del = aug(p->to, min(tot - sum, p->cap - p->flow));
				sum += del, p->flow += del, p->rev->flow -= del;
				if( sum == tot ) break;
			}
		}
		vis[x] = false; return sum;
	}
	
	int min_cost_flow(int _s, int _t, int l) {
		int cost = 0; s = _s, t = _t;
		while( l && relabel() && (d[s] + h[s]) < 0 ) {
			int del = aug(s, l); l -= del;
			cost += (d[s] + h[s]) * del;
		}
		return cost;
	}
}

int A[MAXN + 5][MAXN + 5], id[2][MAXN + 5][MAXN + 5], cnt;
int main() {
	int ans = 0, n, m, k; scanf("%d%d%d", &n, &m, &k);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			scanf("%d", &A[i][j]), ans += A[i][j];
	for(int i=1,X,Y;i<=k;i++)
		scanf("%d%d", &X, &Y), A[X][Y] = -1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) {
			if( A[i][j] == -1 ) continue;
			else if( A[i][j] == 0 ) {
				if( (i + j) % 2 == 0 )
					id[0][i][j] = (++cnt);
			}
			else id[0][i][j] = (++cnt), id[1][i][j] = (++cnt);
		}
		
	int s = (++cnt), t = (++cnt);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) {
			if( A[i][j] == -1 ) continue;
			if( i % 2 == 0 && j % 2 == 0 ) {
				FlowGraph::addedge(s, id[0][i][j], 1, 0);
				for(int p=0;p<4;p++) {
					int x = i + dx[p], y = j + dy[p];
					if( x < 1 || y < 1 || x > n || y > n || !A[x][y] ) continue;
					FlowGraph::addedge(id[0][i][j], id[0][x][y], 1, 0);
				}
			}
			else if( i % 2 == 1 && j % 2 == 1 ) {
				FlowGraph::addedge(id[0][i][j], t, 1, 0);
				for(int p=0;p<4;p++) {
					int x = i + dx[p], y = j + dy[p];
					if( x < 1 || y < 1 || x > n || y > n || !A[x][y] ) continue;
					FlowGraph::addedge(id[1][x][y], id[0][i][j], 1, 0);
				}
			}
			else if( A[i][j] )
				FlowGraph::addedge(id[0][i][j], id[1][i][j], 1, -A[i][j]);
		}
	
	printf("%d\n", ans + FlowGraph::min_cost_flow(s, t, m));
}

loj - 2508:将没有锁门的房间合并,然后直接暴力做(指每次寻找左右门的钥匙是否在当前区间里面),当然暴力是会TLE的然后记忆化搜索一下。时间复杂度的分析可以考虑如果一个钥匙藏在门后,则这个门无法被打开。将无法被打开的门看作单向边,每一条链上的复杂度是均摊 O(n),因此总复杂度 O(n)。

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

const int MAXN = 1000000;

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

int y[MAXN + 5], n, m, p;
int l[MAXN + 5], r[MAXN + 5], id[MAXN + 5];

void func(int x) {
	while( true ) {
		if( l[x] <= y[l[x] - 1] && y[l[x] - 1] <= r[x] )
			func(id[l[x] - 1]), l[x] = l[id[l[x] - 1]];
		else if( l[x] <= y[r[x]] && y[r[x]] <= r[x] )
			func(id[r[x] + 1]), r[x] = r[id[r[x] + 1]];
		else break;
	}
}

int main() {
	n = read(), m = read(), p = read();
	for(int i=0;i<=n;i++) y[i] = -1;
	y[0] = 0, y[n] = n + 1;
	for(int i=1,x;i<=m;i++) x = read(), y[x] = read();
	
	int cnt = 0, lst = 1;
	for(int i=1;i<=n;i++) {
		id[i] = (cnt + 1);
		if( y[i] != -1 )
			cnt++, l[cnt] = lst, r[cnt] = i, lst = i + 1;
	}
	
	for(int i=1;i<=cnt;i++)
		func(i);
	
	for(int i=1;i<=p;i++) {
		int S = read(), T = read();
		puts(l[id[S]] <= T && T <= r[id[S]] ? "YES" : "NO");
	}
}

loj - 3146:考虑某个路灯亮起/熄灭,它会导致 a 在某个区间 [l1, r1]、b 在某个区间 [l2, r2] 变得可行/不可行(找区间用 set 即可)。“至今所有时刻”可以考虑提前计算代价,询问时减去多的代价。然后就是二维区间加 + 询问。空间不够可以用些 trick 离线处理树套树。

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

typedef pair<int, int> pii;
#define fi first
#define se second
#define mp make_pair

const int MAXN = 300000;

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

int ch[2][70*MAXN + 5], sum[70*MAXN + 5], ncnt;
int newnode() {
	int p = (++ncnt);
	ch[0][p] = ch[1][p] = sum[p] = 0;
	return p;
}
void update(int &x, int l, int r, int p, int k) {
	if( !x ) x = newnode(); sum[x] += k;
	if( l == r ) return ;
	int m = (l + r) >> 1;
	if( p <= m ) update(ch[0][x], l, m, p, k);
	else update(ch[1][x], m + 1, r, p, k);
}
int gsum(int x, int l, int r, int p) {
	if( l == r ) return sum[x];
	int m = (l + r) >> 1;
	if( p <= m ) return gsum(ch[0][x], l, m, p);
	else return gsum(ch[1][x], m + 1, r, p) + sum[ch[0][x]];
}
vector<pii>v[MAXN + 5];

int rt[MAXN + 5], n, q;
int lowbit(int x) {return x & -x;}
void add(int x, int y, int k) {
//	printf("+ %d %d %d\n", x, y, k);
	for(int i=x;i<=n;i+=lowbit(i))
		if( y <= n ) v[i].push_back(mp(y, k)); // update(rt[i], 1, n, y, k);
}
void add(int x1, int x2, int y1, int y2, int k) {
//	printf("! %d %d %d %d %d\n", x1, x2, y1, y2, k);
	add(x2 + 1, y2 + 1, k), add(x1, y1, k);
	add(x2 + 1, y1, -k), add(x1, y2 + 1, -k);
}
int fsum(int x, int y, int p) {
//	printf("? %d %d\n", x, y);
	int ret = 0;
	for(int i=x;i;i-=lowbit(i))
		v[i].push_back(mp(-y, p)); //ret += gsum(rt[i], 1, n, y);
	return ret;
}

set<int>st; int s[MAXN + 5];
void modify(int x, int k) {
	int l, r;
	set<int>::iterator it1 = st.upper_bound(x);
	set<int>::iterator it2 = it1; it2--;
	if( !s[x] ) it2--, st.erase(x);
	else st.insert(x);
	l = (*it2), r = (*it1), s[x] ^= 1;
	add(l + 1, x, x + 1, r, s[x] ? +k : -k);
}
int query(int l, int r, int k, int p) {
	set<int>::iterator it = st.lower_bound(l);
	fsum(l, r, p); return ((*it) < r ? 0 : -k);
}

int ans[MAXN + 5]; char str[MAXN + 5], op[10];
int main() {
	n = read() + 1, q = read() + 1; scanf("%s", str + 1);
	for(int i=1;i<n;i++) st.insert(i); st.insert(0), st.insert(n);
	for(int i=1;i<n;i++) if( str[i] - '0' ) modify(i, q);
	
	int qcnt = 0;
	for(int i=2,l,r;i<=q;i++) {
		scanf("%s", op);
		if( op[0] == 't' ) modify(read(), q - i + 1);
		else l = read(), r = read(), qcnt++, ans[qcnt] = query(l, r, q - i + 1, qcnt);
	}
	for(int i=1,root;i<=n;i++) {
		root = ncnt = 0;
		for(int j=0;j<v[i].size();j++) {
			pii p = v[i][j];
			if( p.fi > 0 ) update(root, 1, n, p.fi, p.se);
			else ans[p.se] += gsum(root, 1, n, -p.fi);
		}
	}
	for(int i=1;i<=qcnt;i++)
		printf("%d\n", ans[i]);
}

loj - 3156:转移是个 DAG,代价是个二次函数,直接上斜率优化 dp。可以先对 m 条边的出现时刻与结束时刻按时间排序做到 O(m) 的斜率优化(瓶颈在排序的 O(mlogm))。不理解我当时为什么写挂了5分,更不理解为什么NOI上会有这种题。

#include <cmath>
#include <deque>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long ll;
typedef long double ld;
typedef pair<ll, ll> pll;
#define fi first
#define se second
#define mp make_pair

const int MAXN = 100000, MAXM = 200000;
const ll INF = ll(6E18);
const ld inf = 9E18, EPS = 1E-9;

int dcmp(ld x) {return fabs(x) < EPS ? 0 : (x > 0 ? 1 : -1);}

ld slope(pll a, pll b) {
	if( a.fi == b.fi )
		return a.se < b.se ? inf : -inf;
	else return ((ld)(a.se - b.se)) / (a.fi - b.fi);
}

deque<pll>que[MAXN + 5];
void insert(int u, pll p) {
	while( !que[u].empty() ) {
		pll b = que[u].back(); que[u].pop_back();
		if( que[u].empty() || dcmp(slope(que[u].back(), b) - slope(b, p)) < 0 ) {
			que[u].push_back(b);
			break;
		}
	}
	que[u].push_back(p);
}
ll query(int u, ll k) {
	while( !que[u].empty() ) {
		pll f = que[u].front(); que[u].pop_front();
		if( que[u].empty() || dcmp(slope(f, que[u].front()) - k) > 0 ) {
			que[u].push_front(f);
			break;
		}
	}
	return que[u].front().se - k * que[u].front().fi;
}

struct node{
	int t, u, i;
	friend bool operator < (const node &a, const node &b) {
		return (a.t == b.t) ? (a.i < b.i) : (a.t < b.t);
	}
}a[2*MAXM + 5];

ll dp[MAXM + 5]; bool tag[MAXM + 5];
int main() {
	freopen("route.in", "r", stdin);
	freopen("route.out", "w", stdout);
	
	int n, m, A, B, C;
	scanf("%d%d%d%d%d", &n, &m, &A, &B, &C);
	for(int i=1,x,y,p,q;i<=m;i++) {
		scanf("%d%d%d%d", &x, &y, &p, &q);
		a[2*i - 1] = (node){p, x, i}, a[2*i] = (node){q, y, -i};
	}
	sort(a + 1, a + 2*m + 1);
	
	ll ans = INF; insert(1, mp(0, 0));
	for(int i=1;i<=2*m;i++) {
		if( a[i].i < 0 ) {
			if( tag[-a[i].i] ) {
				insert(a[i].u, mp(2*A*a[i].t, dp[-a[i].i] + 1LL*a[i].t*(A*a[i].t - B)));
				if( a[i].u == n ) ans = min(ans, dp[-a[i].i] + a[i].t);
			}
		} else {
			if( que[a[i].u].empty() ) continue;
			tag[a[i].i] = true;
			
			dp[a[i].i] = query(a[i].u, a[i].t) + 1LL*a[i].t*(A*a[i].t + B) + C;
		}
	}
	printf("%lld\n", ans);
}

codefoces - 1314C:把所有子串插入到 trie 中排序。二分答案子串 T,相当于求是否存在 ≥k 种划分,使得每个划分出来的串都 > T,可以 \(O(n^2m)\) dp。注意到如果 P > T,则以 P 为前缀的所有串都 > T,前缀和优化一下就成 \(O(nm)\) 了。总时间复杂度 \(O(nm\log A)\)

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

typedef long long ll;

const int MAXN = 1000;
const int MAXM = MAXN*MAXN;

int ch[26][MAXM + 5], dfn[MAXM + 5], ncnt, dcnt;
void dfs(int x) {
	dfn[x] = (++dcnt);
	for(int i=0;i<26;i++)
		if( ch[i][x] ) dfs(ch[i][x]);
}
char ans[MAXN + 5];
void print(int x, int k, int d) {
	if( dfn[x] == k ) puts(ans);
	for(int i=0;i<26;i++)
		if( ch[i][x] ) ans[d] = i + 'a', print(ch[i][x], k, d + 1), ans[d] = 0;
}

int id[MAXN + 5][MAXN + 5], n, m; ll k;
ll add(ll x, ll y) {return (x + y > k ? k : x + y);}
ll mul(ll x, ll y) {return (x > k / y ? k : x * y);}

int a[MAXN + 5]; ll f[MAXN + 5][MAXN + 5], sum[MAXN + 5][MAXN + 5];
bool check(int x) {
	for(int i=n;i>=1;i--) {
		a[i] = 0;
		while( i + a[i] <= n && id[i][i + a[i]] <= x )
			a[i]++;
	}
	
	f[n + 1][0] = sum[n + 1][0] = 1;
	for(int i=n;i>=1;i--) {
		for(int j=0;j<=m;j++) f[i][j] = 0;
		for(int j=1;j<=m;j++) f[i][j] = add(f[i][j], sum[i + a[i] + 1][j - 1]);
		for(int j=0;j<=m;j++) sum[i][j] = add(sum[i + 1][j], f[i][j]);
	}
	return f[1][m] < k;
}

char s[MAXN + 5];
int main() {
	scanf("%d%d%lld%s", &n, &m, &k, s + 1);
	for(int i=1;i<=n;i++) {
		int nw = 0;
		for(int j=i;j<=n;j++) {
			if( ch[s[j] - 'a'][nw] == 0 )
				ch[s[j] - 'a'][nw] = (++ncnt);
			id[i][j] = nw = ch[s[j] - 'a'][nw];
		}
	}
	
	dfs(0);
	for(int i=1;i<=n;i++)
		for(int j=i;j<=n;j++)
			id[i][j] = dfn[id[i][j]];
	
	int l = 1, r = dcnt;
	while( l < r ) {
		int mid = (l + r) >> 1;
		if( check(mid) ) r = mid;
		else l = mid + 1;
	}
	print(0, r, 0);
}

codefoces - 995F:设 dp(i, j) 表示以 i 为根的子树中 i 的工资为 j 的方案数。发现转移只有两类(1)对应位置相乘(2)前缀和,那么 dp(i, j) 是一个关于 j 的 \(O(size_i)\) 次多项式。求出前 \(O(n)\) 项 dp 值然后拉格朗日插值。什么时候div1F变成2700难度了。

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

const int MAXN = 3000;
const int MOD = int(1E9) + 7;

inline int add(int x, int y) {x += y; return (x >= MOD ? x - MOD : x);}
inline int sub(int x, int y) {x -= y; return (x < 0 ? x + MOD : x);}
inline int mul(int x, int y) {return 1LL * x * y % MOD;}

int pow_mod(int b, int p) {
	int ret = 1;
	for(int i=p;i;i>>=1,b=mul(b,b))
		if( i & 1 ) ret = mul(ret, b);
	return ret;
}

int ifct[MAXN + 5];
void init() {
	ifct[0] = 1; for(int i=1;i<=MAXN;i++) ifct[i] = mul(ifct[i - 1], i);
	for(int i=0;i<=MAXN;i++) ifct[i] = pow_mod(ifct[i], MOD - 2);
}

int lf[MAXN + 5], rf[MAXN + 5];
int get_y(int n, int *y, int x) {
	lf[0] = 1; for(int i=1;i<=n;i++) lf[i] = mul(lf[i - 1], sub(x, i));
	rf[n + 1] = 1; for(int i=n;i>=1;i--) rf[i] = mul(rf[i + 1], sub(x, i));
	
	int ans = 0;
	for(int i=1;i<=n;i++) {
		int del = mul(mul(lf[i - 1], rf[i + 1]), mul(ifct[i - 1], ifct[n - i]));
		
		ans = ((n - i) & 1) ? sub(ans, mul(del, y[i])) : add(ans, mul(del, y[i]));
	}
	return ans;
}

int dp[MAXN + 5][MAXN + 5];

int p[MAXN + 5], n, D;
int main() {
	init(), scanf("%d%d", &n, &D);
	for(int i=2;i<=n;i++) scanf("%d", &p[i]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n+1;j++)
			dp[i][j] = 1;
	for(int i=n;i>=1;i--) {
		for(int j=1;j<=n+1;j++) dp[i][j] = add(dp[i][j], dp[i][j - 1]);
		for(int j=1;j<=n+1;j++) dp[p[i]][j] = mul(dp[p[i]][j], dp[i][j]);
	}
	printf("%d\n", get_y(n + 1, dp[1], D));
}

uoj - 240:题目所述的矩形可以看成区间,限制点 (x, y) 即某个选择的区间完全包含区间 [min(x,y), max(x,y)]。丢弃包含的限制点,可以写出一个 O(n^2*k) 的 dp。当 k > n' 时可以直接算;当 k ≤ n' 时最优恰好选 k 个。然后就是带权二分 + 斜率优化。

#include "aliens.h"
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;

typedef vector<int> vi;
typedef long long ll;
typedef long double ld;

const int MAXN = 100000;
const ll INF = ll(1E13);
const ld EPS = 1E-9;

int dcmp(ld x) {return fabs(x) < EPS ? 0 : (x < 0 ? -1 : 1);}

struct node{
	int l, r;
	friend bool operator < (node a, node b) {
		return (a.l == b.l) ? (a.r > b.r) : (a.l < b.l);
	}
}a[MAXN + 5];

void init(int &n, const vi &r, const vi &c) {
	for(int i=0;i<n;i++)
		a[i + 1] = (node){min(r[i], c[i]), max(r[i], c[i])};
	sort(a + 1, a + n + 1);
	
	int rmost = -1, p = 0;
	for(int i=1;i<=n;i++) {
		if( rmost >= a[i].r ) continue;
		rmost = max(rmost, a[i].r), swap(a[i], a[++p]);
	}
	a[0] = (node){-1, -1}, n = p;
}

ll pw2(ll x) {return x * x;}
ll trans(int i) {return (a[i+1].l > a[i].r) ? 0 : (pw2(a[i].r - a[i+1].l + 1));}

struct point{ll x, y;};
ld slope(point p, point q) {return ((ld) (p.y - q.y)) / (p.x - q.x);}
struct state{point p; int k;}que[MAXN + 5]; int s, t;
void add_point(state p) {
	while( s < t ) {
		/*if( dcmp(slope(que[t - 1].p, que[t].p) - slope(que[t].p, p.p)) > 0 ||
			(dcmp(slope(que[t - 1].p, que[t].p) - slope(que[t].p, p.p)) == 0 && p.k < que[t].k) )*/
		ll d = (que[t].p.y - que[t-1].p.y) * (p.p.x - que[t].p.x) - (p.p.y - que[t].p.y) * (que[t].p.x - que[t-1].p.x);
		if( d > 0 || (d == 0 && p.k < que[t].k) )
			t--;
		else break;
	}
	que[++t] = p;
}
state query(ll k) {
	while( s < t ) {
		/*if( dcmp(slope(que[s].p, que[s + 1].p) - k) < 0 ||
			(dcmp(slope(que[s].p, que[s + 1].p) - k) == 0 && que[s].k > que[s + 1].k) )*/
		ll d = (que[s + 1].p.y - que[s].p.y) - k * (que[s + 1].p.x - que[s].p.x);
		if( d < 0 || (d == 0 && que[s].k > que[s + 1].k) )
			s++;
		else break;
	}
	return que[s];
}

ll dp[MAXN + 5]; int cnt[MAXN + 5];
int check(int n, ll del) {
	que[s = t = 1] = (state){(point){a[1].l, pw2(a[1].l)}, 0};
	for(int i=1;i<=n;i++) {
		state p = query(2*(a[i].r + 1));
		cnt[i] = p.k + 1, dp[i] = p.p.y - 2*(a[i].r + 1)*p.p.x + pw2(a[i].r + 1) + del;
		if( i != n ) add_point((state){(point){a[i + 1].l, dp[i] + pw2(a[i + 1].l) - trans(i)}, cnt[i]});
	}
	return cnt[n];
}

ll take_photos(int n, int m, int k, vi r, vi c) {
	init(n, r, c);
	
/*
	for(int i=1;i<=n;i++) {
		for(int j=0;j<=k;j++) dp[i][j] = INF;
		for(int j=1;j<=i;j++)
			for(int p=1;p<=k;p++)
				dp[i][p] = min(dp[i][p], dp[j-1][p-1] + pw2(a[i].r-a[j].l+1) - trans(j-1));
	}
	
	ll ans = INF;
	for(int i=1;i<=k;i++)
		ans = min(ans, dp[n][i]);
	return ans;
*/
	
	if( k >= n ) {
		ll ans = 0;
		for(int i=1;i<=n;i++)
			ans = ans + pw2(a[i].r - a[i].l + 1) - trans(i - 1);
		return ans;
	} else {
		ll le = -INF, ri = INF;
		while( le < ri ) {
			ll mid = (ll)floor((le + ri) / 2.0);
			if( check(n, mid) <= k ) ri = mid;
			else le = mid + 1;
		}
		check(n, le); return dp[n] - le*k;
	}
}

loj - 3058:单位根反演。最后得到 \(ans_t = \sum_{i=0}^{k-1} a_iw_{k}^{-it}\)。做神奇变换(据说叫作 chirp-Z 变换)\(w_k^{-it} = w_k^{{t\choose 2}+{i\choose2}-{i+t\choose 2}}\),这样就可以把乘积项拆解。然后任意模数 fft。

#include <cmath>

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

typedef long double ld;
typedef long long ll;
#define rep(i, x, n) for(int i=x;i<n;i++)

const int MAXK = 65536;
const int SQRT = 32768;
const ld PI = acos(-1);

int p;
inline int add(int x, int y) {x += y; return x >= p ? x - p : x;}
inline int sub(int x, int y) {x -= y; return x < 0 ? x + p : x;}
inline int mul(int x, int y) {return (int)(1LL * x * y % p);}

struct complex{
	ld r, i;
	friend complex operator + (const complex &x, const complex &y) {
		return (complex){x.r + y.r, x.i + y.i};
	}
	friend complex operator - (const complex &x, const complex &y) {
		return (complex){x.r - y.r, x.i - y.i};
	}
	friend complex operator * (const complex &x, const complex &y) {
		return (complex){x.r*y.r - x.i*y.i, x.i*y.r + y.i*x.r};
	}
	friend complex operator / (const complex x, const double k) {
		return (complex){x.r / k, x.i / k};
	}
	friend complex conj(const complex x) {
		return (complex){x.r, -x.i};
	}
};
void fft(complex *A, int n, int type) {
	for(int i=0,j=0;i<n;i++) {
		if( i < j ) swap(A[i], A[j]);
		for(int k=(n>>1);(j^=k)<k;k>>=1);
	}
	for(int i=1,s=2,t=1;s<=n;i++,s<<=1,t<<=1) {
		complex u = (complex){cos(type*2*PI/s), sin(type*2*PI/s)};
		for(int j=0;j<n;j+=s) {
			complex r = (complex){1, 0};
			for(int k=0;k<t;k++,r=r*u) {
				complex x = A[j + k], y = A[j + k + t] * r;
				A[j + k] = x + y, A[j + k + t] = x - y;
			}
		}
	}
	if( type == -1 ) {
		for(int i=0;i<n;i++)
			A[i] = A[i] / n;
	}
}
int length(int n) {
	int len; for(len = 1; len < n; len <<= 1);
	return len;
}

complex a1[6*MAXK + 5], b1[6*MAXK + 5];
complex ta[6*MAXK + 5], tb[6*MAXK + 5];
void poly_mul(int *A, int n, int *B, int m, int *C) {
	int len = length(n + m - 1);
	rep(i, 0, n) a1[i] = (complex){(ld)(A[i] / SQRT), -(ld)(A[i] % SQRT)};
	rep(i, 0, m) b1[i] = (complex){(ld)(B[i] / SQRT), -(ld)(B[i] % SQRT)};
	fft(a1, len, 1), fft(b1, len, 1);
	
	rep(i, 0, len) {
		complex p1 = (a1[i] + conj(a1[i == 0 ? 0 : len - i])) / 2;
		complex p2 = (a1[i] - conj(a1[i == 0 ? 0 : len - i])) / 2 * (complex){0, 1};
		
		complex q1 = (b1[i] + conj(b1[i == 0 ? 0 : len - i])) / 2;
		complex q2 = (b1[i] - conj(b1[i == 0 ? 0 : len - i])) / 2 * (complex){0, 1};
		
		ta[i] = p1*q1 + (complex){0, 1}*p2*q2, tb[i] = p1*q2 + p2*q1;
	}
	fft(ta, len, -1), fft(tb, len, -1);
	for(int i=0;i<n+m-1;i++)
		C[i] = add(mul(SQRT, add(mul(SQRT, (ll)(ta[i].r + 0.5) % p), (ll)(tb[i].r + 0.5) % p)), (ll)(ta[i].i + 0.5) % p);
}

int pow_mod(int b, int k) {
	int ret = 1;
	for(int i=k;i;i>>=1,b=mul(b,b))
		if( i & 1 ) ret = mul(ret, b);
	return ret;
}

int a[50], cnt;
int find_root() {
	int lim = (int)sqrt(p - 1), t = p - 1;
	rep(i, 2, lim + 1) {
		if( t % i == 0 ) {
			a[++cnt] = (p - 1) / i;
			while( t % i == 0 )
				t /= i;
		}
	}
	if( t != 1 ) a[++cnt] = (p - 1) / t;
	rep(i, 2, p) {
		bool flag = true;
		for(int j=1;j<=cnt;j++)
			if( pow_mod(i, a[j]) == 1 ) {
				flag = false;
				break;
			}
		if( flag ) return i;
	}
	return -1;
}

int n;
struct matrix{
	int m[3][3];
	friend matrix operator * (const matrix &A, const matrix &B) {
		matrix C; rep(i, 0, n) rep(j, 0, n) C.m[i][j] = 0;
		rep(i, 0, n) rep(k, 0, n) rep(j, 0, n)
			C.m[i][j] = add(C.m[i][j], mul(A.m[i][k], B.m[k][j]));
		return C;
	}
}A, M;
matrix mpow(matrix B, int L) {
	matrix R; rep(i, 0, n) rep(j, 0, n) R.m[i][j] = (i == j);
	for(int i=L;i;i>>=1,B=B*B)
		if( i & 1 ) R = R*B;
	return R;
}

int w[MAXK + 5], k;
void get_w(int g) {
	for(int i=0;i<k;i++)
		w[i] = pow_mod(g, (p - 1) / k * i);
}

int f[2*MAXK + 5], g[MAXK + 5], ans[3*MAXK + 5];
int main() {
	int x, y, L; scanf("%d%d%d%d%d%d", &n, &k, &L, &x, &y, &p), x--, y--;
	rep(i, 0, n) rep(j, 0, n) scanf("%d", &A.m[i][j]);
	
	get_w(find_root()); int ivk = pow_mod(k, p - 2);
	rep(i, 0, k) {
		rep(s, 0, n) rep(t, 0, n)
			M.m[s][t] = add(mul(w[i], A.m[s][t]), s == t);
		g[i] = mul(mul(mpow(M, L).m[x][y], ivk), w[1LL*i*(i+1)/2%k]);
	}
	rep(i, 0, 2*k) f[i] = w[(k - 1LL*i*(i+1)/2%k)%k]; reverse(f, f + 2*k);
	poly_mul(f, 2*k, g, k, ans), reverse(ans, ans + 2*k);
	rep(i, 0, k) printf("%d\n", mul(ans[i], w[1LL*i*(i+1)/2%k]));
}

loj - 3054:身体和尾巴分开统计。尾巴以每个点为原点做极角扫描线,每个点处理 3 次:加入待选点;从待选点中删除;询问。先删后询再加。身体处理出每对点的中垂线与连线,共线拿出来扫一遍统计答案。需要避免使用浮点数(如不要把极角算出来再排序,误差大)。细节较多。

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

typedef long long ll;
typedef pair<int, int> pii;
#define fi first
#define se second
#define pr make_pair

const int MAXN = 1000;

struct point{
	ll x, y; point() {}
	point(ll _x, ll _y) : x(_x), y(_y) {}
	friend point operator + (const point &a, const point &b) {return point(a.x + b.x, a.y + b.y);}
	friend point operator - (const point &a, const point &b) {return point(a.x - b.x, a.y - b.y);}
	friend point operator * (const point &a, const ll &k) {return point(a.x * k, a.y * k);}
	friend ll operator * (const point &a, const point &b) {return a.x*b.x + a.y*b.y;}
	friend ll operator ^ (const point &a, const point &b) {return a.x*b.y - a.y*b.x;}
	friend point normal(const point &a) {return point(a.y, -a.x);}
	friend ll length(const point &a) {return a * a;}
	friend point middle(const point &a, const point &b) {return point((a.x + b.x) / 2, (a.y + b.y) / 2);}
}pnt[MAXN + 5]; int n;

bool cmp(const point &a, const point &b) {
	if( a.y >= 0 && b.y < 0 ) return false;
	else if( a.y < 0 && b.y >= 0 ) return true;
	else if( a.y == 0 && b.y == 0 ) return a.x > 0 && b.x < 0;
	else return (a ^ b) > 0;
}
bool equal(const point &a, const point &b) {
	return !(cmp(a, b)) && !(cmp(b, a));
}

struct node{
	point p; int id, type;
	friend bool operator < (const node &a, const node &b) {
		return equal(a.p, b.p) ? a.type < b.type : cmp(a.p, b.p);
	}
};
vector<node>v;
int ans1[MAXN + 5][MAXN + 5], t[MAXN + 5], nwans1;
void add(int x) {nwans1 += 2*t[x]; t[x]++;}
void erase(int x) {t[x]--; nwans1 -= 2*t[x];}
ll d[MAXN + 5]; int lens[MAXN + 5], dcnt;
void solve1() {
	for(int i=1;i<=n;i++) {
		dcnt = 0;
		for(int j=1;j<=n;j++)
			if( i != j ) d[++dcnt] = length(pnt[j] - pnt[i]);
		sort(d + 1, d + dcnt + 1), dcnt = unique(d + 1, d + dcnt + 1) - d - 1;
		for(int j=1;j<=n;j++)
			if( i != j ) lens[j] = lower_bound(d + 1, d + dcnt + 1, length(pnt[j] - pnt[i])) - d;
		
		v.clear(); nwans1 = 0;
		for(int j=1;j<=dcnt;j++) t[j] = 0;
		for(int j=1;j<=n;j++)
			if( i != j ) {
				v.push_back((node){pnt[i] - pnt[j], j, 1});
				v.push_back((node){normal(pnt[j] - pnt[i]), j, 2});
				v.push_back((node){pnt[j] - pnt[i], j, 3});
			}
		
		sort(v.begin(), v.end());
		for(int j=1;j<=n;j++)
			if( i != j && (pnt[j].y > pnt[i].y || (pnt[j].y == pnt[i].y && pnt[j].x < pnt[i].x)) )
				add(lens[j]);
		
		for(unsigned j=0;j<v.size();j++) {
			if( v[j].type == 1 ) erase(lens[v[j].id]);
			else if( v[j].type == 3 ) add(lens[v[j].id]);
			else {
				int x = min(i, v[j].id), y = max(i, v[j].id);
				ans1[x][y] += nwans1;
			}
		}
	}
}

struct line{
	point a, ab; line() {}
	line(point _a, point _ab) : a(_a) {
		ab = (_ab.x + _ab.y < 0 || (_ab.x + _ab.y == 0 && _ab.x < 0)) ? point(-_ab.x, -_ab.y) : _ab;
	}
	bool on_left(point x) {return (ab ^ (x - a)) > 0;}
	friend bool operator < (line a, line b) {
		return equal(a.ab, b.ab) ? a.on_left(b.a) : cmp(a.ab, b.ab);
	}
	friend bool operator == (line a, line b) {
		return (a.ab ^ b.ab) == 0 && (a.ab ^ (b.a - a.a)) == 0;
	}
};
struct type{
	line l; int id;
	friend bool operator < (type a, type b) {
		return (a.l < b.l);
	}
};
vector<type>vl;

ll d2[MAXN*MAXN + 5]; int dcnt2;
int sum[MAXN*MAXN + 5], pos[MAXN*MAXN + 5];
vector<pii>v1; int ans2[MAXN + 5][MAXN + 5];
void solve2() {
	for(int i=1;i<=n;i++)
		pnt[i].x *= 2, pnt[i].y *= 2;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if( i != j ) {
				line l = line(middle(pnt[i], pnt[j]), normal(pnt[i] - pnt[j]));
				vl.push_back((type){l, -1});
				l = line(pnt[i], pnt[i] - pnt[j]);
				vl.push_back((type){l, i});
			}
	sort(vl.begin(), vl.end());
	for(unsigned i=0,j;i<vl.size();i=j+1) {
		for(j = i; j < vl.size() - 1 && vl[i].l == vl[j + 1].l; j++);
		
		if( vl[i].l.ab.x == 0 ) {
			dcnt2 = 0;
			for(unsigned p=i;p<=j;p++)
				d2[++dcnt2] = vl[p].l.a.y;
			sort(d2 + 1, d2 + dcnt2 + 1), dcnt2 = unique(d2 + 1, d2 + dcnt2 + 1) - d2 - 1;
			
			for(int p=1;p<=dcnt2;p++)
				sum[p] = 0, pos[p] = -1;
			for(unsigned p=i;p<=j;p++) {
				int x = lower_bound(d2 + 1, d2 + dcnt2 + 1, vl[p].l.a.y) - d2;
				if( vl[p].id < 0 ) sum[x]++;
				else pos[x] = vl[p].id;
			}
		} else {
			dcnt2 = 0;
			for(unsigned p=i;p<=j;p++)
				d2[++dcnt2] = vl[p].l.a.x;
			sort(d2 + 1, d2 + dcnt2 + 1), dcnt2 = unique(d2 + 1, d2 + dcnt2 + 1) - d2 - 1;
			
			for(int p=1;p<=dcnt2;p++)
				sum[p] = 0, pos[p] = -1;
			for(unsigned p=i;p<=j;p++) {
				int x = lower_bound(d2 + 1, d2 + dcnt2 + 1, vl[p].l.a.x) - d2;
				if( vl[p].id < 0 ) sum[x]++;
				else pos[x] = vl[p].id;
			}
		}
			
		v1.clear(); int tmp = 0;
		for(int p=1;p<=dcnt2;p++) {
			if( pos[p] != -1 ) {
				for(unsigned q=0;q<v1.size();q++) {
					int x = min(v1[q].fi, pos[p]), y = max(v1[q].fi, pos[p]);
					ans2[x][y] += tmp - v1[q].se;
				}
			}
			tmp += sum[p];
			if( pos[p] != -1 )
				v1.push_back(pr(pos[p], tmp));
		}
	}
}

int main() {
	scanf("%d", &n);
	for(int i=1,x,y;i<=n;i++)
		scanf("%d%d", &x, &y), pnt[i] = point(x, y);
	solve1(), solve2();
	
	ll ans = 0;
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++) {
			ans += 1LL*ans1[i][j]*ans2[i][j];
//			printf("%d %d : %d %d\n", i, j, ans1[i][j], ans2[i][j]);
		}
	printf("%lld\n", ans);
}

loj - 2540:生成一个随机排列的过程等价于生成一个无限长的可重复序列,取每个元素的第一次出现位置。注意到这道题中重复加点不会使最大独立集变大,因此可以直接等价转化成生成一个无限长的可重复序列。然后直接 dp 当前最大独立集是什么即可。

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

const int MOD = 998244353;

inline int add(int x, int y) {x += y; return x >= MOD ? x - MOD : x;}
inline int sub(int x, int y) {x -= y; return x < 0 ? x + MOD : x;}
inline int mul(int x, int y) {return (int)(1LL * x * y % MOD);}

int pow_mod(int b, int p) {
	int ret = 1;
	for(int i=p;i;i>>=1,b=mul(b,b))
		if( i & 1 ) ret = mul(ret, b);
	return ret;
}

int G[20], inv[25], n, m, t;
bool tg[1<<20]; int bts[1<<20], dp[1<<20];
int main() {
	scanf("%d%d", &n, &m), t = (1 << n);
	for(int i=1,u,v;i<=m;i++) {
		scanf("%d%d", &u, &v), u--, v--;
		G[u] |= (1 << v), G[v] |= (1 << u);
	}
	
	int mx = 0; tg[0] = true;
	for(int s=1;s<t;s++) {
		for(int i=0;i<n;i++)
			if( (s >> i) & 1 ) {
				tg[s] = tg[s^(1<<i)] && !(G[i] & s);
				break;
			}
		bts[s] = bts[s >> 1] + (s & 1);
		if( tg[s] ) mx = max(mx, bts[s]);
	}
	
	for(int i=1;i<=n;i++) inv[i] = pow_mod(i, MOD - 2);
	for(int s=t-1;s>=0;s--) {
		if( !tg[s] ) continue;
		int sum = 0, cnt = 0;
		for(int i=0;i<n;i++)
			if( !((s >> i) & 1) && tg[s|(1<<i)] )
				sum = add(sum, dp[s|(1<<i)]), cnt++;
		if( cnt == 0 ) dp[s] = (bts[s] == mx);
		else dp[s] = mul(sum, inv[cnt]);
	}
	printf("%d\n", dp[0]);
}

loj - 2529:二分每个点 x 所能影响到的左边界(右边界同理),找到能在 x 到达之前到达二分值的点 y 距离二分值的最短距离,\(O(KlogK)\) 预处理一下 st 表 + 二分查找即可。在同一时间以同一距离到达同一个点的算右边的(不然会算重)。时间复杂度 \(O(nlog^2n)\)\(\sum K\)\(n\) 同阶)。

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

typedef long long ll;
typedef pair<int, ll> pil;
#define fi first
#define se second
#define pr make_pair

const int MAXN = 200000;
const ll INF = ll(1E18);

pil a[MAXN + 5]; ll sum[MAXN + 5]; int n, m, K;

ll st[2][20][MAXN + 5]; int lg[MAXN + 5];
void init_st() {
	for(int i=2;i<=K;i++) lg[i] = lg[i >> 1] + 1;
	for(int i=1;i<=K;i++) {
		st[0][0][i] = a[i].se + sum[a[i].fi];
		st[1][0][i] = a[i].se - sum[a[i].fi];
	}
	for(int o=0;o<=1;o++) {
		for(int j=1;j<=lg[K];j++) {
			int t = (1 << (j - 1));
			for(int i=1;i+t<=K;i++)
				st[o][j][i] = min(st[o][j - 1][i], st[o][j - 1][i + t]);
		}
	}
}
ll rmq(int o, int l, int r) {
	if( l > r ) return INF;
	int k = lg[r - l + 1], p = (1 << k);
	return min(st[o][k][l], st[o][k][r - p + 1]);
}

int lf[MAXN + 5];
void getl(int x) {
	int le = 1, ri = a[x].fi;
	while( le < ri ) {
		int mid = (le + ri) >> 1;
		int ql = upper_bound(a + 1, a + K + 1, pr(2*mid - a[x].fi, INF)) - a;
		int qm = lower_bound(a + 1, a + K + 1, pr(mid, -1LL)) - a - 1;
		int qr = lower_bound(a + 1, a + K + 1, pr(a[x].fi, -1LL)) - a - 1;
		
		bool flag = false;
		if( sum[mid] + rmq(1, ql, qm) <= st[0][0][x] - sum[mid] )
			flag = true;
		else if( rmq(0, qm + 1, qr) - sum[mid] <= st[0][0][x] - sum[mid] )
			flag = true;
		else if( 1 <= ql - 1 && a[ql - 1].fi + a[x].fi == 2*mid &&
			sum[mid] + st[1][0][ql - 1] < st[0][0][x] - sum[mid] )
			flag = true;
/*
		for(int p=1;p<=K;p++) {
			if( l < a[p].fi && a[p].fi < r ) {
				if( dist(p, mid) <= dist(x, mid) ) {
					flag = true;
					break;
				}
			} else if( a[p].fi == l ) {
				if( dist(p, mid) < dist(x, mid) ) {
					flag = true;
					break;
				}
			}
		}
*/
		if( flag ) le = mid + 1;
		else ri = mid;
	}
	lf[x] = ri;
}

int rf[MAXN + 5];
void getr(int x) {
	int le = a[x].fi, ri = n;
	while( le < ri ) {
		int mid = (le + ri + 1) >> 1;
		int ql = upper_bound(a + 1, a + K + 1, pr(a[x].fi, INF)) - a;
		int qm = lower_bound(a + 1, a + K + 1, pr(mid, -1LL)) - a - 1;
		int qr = upper_bound(a + 1, a + K + 1, pr(2*mid - a[x].fi, INF)) - a - 1;
		
		bool flag = false;
		if( sum[mid] + rmq(1, ql, qm) <= st[1][0][x] + sum[mid] )
			flag = true;
		else if( rmq(0, qm + 1, qr) - sum[mid] <= st[1][0][x] + sum[mid] )
			flag = true;
/*
		for(int p=1;p<=K;p++) {
			if( l < a[p].fi && a[p].fi <= r ) {
				if( dist(p, mid) <= dist(x, mid) ) {
					flag = true;
					break;
				}
			}
		}
*/
		if( flag ) ri = mid - 1;
		else le = mid;
	}
	rf[x] = le;
}

int main() {	
	scanf("%d%d", &n, &m);
	for(int i=2;i<=n;i++)
		scanf("%lld", &sum[i]), sum[i] += sum[i - 1];
	
	for(int i=1;i<=m;i++) {
		scanf("%d", &K);
		for(int j=1;j<=K;j++)
			scanf("%d%lld", &a[j].fi, &a[j].se);
		sort(a + 1, a + K + 1), init_st();
		
		for(int j=1;j<=K;j++)
			getl(j), getr(j);
			
		ll ans = 0;
		for(int j=1;j<=K;j++)
			ans += (rf[j] - lf[j] + 1);
		printf("%lld\n", ans);
	}
}

loj - 6517:一看就是莫队但是不知道怎么莫队。先建 trie。考虑把字符串拼在一起形成长度为 \(O(\sum |S|)\) 的序列,之后在新序列上用回滚莫队(从大往小滚) + 链表就可以 \(O(n\sqrt{n})\) 了。

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

typedef long long ll;
typedef pair<ll, ll> pll;

#define fi first
#define se second
#define pr make_pair
#define rep(i, x, n) for(int i=x;i<=n;i++)
#define per(i, x, n) for(int i=x;i>=n;i--)
#define pb push_back

const int MAXN = 100000;
const int MAXS = 300000;
const int BLOCK = 950;

ll gcd(ll x, ll y) {return (y == 0 ? x : gcd(y, x % y));}
void print(pll p) {
	ll d = gcd(p.fi, p.se); p.fi /= d, p.se /= d;
	printf("%lld/%lld\n", p.fi, p.se);
}

struct query{
	int l, r, id;
	friend bool operator < (const query &a, const query &b) {
		return a.r > b.r;
	}
}; vector<query>qry[BLOCK + 5];

struct modify{ll *p, k;}stk[10*MAXS + 5]; int tp;
void restore(int tim) {while( tp > tim ) (*stk[tp].p) = stk[tp].k, tp--;}

ll tot[MAXS + 5], lst[MAXS + 5], nxt[MAXS + 5], nwans;
void update(int x) {
	stk[++tp] = (modify){&nwans, nwans}, nwans -= (nxt[x] - x) * (x - lst[x]);
	stk[++tp] = (modify){nxt + lst[x], nxt[lst[x]]}, nxt[lst[x]] = nxt[x];
	stk[++tp] = (modify){lst + nxt[x], lst[nxt[x]]}, lst[nxt[x]] = lst[x];
}

ll cnt[MAXS + 5], f[MAXS + 5], vs[MAXS + 5], lb[MAXS + 5];
ll vis[MAXS + 5]; int id[MAXS + 5], g[MAXS + 5], dep[MAXS + 5];
void remove(int x) {
	stk[++tp] = (modify){f + id[x], f[id[x]]}, f[id[x]] -= vs[x];
	stk[++tp] = (modify){cnt + id[x], cnt[id[x]]}, cnt[id[x]]--;
	
	int y = dep[id[x]];
	if( !vis[id[x]] && (cnt[id[x]] == 0 || f[id[x]] < lb[y]) ) {
		stk[++tp] = (modify){tot + g[y], tot[g[y]]}, tot[g[y]]--;
		stk[++tp] = (modify){vis + id[x], vis[id[x]]}, vis[id[x]] = 1;
		if( tot[g[y]] == 0 ) update(g[y]);
	}
}

ll A, B, C; pll ans[MAXN + 5];
int len[MAXN + 5], N, M, L, ncnt;
void solve() {
	rep(i, 0, len[N] - 1) f[id[i]] += vs[i], cnt[id[i]]++;
	rep(i, 1, ncnt) {
		if( f[i] >= lb[dep[i]] )
			tot[g[dep[i]]]++;
		else vis[i] = true;
	}
	
	int lt = 0, rt = L + 1;
	rep(i, 1, L)
		if( tot[i] ) {
			nwans += 1LL*(i - lt)*(L - i + 1);
			lst[i] = lt, lt = i;
		}
	per(i, L, 1) if( tot[i] ) nxt[i] = rt, rt = i;
	lst[L + 1] = lt, nxt[0] = rt;
	
	ll sum = 1LL * L * (L + 1) / 2;
	rep(i, 0, BLOCK) {
		sort(qry[i].begin(), qry[i].end());
		
		int bl = i*BLOCK, tim1 = tp, nwr = len[N] - 1;
		for(unsigned j=0;j<qry[i].size();j++) {
			query q = qry[i][j];
			while( nwr > q.r ) remove(nwr--);
			
			int tim = tp;
			for(int p=bl;p<q.l;p++) remove(p);
			ans[q.id] = pr(nwans, sum), restore(tim);
		}
		int br = min((i + 1)*BLOCK - 1, len[N] - 1);
		restore(tim1); rep(j, bl, br) remove(j);
	}
}

char str[MAXS + 5]; ll v[MAXN + 5]; int ch[26][MAXS + 5];
int main() {
	scanf("%d%lld%lld%lld", &N, &A, &B, &C);
	rep(i, 1, N) scanf("%lld", &v[i]);
	rep(i, 1, N) {
		scanf("%s", str + len[i - 1]);
		
		int nwl = strlen(str + len[i - 1]);
		L = max(L, nwl), len[i] = len[i - 1] + nwl;
		
		int nw = 0;
		rep(j, len[i - 1], len[i] - 1) {
			if( ch[str[j] - 'a'][nw] == 0 )
				dep[ch[str[j] - 'a'][nw] = (++ncnt)] = dep[nw] + 1;
			id[j] = nw = ch[str[j] - 'a'][nw], vs[j] = v[i];
		}
	}
	rep(i, 1, L) {
		scanf("%d", &g[i]);
		lb[i] = (C < A*i ? 0 : (C - A*i + B - 1) / B);
	}
	scanf("%d", &M);
	rep(i, 1, M) {
		int l, r; scanf("%d%d", &l, &r), l = len[l - 1], r = len[r] - 1;
		qry[l / BLOCK].pb((query){l, r, i});
	}
	solve();
	rep(i, 1, M) print(ans[i]);
}

loj - 6518:论文题。详见《浅谈保序回归问题 高睿泉》。其中证明了全局最优解对应了将值域区间缩到 [a, a + 1](即 ≤a 的变成 a;>a 的变成 a + 1)的最优解。于是二分 + 最大权闭合子图。

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

const int MAXN = 5000;
const int MAXM = 15000;
const int INF = (1 << 30);

namespace FlowGraph{
	const int MAXV = 50*MAXN;
	const int MAXE = 100*MAXM;
	
	struct edge{
		int to, cap, flow;
		edge *nxt, *rev;
	}edges[MAXE + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt;
	int n, s, t;
	void clear(int _n) {
		n = _n, ecnt = edges;
		for(int i=0;i<=n;i++)
			adj[i] = NULL;
	}
	void addedge(int u, int v, int c) {
		edge *p = (++ecnt), *q = (++ecnt);
		(*p) = (edge){v, c, 0, adj[u], q}, adj[u] = p;
		(*q) = (edge){u, 0, 0, adj[v], p}, adj[v] = q;
//		printf("! %d %d %d\n", u, v, c);
	}
	int dis[MAXV + 5], que[MAXV + 5], hd, tl;
	bool relabel() {
		for(int i=0;i<=n;i++)
			dis[i] = n + 5, cur[i] = adj[i];
		dis[que[hd = tl = 1] = t] = 0;
		while( hd <= tl ) {
			int x = que[hd++];
			for(edge *p=adj[x];p;p=p->nxt) {
				if( dis[p->to] > dis[x] + 1 && p->rev->cap > p->rev->flow )
					dis[p->to] = dis[x] + 1, que[++tl] = p->to;
			}
		}
		return !(dis[s] == n + 5);
	}
	int aug(int x, int tot) {
		if( x == t ) return tot;
		int sum = 0;
		for(edge *&p=cur[x];p;p=p->nxt) {
			if( p->cap > p->flow && dis[p->to] + 1 == dis[x] ) {
				int del = aug(p->to, min(tot - sum, p->cap - p->flow));
				p->flow += del, p->rev->flow -= del, sum += del;
				if( sum == tot ) break;
			}
		}
		return sum;
	}
	int max_flow(int _s, int _t) {
		int flow = 0; s = _s, t = _t;
		while( relabel() )
			flow += aug(s, INF);
		return flow;
	}
}

int abs(int x) {return x >= 0 ? x : -x;}

int a[MAXN + 5], N, M, ans;

bool tag[FlowGraph::MAXV + 5]; int b[MAXN + 5], d[MAXN + 5], dcnt;
int le[2][MAXN + 5], ri[2][MAXN + 5];

int tmp[MAXN + 5], id[2][4*MAXN + 5];
void build_segtree(int x, int l, int r, int &cnt) {
	if( l == r ) {
		id[0][x] = id[1][x] = tmp[l];
		return ;
	}
	else {
		id[0][x] = (++cnt), id[1][x] = (++cnt);
		int m = (l + r) >> 1;
		build_segtree(x << 1, l, m, cnt);
		build_segtree(x << 1 | 1, m + 1, r, cnt);
	}
}
void link_segtree(int f, int x, int l, int r) {
	if( f ) {
		FlowGraph::addedge(id[1][f], id[1][x], INF);
		FlowGraph::addedge(id[0][x], id[0][f], INF);
	}
	if( l == r )  return ;
	int m = (l + r) >> 1;
	link_segtree(x, x << 1, l, m);
	link_segtree(x, x << 1 | 1, m + 1, r);
}
void add_edge(int x, int l, int r, int ql, int qr, int y, int o) {
	if( ql > r || qr < l ) return ;
	if( ql <= l && r <= qr ) {
		if( o == 0 ) FlowGraph::addedge(id[o][x], y, INF);
		else FlowGraph::addedge(y, id[o][x], INF);
		return ;
	}
	int m = (l + r) >> 1;
	add_edge(x << 1, l, m, ql, qr, y, o);
	add_edge(x << 1 | 1, m + 1, r, ql, qr, y, o);
}
void dfs(int x) {
	if( tag[x] ) return ;
	tag[x] = true;
	for(FlowGraph::edge *p=FlowGraph::adj[x];p;p=p->nxt)
		if( p->cap > p->flow ) dfs(p->to);
}
void clear_tag(int x) {
	if( tag[x] ) tag[x] = false;
	else return ;
	for(FlowGraph::edge *p=FlowGraph::adj[x];p;p=p->nxt)
		if( p->cap > p->flow ) clear_tag(p->to);
}
void divide(int l, int r, int L, int R) {
	if( l > r ) return ;
	if( L == R ) {
		for(int i=l;i<=r;i++)
			ans += abs(d[L] - a[b[i]]);
		return ;
	}
	int mid = (L + R) >> 1, s = 0, t = 1, cnt = r - l + 2;
	for(int i=l;i<=r;i++) tmp[i - l + 1] = i - l + 2;
	build_segtree(1, 1, r - l + 1, cnt), FlowGraph::clear(cnt);
	link_segtree(0, 1, 1, r - l + 1);
	
	sort(b + l, b + r + 1);
	for(int i=l;i<=r;i++) {
		if( a[b[i]] <= d[mid] )
			FlowGraph::addedge(s, i - l + 2, 1);
		else FlowGraph::addedge(i - l + 2, t, 1);
	}
	for(int o=0;o<=1;o++) {
		for(int i=l;i<=r;i++) {
			int lft = lower_bound(b + l, b + r + 1, le[o][b[i]]) - b - l + 1;
			int rgt = upper_bound(b + l, b + r + 1, ri[o][b[i]]) - b - l;
			if( lft == rgt ) continue;
			add_edge(1, 1, r - l + 1, lft, rgt, i - l + 2, o);
		}
	}
	FlowGraph::max_flow(s, t), dfs(s);
	
	int p = l;
	for(int i=l;i<=r;i++)
		if( tag[i - l + 2] ) swap(b[i], b[p++]);
	clear_tag(s), divide(l, p - 1, L, mid), divide(p, r, mid + 1, R);
}

int main() {
	scanf("%d%d", &N, &M);
	for(int i=1;i<=N;i++) {
		scanf("%d", &a[i]), d[i] = a[i];
		le[0][i] = le[1][i] = ri[0][i] = ri[1][i] = i;
	}
	sort(d + 1, d + N + 1), dcnt = unique(d + 1, d + N + 1) - d - 1;
	for(int i=1,t,l,r,k;i<=M;i++) {
		scanf("%d%d%d%d", &t, &l, &r, &k);
		le[t][k] = min(le[t][k], l), ri[t][k] = max(ri[t][k], r);
	}
	for(int i=1;i<=N;i++) b[i] = i;
	divide(1, N, 1, dcnt), printf("%d\n", ans);
}

loj - 2570:考虑被定位的区间,可以看成某个结点往上走的所有左儿子/右儿子(如果左儿子/右儿子不是自己的话)。然后树上倍增 + 分 类 大 讨 论。写的常数好像比较大。

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

typedef long long ll;
typedef pair<ll, int> pli;

#define fi first
#define se second
#define pr make_pair

const int MAXN = 400000;

int ch[2][MAXN + 5], le[MAXN + 5], ri[MAXN + 5], id[MAXN + 5], cnt;
int build(int l, int r) {
	int x = (++cnt); le[x] = l, ri[x] = r;
	if( l != r ) {
		int m; scanf("%d", &m);
		ch[0][x] = build(l, m);
		ch[1][x] = build(m + 1, r);
	} else id[l] = x;
	return x;
}

int fa[20][MAXN + 5], dep[MAXN + 5];
pli f[2][20][MAXN + 5], g[2][20][MAXN + 5]; int mst[2][MAXN + 5];
pli merge(const pli &a, const pli &b) {return pr(a.fi + b.fi, a.se + b.se);}
void dfs(int x, int pre, int o) {
	if( !x ) return ;
	if( o == -1 )
		mst[0][x] = mst[1][x] = x;
	else {
		fa[0][x] = pre, dep[x] = dep[pre] + 1, mst[o][x] = mst[o][pre], mst[!o][x] = x;
		f[o][0][x] = pr(dep[pre] + 1, 1), f[!o][0][x] = pr(0, 0);
		g[o][0][x] = pr(dep[pre] + 1 - 2*dep[pre], 1), g[!o][0][x] = pr(0, 0);
		for(int i=1;i<20;i++) {
			for(int p=0;p<=1;p++) {
				f[p][i][x] = merge(f[p][i-1][x], f[p][i-1][fa[i-1][x]]);
				g[p][i][x] = merge(g[p][i-1][x], g[p][i-1][fa[i-1][x]]);
			}
			fa[i][x] = fa[i-1][fa[i-1][x]];
		}
	}
	dfs(ch[0][x], x, 0), dfs(ch[1][x], x, 1);
}
int lca(int u, int v) {
	if( dep[u] < dep[v] ) swap(u, v);
	for(int i=19;i>=0;i--)
		if( dep[fa[i][u]] >= dep[v] )
			u = fa[i][u];
	if( u == v ) return u;
	for(int i=19;i>=0;i--)
		if( fa[i][u] != fa[i][v] )
			u = fa[i][u], v = fa[i][v];
	return fa[0][u];
}
int dist(int u, int v) {return dep[u] + dep[v] - 2*dep[lca(u, v)];}

pli query_f(int x, int d, int o) {
	pli ret = pr(0, 0);
	for(int i=19;i>=0;i--)
		if( dep[fa[i][x]] >= d )
			ret = merge(ret, f[o][i][x]), x = fa[i][x];
	return ret;
}
pli query_g(int x, int d, int o) {
	pli ret = pr(0, 0);
	for(int i=19;i>=0;i--)
		if( dep[fa[i][x]] >= d )
			ret = merge(ret, g[o][i][x]), x = fa[i][x];
	return ret;
}
int anc(int x, int d) {
	for(int i=19;i>=0;i--)
		if( dep[fa[i][x]] >= d ) x = fa[i][x];
	return x;
}

ll work(int x, int y, int u, int o) {
	pli k = query_g(x, dep[y], o);
	return k.fi + 1LL*k.se*dep[u];
}
ll get(int u, int x, int p, int o) {
	int y = lca(u, p);
	if( y == p )
		return dist(p, u) + work(p, x, u, o);
	else if( y < x ) {
		pli k = query_f(p, dep[x], o);
		return dist(p, u) + k.fi + 1LL*k.se*(dep[u] - 2*dep[y]);
	} else {
		pli k = query_f(p, dep[y] + 1, o); int np = anc(p, dep[y] + 1);
		ll ans = k.fi + 1LL*k.se*(dep[u] - 2*dep[y]);
		if( ch[o][y] == np ) ans += dist(u, ch[!o][y]);
		return dist(p, u) + ans + work(y, x, u, o);
	}
}
ll solve(int u, int l, int r) {
	int p = lca(id[l], id[r]);
	if( l == le[p] && ri[p] == r ) return dist(p, u);
	else if( l == le[p] ) return get(u, p, mst[1][id[r]], 1);
	else if( r == ri[p] ) return get(u, p, mst[0][id[l]], 0);
	else return solve(u, l, ri[ch[0][p]]) + solve(u, le[ch[1][p]], r);
}

int n, m;
int main() {
	scanf("%d", &n), build(1, n), dfs(1, 0, -1);
	
	scanf("%d", &m);
	for(int i=1,u,l,r;i<=m;i++) {
		scanf("%d%d%d", &u, &l, &r);
		printf("%lld\n", solve(u, l, r));
	}
}

codeforces - 235C:循环同构串,其实就是复制两遍。注意在 SAM 上打 tag 避免重复计数。
提交记录链接。

codefoces - 547E:广义后缀自动机 + 可持久化线段树合并。(我写的广义后缀自动机)有个问题:有些点的父亲连向了 len 相同的点。首先基数排序没用,还得用 dfs;其次每个串定位的结点可能要跳几个父亲才能到达真实结点。
提交记录链接。

loj - 6625:合数部分只会在最小质因子处统计,质数部分发现是个质数之和,因此直接 min_25 筛即可。
提交记录链接。

loj - 3069:记 \(f'(n) = \frac{f(n)}{4}\)通过打表不难发现 \(f'(n)\) 是积性函数,且 \(\begin{cases}f'(p^c)=1 & (p\mod 4 = 1 ||p=2)\\f'(p^c)=2c+1 &(p\mod 4 = 3)\end{cases}\)。然后直接 min-25 筛,前半部分做 dp 求模 4 等于 1/3 的质数个数。证明详见2019年论文《<整点计数>命题报告以及对高斯整数的若干研究》。
提交记录链接。

codeforces - 1043F:连边 \((i, j)\) 当且仅当 \(j|i\)\(\exist x, \gcd(x,\frac{i}{j}) = 1\)。莫比乌斯反演算一下 \(\gcd(x,\frac{i}{j}) = 1\)\(x\) 个数。然后跑最短路即可。
提交记录链接。

codeforces - 645F:记 \(i\) 的倍数有 \(b_i\) 个,答案为 \(\sum_{i=1}\phi(i)\times\binom{b_i}{k}\)。每次只会更改 \(O(\sqrt{A})\) 个,暴力改即可。
提交记录链接。

codeforces - 1295F:分段拉格朗日插值。具体分段方法可参考 「NOI2019」机器人 。时间复杂度 \(O(n^3)\)
提交记录链接。

loj - 6511:和 cf1307G 的处理方法差不多:列线性规划,对偶成费用流。最后关于 \(\sum_{i=1}b_{i,x}-y_x\leq c_x\) 的处理,首先拆点变成 \(p_x-y_x\leq c_x\),然后建两条边 \((c_x,t_x)\)\((\infin, 0)\)。求答案可以二分,也可以每增广一次更新一次,因为最优解一定在凸包上(在此感谢 vuq 中的 myh 大佬)。
提交记录链接。

loj - 6259:对网格外新建 0 号点,合法方案是一个以 0 号点为根的内向树。已存在的连通块是以 0号点/未确定点 为根的内向树,因此可将已有的连通块缩掉(如果成环则无解)。然后有向图 matrix-tree。
提交记录链接。

posted @ 2020-06-01 15:27  Tiw_Air_OAO  阅读(369)  评论(0编辑  收藏  举报