• @一句话题解


    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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", ans);
    }
    

    codefoces - 1314C:把所有子串插入到 trie 中排序。二分答案子串 T,相当于求是否存在 ≥k 种划分,使得每个划分出来的串都 > T,可以 (O(n^2m)) dp。注意到如果 P > T,则以 P 为前缀的所有串都 > T,前缀和优化一下就成 (O(nm)) 了。总时间复杂度 (O(nmlog 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
    ", 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^{{tchoose 2}+{ichoose2}-{i+tchoose 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
    ", 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
    ", i, j, ans1[i][j], ans2[i][j]);
    		}
    	printf("%lld
    ", 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
    ", 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
    ", ans);
    	}
    }
    

    loj - 6517:一看就是莫队但是不知道怎么莫队。先建 trie。考虑把字符串拼在一起形成长度为 (O(sum |S|)) 的序列,之后在新序列上用回滚莫队(从大往小滚) + 链表就可以 (O(nsqrt{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
    ", 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
    ", 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
    ", 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
    ", 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)) 是积性函数,且 (egin{cases}f'(p^c)=1 & (pmod 4 = 1 ||p=2)\f'(p^c)=2c+1 &(pmod 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) imesinom{b_i}{k})。每次只会更改 (O(sqrt{A})) 个,暴力改即可。
    提交记录链接。

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

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

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

  • 相关阅读:
    1 Groovy
    HDU
    伸展树整理
    HYSBZ
    markdown语法整理
    HDU
    【JZOJ3085】图的计数【数论】
    【JZOJ3085】图的计数【数论】
    【JZOJ3084】超级变变变【模拟】【规律】
    【JZOJ3084】超级变变变【模拟】【规律】
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/13025439.html
Copyright © 2020-2023  润新知