@一句话题解 - 2020.05@

发现我 4 月根本没写多少题解。

这实在是太颓了啊啊啊啊啊啊啊。

codeforces - 674D:给儿子维护优先队列,对儿子影响打 tag;给父亲直接暴力改(因为人只会有一个父亲,尽管可以改父亲)。真实值 = 维护的值 + 父亲的 tag,然后全局再维护优先队列。因为环大小 >= 3 所以不会出问题。

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

typedef long long ll;

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

struct heap{
	priority_queue<ll>q1, q2;
	
	void maintain() {
		while( !q1.empty() && !q2.empty() && q1.top() == q2.top() )
			q1.pop(), q2.pop();
	}
	bool empty() {maintain(); return q1.empty();}
	ll top() {maintain(); return q1.top();}
	void pop(ll k) {q2.push(k); maintain();}
	void push(ll k) {q1.push(k); maintain();}
	
}h1[MAXN + 5], h2[MAXN + 5], h3, h4;

ll a[MAXN + 5], tg[MAXN + 5];

ll t[MAXN + 5]; int d[MAXN + 5], f[MAXN + 5], n, q;
ll func(int x) {return t[x] / (d[x] + 2);}
ll func2(int x) {return t[x] - t[x] / (d[x] + 2) * (d[x] + 1);}
void pop(int x) {
	if( !h1[x].empty() ) h3.pop(h1[x].top() - tg[x]);
	if( !h2[x].empty() ) h4.pop(h2[x].top() + tg[x]);
}
void push(int x) {
	if( !h1[x].empty() ) h3.push(h1[x].top() - tg[x]);
	if( !h2[x].empty() ) h4.push(h2[x].top() + tg[x]);
}
void pop(int x, ll k) {pop(x), h1[x].pop(-k), h2[x].pop(k), push(x);}
void push(int x, ll k) {pop(x), h1[x].push(-k), h2[x].push(k), push(x);}
void add(int x, ll k) {pop(f[x], a[x]), push(f[x], a[x] += k);}
void update(int x, int k) {
	ll p1 = func(x), p2 = func2(x);
	pop(x), d[x] += k, tg[x] = func(x);
	add(f[x], tg[x] - p1), add(x, func2(x) - p2);
	push(x);
}
void debug() {
	puts("debug : ");
	for(int i=1;i<=n;i++)
		printf("%lld ", a[i] + tg[f[i]]);
	puts("");
}
int main() {
	scanf("%d%d", &n, &q);
	for(int i=1;i<=n;i++) scanf("%lld", &t[i]);
	for(int i=1;i<=n;i++) scanf("%d", &f[i]), d[f[i]]++;
	for(int i=1;i<=n;i++) a[f[i]] += func(i), a[i] += func2(i), tg[i] = func(i);
	for(int i=1;i<=n;i++) push(f[i], a[i]);
	for(int i=1;i<=q;i++) {
		int type; scanf("%d", &type);
		if( type == 1 ) {
			int x, y; scanf("%d%d", &x, &y);
			add(f[x], -func(x)), update(f[x], -1), pop(f[x], a[x]);
			push(f[x] = y, a[x]), update(f[x], 1), add(f[x], func(x));
		} else if( type == 2 ) {
			int x; scanf("%d", &x);
			printf("%lld\n", a[x] + tg[f[x]]);
		} else printf("%lld %lld\n", -h3.top(), h4.top());
//		debug();
	}
}

atcoder - AGC032C:首先原图要有欧拉回路。如果存在点 v 度数 >= 6,则会出现形如 v->A->v->B->v->C->v 的欧拉回路,此时一定有解;否则考虑两个度数为 4 的点 p, q,如果有 p->A->q->B->q->C->p->D->p 或 p->A->p->B->q->C->q->D->p,则也存在解;如果找不到这样的点对,则一定无解。跑欧拉回路判区间相离/包含即可。

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

const int MAXN = 100000;

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

int arr[MAXN + 5], cnt;
void dfs(int x) {
	for(;adj[x];) {
		edge *p = adj[x]; adj[x] = adj[x]->nxt;
		if( p->tag ) continue;
		p->rev->tag = true, dfs(p->to);
	}
	arr[++cnt] = x;
}

int nxt[MAXN + 5];

int deg[MAXN + 5];

int main() {
	int N, M; scanf("%d%d", &N, &M);
	for(int i=1;i<=M;i++) {
		int a, b; scanf("%d%d", &a, &b);
		addedge(a, b), deg[a]++, deg[b]++;
	}
	
	for(int i=1;i<=N;i++)
		if( deg[i] & 1 ) {
			puts("No");
			return 0;
		}
	for(int i=1;i<=N;i++)
		if( deg[i] >= 6 ) {
			puts("Yes");
			return 0;
		}
		
	dfs(1);
//	for(int i=1;i<=cnt;i++) printf("%d ", arr[i]);
	int mx = 1, mn = cnt;
	for(int i=cnt;i>=1;i--) {
		if( deg[arr[i]] == 4 ) {
			if( nxt[arr[i]] ) mx = max(mx, i);
			else mn = min(mn, nxt[arr[i]] = i);
		}
	}
	
	if( mn < mx ) puts("Yes");
	else {
		int p = 1;
		for(int i=1;i<=cnt;i++)
			if( nxt[arr[i]] && nxt[arr[i]] != i ) {
				if( nxt[arr[i]] < p ) {
					puts("Yes");
					return 0;
				}
				else p = nxt[arr[i]];
			}
		puts("No");
	}
}

atcoder - AGC028C:一条边 min(Ax, By) 可以拆成两条 Ax, By。每个点的贡献有四种 Ax + Bx, Ax, Bx, 0。合法的情况要么全 Ax;要么全 Bx;要么至少包含一个 Ax + Bx。最后一个可以找前 n 小的 A, B,然后再讨论一下。

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

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

const int MAXN = 100000;

bool tag[MAXN + 5]; pii p[2*MAXN + 5]; int N;
int main() {
	ll SA = 0, SB = 0; scanf("%d", &N);
	for(int i=1;i<=N;i++) {
		int A, B; scanf("%d%d", &A, &B);
		SA += A, SB += B;
		p[2*i-1] = make_pair(A, i);
		p[2*i] = make_pair(B, i);
	}
	sort(p + 1, p + 2*N + 1);
	
	ll ans = 0; bool flag = false;
	for(int i=1;i<=N;i++) {
		if( tag[p[i].second] ) flag = true;
		else tag[p[i].second] = true;
		ans += p[i].first;
	}
	if( !flag ) {
		if( p[N].second == p[N + 1].second )
			ans = min(ans - p[N - 1].first + p[N + 1].first, ans - p[N].first + p[N + 2].first);
		else ans = ans - p[N].first + p[N + 1].first;
	}
	
	printf("%lld\n", min(ans, min(SA, SB)));
}

atcoder - ARC091F:对 sg 函数打表得到,当 A mod K = 0 时 sg(A, K) = A / K;否则 sg(A, K) = sg(A - ⌊A/K⌋ - 1, K)。证明不会,猜测归纳法可证。求的时候如果步长 ⌊A/K⌋ + 1 相同则一次性跳到底,然后就过了时间复杂度我也不会证,感觉可以按√10^9分类。

#include <cstdio>

int sg(int x, const int &K) {
	if( x % K == 0 ) return x / K;
	int p = x / K + 1, q = x % K;
	return sg(x - (q + p - 1) / p * p, K);
}

int main() {
	int N, ans = 0; scanf("%d", &N);
	
	for(int i=1;i<=N;i++) {
		int A, K; scanf("%d%d", &A, &K);
		ans ^= sg(A, K);
	}
	puts(ans ? "Takahashi" : "Aoki");
}
/*
sg[i] = (i % K == 0 ? i / K : sg[i - i/K - 1])
*/

atcoder - ARC095F:如果记 b[a[i]] = i,则有 fa[i] = max(fa[i - 1], b[i - 1])。因此如果有解则应满足非叶结点构成一条链。先特判链为空、链只含 1 个点。如果起点是叶子,则 a[1] = 1 满足字典序最小;因此起点是链某个端点所连叶结点。两个端点都求一遍即可。

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

const int MAXN = 100000;

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

int deg[MAXN + 5], tmp[MAXN + 5];
int ans[MAXN + 5], a[MAXN + 5], cnt;
void dfs(int x, int f) {
	if( !f ) a[++cnt] = 1;
	int p = cnt + 1;
	for(int i=1;i<=deg[x]-2;i++)
		cnt++, a[cnt] = cnt + 1;
	a[++cnt] = p;
	if( f && tmp[x] == 1 ) cnt++, a[cnt] = cnt;
	for(edge *p=adj[x];p;p=p->nxt)
		if( deg[p->to] != 1 && p->to != f ) dfs(p->to, x);
}
void update() {
	for(int i=1;i<=cnt;i++) {
		if( ans[i] < a[i] ) break;
		else if( ans[i] > a[i] ) {
			for(int j=1;j<=cnt;j++)
				ans[j] = a[j];
			break;
		}
	}
	cnt = 0;
}

int main() {
	int n; scanf("%d", &n);
	for(int i=1;i<n;i++) {
		int u, v; scanf("%d%d", &u, &v);
		addedge(u, v), deg[u]++, deg[v]++;
	}
	
	if( n == 2 ) {
		printf("%d %d\n", 1, 2);
		return 0;
	}
	for(int i=1;i<=n;i++) tmp[i] = deg[i];
	for(int i=1;i<=n;i++)
		if( deg[i] == 1 ) tmp[adj[i]->to]--;
	for(int i=1;i<=n;i++)
		if( deg[i] != 1 && tmp[i] > 2 ) {
			puts("-1");
			return 0;
		}
	
	int s = 0, t = 0;
	for(int i=1;i<=n;i++)
		if( deg[i] != 1 && tmp[i] == 1 ) {
			if( !s ) s = i;
			else t = i;
		}
	if( t == 0 ) {
		printf("1");
		for(int i=3;i<n;i++)
			printf(" %d", i);
		printf(" %d %d\n", 2, n);
	}
	else {
		for(int i=1;i<=n;i++) ans[i] = n - i + 1;
		dfs(s, 0), update(), dfs(t, 0), update();
		for(int i=1;i<=n;i++)
			printf("%d%c", ans[i], i == n ? '\n' : ' ');
	}
}

atcoder - AGC026D:对于 h 进行类似笛卡尔树建树(不同的是 h 相同的放一层),树中每个结点对应原图的一个矩形。定义 dp[i][0/1] 表示矩形的底层是/不是红蓝相间,可以将矩形分为三类:有两个横着相邻同色;有两个竖着相邻同色;没有相邻同色。转移时分析性质讨论一下即可。

#include <cstdio>
#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 = 100;
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 h[MAXN + 5], N;
pii get(int l, int r, int lh) {
	int mn = h[l];
	for(int i=l;i<=r;i++)
		mn = min(mn, h[i]);
	
	int lst = l; pii ret = mp(1, 1);
	for(int i=l;i<=r;i++) {
		if( h[i] == mn ) {
			if( i > lst ) {
				pii tmp = get(lst, i - 1, mn);
				ret.fi = mul(ret.fi, tmp.fi);
				ret.se = mul(ret.se, add(mul(2, tmp.fi), tmp.se));
			}
			ret.se = mul(ret.se, 2);
			
			lst = i + 1;
		}
	}
	if( r >= lst ) {
		pii tmp = get(lst, r, mn);
		ret.fi = mul(ret.fi, tmp.fi);
		ret.se = mul(ret.se, add(mul(2, tmp.fi), tmp.se));
	}
	
	return mp(mul(ret.fi, pow_mod(2, mn - lh)), sub(ret.se, mul(2, ret.fi)));
}

int main() {
	scanf("%d", &N);
	for(int i=1;i<=N;i++) scanf("%d", &h[i]);
	
	pii k = get(1, N, 0);
	printf("%d\n", add(k.fi, k.se));
}

atcoder - ARC060F:如果不是循环串输出 1 1;如果是单字母串输出 n 1;否则把第一个字母单独剖出来一定是合法方案。因此正着倒着分别 kmp 判前缀后缀是否为循环串即可。怎么判循环串相信都是基础内容了是的,模数是唬你的

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

const int MAXN = 500000;

int f[MAXN + 5], g[MAXN + 5], lenw; char w[MAXN + 5];
bool checkf(int x) {return f[x] && (x % (x - f[x]) == 0);}
bool checkg(int x) {return g[x] && (x % (x - g[x]) == 0);}
int main() {
	scanf("%s", w), lenw = strlen(w);
	
	f[0] = -1, f[1] = 0;
	for(int i=2;i<=lenw;i++) {
		int j = f[i - 1];
		while( j != -1 && w[j] != w[i - 1] )
			j = f[j];
		f[i] = j + 1;
	}
	
	if( checkf(lenw) ) {
		int k = lenw / (lenw - f[lenw]);
		if( k == lenw ) printf("%d\n1\n", lenw);
		else {
			reverse(w, w + lenw), g[0] = -1, g[1] = 0;
			for(int i=2;i<=lenw;i++) {
				int j = g[i - 1];
				while( j != -1 && w[j] != w[i - 1] )
					j = g[j];
				g[i] = j + 1;
			}
			
			int ans = 0;
			for(int i=1;i<lenw;i++)
				ans += (!checkf(i) && !checkg(lenw - i));
			printf("2\n%d\n", ans);
		}
	} else printf("1\n1\n");
}

atcoder - AGC032D:操作其实就是花费 A 把某元素右移;花费 B 把某元素左移。注意到某个元素最多需要移动一次。对不移动的元素 dp,定义 dp[i] 表示上一个不动的元素是第 i 个元素。注意两个相邻不动元素 x, y 之间不能存在 z 使得 x < z < y。(我好像在之前出过一道A等于B情况的水题)

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

typedef long long ll;

const int MAXN = 5000;
const ll INF = (1LL << 60);

ll dp[MAXN + 5];

int p[MAXN + 5], N, A, B;
int main() {
	scanf("%d%d%d", &N, &A, &B);
	for(int i=1;i<=N;i++) scanf("%d", &p[i]);
	p[N + 1] = N + 1;
	
	for(int i=1;i<=N+1;i++) {
		dp[i] = INF; int nw = 0; ll del = 0;
		for(int j=i-1;j>=1;j--) {
			if( p[j] < p[i] ) {
				if( nw < p[j] )
					dp[i] = min(dp[i], dp[j] + del), nw = p[j];
				del += B;
			}
			else del += A;
		}
		if( !nw ) dp[i] = del;
	}
	
	printf("%lld\n", dp[N + 1]);
}

loj - 2012:先利用 trie 针对后缀关系建树,根为空串。先学后缀一定更优,所以相当于自上而下标号使得父子之间标号差之和最小。结论是按照 dfs 的顺序标号最优(不会证,留坑)。根据这个结论,每次 dfs 的时候走子树大小最小的子树即可。

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

const int MAXN = 100000;
const int MAXM = 520000;

int ch[26][MAXM + 5], id[MAXM + 5], ncnt;
void insert(char *S, int x) {
	int lenS = strlen(S), nw = 0;
	for(int i=lenS-1;i>=0;i--) {
		if( !ch[S[i] - 'a'][nw] )
			ch[S[i] - 'a'][nw] = (++ncnt);
		nw = ch[S[i] - 'a'][nw];
	}
	id[nw] = x;
}

vector<int>G[MAXN + 5], sz[MAXN + 5];
void build(int x, int p) {
	if( id[x] ) {
//		printf("! %d %d\n", p, id[x]);
		G[p].push_back(id[x]), p = id[x];
	}
	for(int i=0;i<26;i++)
		if( ch[i][x] ) build(ch[i][x], p);
}

int siz[MAXN + 5];
void dfs(int x) {
	siz[x] = 1;
	for(int i=0;i<G[x].size();i++) {
		int to = G[x][i];
		dfs(to), sz[x].push_back(siz[to]);
		siz[x] += siz[to];
	}
}

char s[MAXM + 5];
int main() {
	int n; scanf("%d", &n);
	for(int i=1;i<=n;i++)
		scanf("%s", s), insert(s, i);
	build(0, 0);
	
	dfs(0);
	
	long long ans = 0;
	for(int i=0;i<=n;i++) {
		sort(sz[i].begin(), sz[i].end());
		int tmp = 1;
		for(int j=0;j<sz[i].size();j++)
			ans += tmp, tmp += sz[i][j];
	}
	printf("%lld\n", ans);
}

codeforces - 575A:用线段树处理矩阵的区间积然后 \(O(8\times n\log n)\) 乱做。注意矩阵乘法不满足交换律。

#include <map>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
 
typedef long long ll;
typedef pair<int, int> pii;
 
const int MAXN = 50000;
 
int N, M, P; ll K;
inline int add(int x, int y) {x += y; return x >= P ? x - P : x;}
inline int mul(int x, int y) {return 1LL * x * y % P;}
 
struct matrix{
	int m00, m01, m10, m11; matrix() {m00 = m01 = m10 = m11 = 0;}
	matrix(int _a, int _b, int _c, int _d) : m00(_a), m01(_b), m10(_c), m11(_d) {}
	
	friend matrix operator * (const matrix &A, const matrix &B) {
		matrix C;
		C.m00 = add(mul(A.m00, B.m00), mul(A.m01, B.m10));
		C.m01 = add(mul(A.m00, B.m01), mul(A.m01, B.m11));
		C.m10 = add(mul(A.m10, B.m00), mul(A.m11, B.m10));
		C.m11 = add(mul(A.m10, B.m01), mul(A.m11, B.m11));
		return C;
	}
}A[MAXN + 5], S;
 
namespace segtree{
	#define lch (x << 1)
	#define rch (x << 1 | 1)
	
	int le[4*MAXN + 5], ri[4*MAXN + 5]; matrix B[4*MAXN + 5];
	void build(int x, int l, int r) {
		le[x] = l, ri[x] = r;
		if( l == r ) {
			B[x] = A[l];
			return ;
		}
		int m = (l + r) >> 1;
		build(lch, l, m), build(rch, m + 1, r);
		B[x] = B[rch] * B[lch];
	}
	matrix query(int x, int l, int r) {
		if( l > ri[x] || r < le[x] ) return matrix(1, 0, 0, 1);
		if( l <= le[x] && ri[x] <= r ) return B[x];
		return query(rch, l, r) * query(lch, l, r);
	}
}
 
matrix getM(int l, int r) {return segtree::query(1, l, r);}
matrix mpow(matrix B, ll p) {
	matrix R(1, 0, 0, 1);
	for(ll i=p;i;i>>=1,B=B*B)
		if( i & 1 ) R = R*B;
	return R;
}
matrix get(ll l, ll r) {
	if( l > r ) return matrix(1, 0, 0, 1);
	if( (l / N) == (r / N) )
		return getM(l % N, r % N);
	else return getM(0, r % N) * mpow(S, (r / N) - (l / N) - 1) * getM(l % N, N - 1);
}
 
int s[MAXN + 5];
void init() {
	for(int i=0;i<N;i++) A[i] = matrix(s[i + 1 == N ? 0 : i + 1], s[i], 1, 0);
	S = matrix(1, 0, 0, 1); for(int i=0;i<N;i++) S = A[i] * S;
	segtree::build(1, 0, N - 1);
}
 
map<ll, pii>mp;
int main() {
	scanf("%lld%d%d", &K, &P, &N);
	for(int i=0;i<N;i++) scanf("%d", &s[i]);
	init();
	
	scanf("%d", &M);
	for(int i=0;i<M;i++) {
		ll j; int v; scanf("%lld%d", &j, &v);
		if( j - 1 < K ) {
			if( !mp.count(j - 1) ) mp[j - 1] = make_pair(s[j % N], s[(j - 1) % N]);
			mp[j - 1].first = v;
		}
		if( j < K ) {
			if( !mp.count(j) ) mp[j] = make_pair(s[(j + 1) % N], s[j % N]);
			mp[j].second = v;
		}
	}
	
	ll nw = 0; matrix ans = matrix(1, 0, 0, 1);
	for(map<ll, pii>::iterator it=mp.begin();it!=mp.end();it++) {
		ll x = it->first; int p = it->second.first, q = it->second.second;
		ans = matrix(p, q, 1, 0) * get(nw, x - 1) * ans;
		nw = x + 1;
	}
	ans = get(nw, K - 1) * ans, printf("%d\n", ans.m10);
}

codeforces - 568C:建 2-sat。字典序最小,首先与 s 能够匹配上的前缀最长,其次公共前缀之后的字符 > s 对应字符,最后取 2-sat 字典序最小解。时间复杂度 O(n^3)。

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

bitset<405>bts[405];
int f[405], nxt[30], n, m, l, sz;
char ans[205], s[205], vc[30], t1[2], t2[2];
bool check(int p, int k) {
	int x; memset(f, -1, sizeof f);
	for(int i=0;i<p;i++) {
		x = ((i << 1) | (vc[s[i] - 'a'] == 'C'));
		if( f[x] == -1 ) {
			if( !bts[x][x^1] ) {
				f[x] = 1, f[x^1] = 0;
				for(int j=0;j<l;j++)
					if( bts[x][j] ) f[j] = 1, f[j^1] = 0;
			} else return false;
		} else if( f[x] == 0 ) return false;
		ans[i] = s[i];
	}
	
	x = ((p << 1) | (vc[k] == 'C'));
	if( f[x] == -1 ) {
		if( !bts[x][x^1] ) {
			f[x] = 1, f[x^1] = 0;
			for(int j=0;j<l;j++)
				if( bts[x][j] ) f[j] = 1, f[j^1] = 0;
		} else return false;
	} else if( f[x] == 0 ) return false;
	ans[p] = k + 'a';
	
	for(int i=p+1;i<n;i++) {
		x = ((i << 1) | (vc[0] == 'C'));
		if( f[x] == -1 ) {
			if( !bts[x][x^1] ) {
				f[x] = 1, f[x^1] = 0;
				for(int j=0;j<l;j++)
					if( bts[x][j] ) f[j] = 1, f[j^1] = 0;
				ans[i] = 'a';
			} else if( !bts[x^1][x] && nxt[0] != -1 ) {
				f[x] = 0, f[x^1] = 1;
				for(int j=0;j<l;j++)
					if( bts[x^1][j] ) f[j] = 1, f[j^1] = 0;
				ans[i] = nxt[0] + 'a';
			} else return false;
		} else if( f[x] == 1 ) ans[i] = 'a';
		else if( nxt[0] != -1 ) ans[i] = nxt[0] + 'a';
		else return false;
	}
	
	puts(ans); return true;
}
int main() {
	scanf("%s%d%d", vc, &n, &m);
	l = (n << 1), sz = strlen(vc);
	for(int i=0,p1,p2;i<m;i++) {
		scanf("%d%s%d%s", &p1, t1, &p2, t2);
		p1 = ((p1 - 1) << 1 | (t1[0] == 'C'));
		p2 = ((p2 - 1) << 1 | (t2[0] == 'C'));
		bts[p1][p2] = bts[p2^1][p1^1] = true;
	}
	scanf("%s", s);
	
	for(int i=0;i<sz;i++) {
		nxt[i] = -1;
		for(int j=i;j<sz;j++)
			if( vc[j] != vc[i] ) {
				nxt[i] = j;
				break;
			}
	}
	
	for(int k=0;k<l;k++)
		for(int i=0;i<l;i++)
			if( bts[i][k] ) bts[i] |= bts[k];
	
	memset(f, -1, sizeof f);
	for(int i=0;i<=n;i++) {
		if( i == n ) {
			puts(s);
			return 0;
		}
		else {
			bool flag = true;
			
			int x = ((i << 1) | (vc[s[i] - 'a'] == 'C'));
			if( f[x] == -1 ) {
				if( !bts[x][x^1] ) {
					f[x] = 1, f[x^1] = 0;
					for(int j=0;j<l;j++)
						if( bts[x][j] ) f[j] = 1, f[j^1] = 0;
					flag = true;
				} else flag = false;
			} else flag = (f[x] == 1);
			
			if( !flag ) {
				for(;i>=0;i--) {
					bool f[2] = {};
					for(int p=s[i]-'a'+1;p<sz;p++)
						if( !f[vc[p] == 'C'] ) {
							f[vc[p] == 'C'] = true;
							if( check(i, p) ) return 0;
						}
				}
				puts("-1"); return 0;
			}
		}
	}
}

codeforces - 704C:把子句建点,两个子句有相同的变量则连边(有两类边,变量正负相同;变量一正一负)。则得到的图每个点度数 <= 2,即只有三种情况:单点、链、环。分开讨论,链和环可以 dp。写得很丑,细节多。

#include <map>
#include <vector>
#include <cstdio>
#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 = 100000;
const int MOD = 1000000007;

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 deg[MAXN + 5]; vector<pii>G[MAXN + 5];
void addedge(int u, int v, int c) {
	deg[u]++, deg[v]++;
	G[u].push_back(mp(v, c));
	G[v].push_back(mp(u, c));
	
//	printf("! %d %d %d\n", u, v, c);
}

int ans[2];
void update(int a0, int a1) {
	int p0 = ans[0], p1 = ans[1];
	ans[0] = add(mul(a0, p0), mul(a1, p1));
	ans[1] = add(mul(a0, p1), mul(a1, p0));
}

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

int f[2][2][MAXN + 5]; bool vis[MAXN + 5];
void dfs1(int x, int lst) {
	vis[x] = true;
	
	for(int i=0;i<G[x].size();i++) {
		int to = G[x][i].fi;
		if( to == lst ) continue;
		
		if( G[x][i].se == 0 ) {
			for(int o=0;o<=1;o++) {
				f[o][0][to] = add(f[o][0][x], f[o][1][x]);
				f[o^1][1][to] = add(f[o^1][0][x], f[o][1][x]);
			}
		}
		else {
			for(int o=0;o<=1;o++) {
				f[o][0][to] = add(f[o^1][0][x], f[o][1][x]);
				f[o^1][1][to] = add(f[o][0][x], f[o][1][x]);
			}
		}
		dfs1(to, x);
		
		return ;
	}
	
	if( k[x] == 2 ) {
		for(int o=0;o<=1;o++)
			f[o][1][x] = add(f[o^1][0][x], mul(f[o][1][x], 2));
	}
	update(add(f[0][0][x], f[0][1][x]), add(f[1][0][x], f[1][1][x]));
}
int tmp[2], st; bool t;
void dfs2(int x, int lst) {
	vis[x] = t;
	
	pii to = G[x][0];
	if( to.fi == lst ) to = G[x][1];
	
	if( to.fi == st ) {
		if( (to.se ^ t) == 0 ) {
			tmp[0] = add(tmp[0], add(f[0][0][x], f[0][1][x]));
			tmp[1] = add(tmp[1], add(f[1][0][x], f[1][1][x]));
		}
		else {
			tmp[0] = add(tmp[0], add(f[1][0][x], f[0][1][x]));
			tmp[1] = add(tmp[1], add(f[0][0][x], f[1][1][x]));
		}
		
		return ;
	}
		
	for(int o1=0;o1<=1;o1++)
		for(int o2=0;o2<=1;o2++)
			f[o1][o2][to.fi] = 0;
		
	if( to.se == 0 ) {
		for(int o=0;o<=1;o++) {
			f[o][0][to.fi] = add(f[o][0][x], f[o][1][x]);
			f[o^1][1][to.fi] = add(f[o^1][0][x], f[o][1][x]);
		}
	}
	else {
		for(int o=0;o<=1;o++) {
			f[o][0][to.fi] = add(f[o^1][0][x], f[o][1][x]);
			f[o^1][1][to.fi] = add(f[o][0][x], f[o][1][x]);
		}
	}
	dfs2(to.fi, x);

	return ;
}

map<int, int>A;
int main() {
	scanf("%d%d", &n, &m);
	for(int i=1;i<=n;i++) {
		scanf("%d", &k[i]);
		for(int j=0,x;j<k[i];j++) {
			scanf("%d", &x);
			if( A.count(x) ) addedge(A[x], i, 0);
			else if( A.count(-x) ) addedge(A[-x], i, 1);
			else A[x] = i;
		}
	}
	
	ans[0] = 1, ans[1] = 0;
	for(int i=1;i<=m;i++)
		if( !A.count(i) && !A.count(-i) )
			ans[0] = mul(2, ans[0]);
	for(int i=1;i<=n;i++) {
		if( vis[i] ) continue;
		
		if( deg[i] == 0 )
			update(1, k[i] == 1 ? 1 : 3), vis[i] = true;
		else if( deg[i] == 1 ) {
			if( k[i] == 1 ) f[0][0][i] = 1;
			else f[0][0][i] = f[1][1][i] = 1;
			
			dfs1(i, -1);
		}
		else {
			if( G[i][0].fi == i ) {
				if( G[i][0].se == 0 ) update(1, 1);
				else update(0, 2);
				
				vis[i] = true;
			}
		}
	}
	for(int i=1;i<=n;i++) {
		if( !vis[i] ) {
			st = i, tmp[0] = tmp[1] = 0;
			f[0][0][i] = 1, t = 0, dfs2(i, -1), f[0][0][i] = 0;
			f[1][1][i] = 1, t = 1, dfs2(i, -1), f[1][1][i] = 0;
			update(tmp[0], tmp[1]);
		}
	}
	
	printf("%d\n", ans[1]);
}

codeforces - 704B:每个中途点一定是一进一出,从前往后 dp 枚举每个点的进出状态。除去 s,e 已经连通的连通块必然是一进一出,因此状态存连通块个数即可。注意讨论一下 s,e 是否在前面出现;以及注意不要提前接成环。

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

typedef long long ll;

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

int n, s, e; bool fs, fe;
ll f[MAXN + 5], g[MAXN + 5], x[MAXN + 5];
ll a[MAXN + 5], b[MAXN + 5], c[MAXN + 5], d[MAXN + 5];

void read() {
	scanf("%d%d%d", &n, &s, &e);
	for(int i=1;i<=n;i++) scanf("%lld", &x[i]);
	for(int i=1;i<=n;i++) scanf("%lld", &a[i]);
	for(int i=1;i<=n;i++) scanf("%lld", &b[i]);
	for(int i=1;i<=n;i++) scanf("%lld", &c[i]);
	for(int i=1;i<=n;i++) scanf("%lld", &d[i]);
}

int main() {
	read();
	
	int cnt = 0;
	for(int i=1;i<=n;i++) {
		for(int j=0;j<=cnt;j++) g[j] = f[j], f[j] = INF;
		if( i == s ) {
			if( fe ) {
				f[cnt + 1] = INF;
				for(int j=0;j<=cnt;j++) {
					f[j] = min(f[j], g[j] + c[i] + x[i]);
					f[j + 1] = min(f[j + 1], g[j] + d[i] - x[i]);
				}
				cnt++;
			}
			else {
				for(int j=0;j<=cnt;j++) {
					if( j ) f[j - 1] = min(f[j - 1], g[j] + c[i] + x[i]);
					f[j] = min(f[j], g[j] + d[i] - x[i]);
				}
			}
			fs = true;
		}
		else if( i == e ) {
			if( fs ) {
				f[cnt + 1] = INF;
				for(int j=0;j<=cnt;j++) {
					f[j] = min(f[j], g[j] + a[i] + x[i]);
					f[j + 1] = min(f[j + 1], g[j] + b[i] - x[i]);
				}
				cnt++;
			}
			else {
				for(int j=0;j<=cnt;j++) {
					if( j ) f[j - 1] = min(f[j - 1], g[j] + a[i] + x[i]);
					f[j] = min(f[j], g[j] + b[i] - x[i]);
				}
			}
			fe = true;
		}
		else {
			f[cnt + 1] = INF;
			for(int j=1;j<=cnt;j++) {
				f[j - 1] = min(f[j - 1], g[j] + c[i] + a[i] + 2*x[i]);
				f[j] = min(f[j], min(g[j] + c[i] + b[i], g[j] + d[i] + a[i]));
				f[j + 1] = min(f[j + 1], g[j] + b[i] + d[i] - 2*x[i]);
			}
			
			if( fs && (!fe) ) {
				f[0] = min(f[0], g[0] + d[i] + a[i]);
				f[1] = min(f[1], g[0] + b[i] + d[i] - 2*x[i]);
			}
			if( (!fs) && fe ) {
				f[0] = min(f[0], g[0] + c[i] + b[i]);
				f[1] = min(f[1], g[0] + b[i] + d[i] - 2*x[i]);
			}
			if( i == 1 )
				f[1] = min(f[1], g[0] + b[i] + d[i] - 2*x[i]);
			
			cnt++;
		}
	}
	
	
	printf("%lld\n", f[0]);
}
/*
i -> j :
xi - xj + ci + bj (j < i)
xj - xi + di + aj (j > i)
*/

codeforces - 576D:记矩阵 Ak[i][j] 表示从点 i 走 k 步后能否到 j。转移矩阵 B 只会有 O(m) 次变更,每次变更后尝试用倍增找一下最小值。加上虚边 n->n 方便处理。最后 bitset 大法好。

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

int n;
struct matrix{
	bitset<150>b[150]; matrix() {memset(b, 0, sizeof b);}
	
	friend matrix operator * (matrix A, matrix B) {
		matrix C;
		for(int i=0;i<n;i++)
			for(int k=0;k<n;k++)
				if( A.b[i][k] ) C.b[i] |= B.b[k];
		return C;
	}
}A, B, I;

matrix P[60];
bool check(int l, int r) {
	if( l == r ) return false;
	
	int k = r - l, nw = l, i; P[0] = B;
	for(i = 1; (1<<i) <= k; i++) P[i] = P[i - 1] * P[i - 1];
	for(i--; i >= 0; i--) {
		if( nw > r - (1 << i) ) continue;
		
		matrix T = A * P[i];
		if( !T.b[0][n - 1] )
			A = T, nw += (1 << i);
	}
	
	if( nw != r ) {
		printf("%d\n", nw + 1);
		return true;
	} else return false;
}

struct edge{
	int a, b, d; edge() {}
	edge(int _a, int _b, int _d) : a(_a), b(_b), d(_d) {}
	friend bool operator < (const edge &a, const edge &b) {
		return a.d < b.d;
	}
}e[155];

int main() {
	int m; scanf("%d%d", &n, &m);
	for(int i=0;i<m;i++)
		scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].d), e[i].a--, e[i].b--;
	e[m].a = n - 1, e[m].b = n - 1, e[m].d = 0;
	sort(e, e + m + 1);
	
	for(int i=0;i<n;i++) I.b[i][i] = 1; A = I;
	
	int lst = 0;
	for(int i=0;i<=m;i++) {
		if( check(lst, e[i].d) ) return 0;
		lst = e[i].d, B.b[e[i].a][e[i].b] = 1;
	}
	if( check(lst, lst + n) ) return 0;
	
	puts("Impossible");
}

codeforces - 547D:对行列建点,每个点所在行列连边成二分图。然后建虚点连奇点跑欧拉路。行->列染红;列->行染蓝。

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

const int N = 200000;

struct edge{
	int to, id; bool tag;
	edge *nxt, *rev;
}edges[4*N + 5], *adj[2*N + 5], *ecnt = edges;

void addedge(int u, int v, int i) {
	edge *p = (++ecnt), *q = (++ecnt);
	p->to = v, p->id = i, p->tag = false, p->nxt = adj[u], adj[u] = p;
	q->to = u, q->id = i, q->tag = false, q->nxt = adj[v], adj[v] = q;
	p->rev = q, q->rev = p;
}

bool ans[N + 5];
void dfs(int x) {
	for(;adj[x];) {
		edge *p = adj[x]; adj[x] = adj[x]->nxt;
		if( p->tag ) continue;
		p->tag = p->rev->tag = true;
		ans[p->id] = (p->to > N);
		dfs(p->to);
	}
}

int deg[2*N + 5];
int main() {
	int n; scanf("%d", &n);
	for(int i=1;i<=n;i++) {
		int x, y; scanf("%d%d", &x, &y);
		addedge(x, N + y, i), deg[x]++, deg[N + y]++;
	}
	for(int i=1;i<=2*N;i++)
		if( deg[i] & 1 ) addedge(0, i, 0);
	
	for(int i=0;i<=2*N;i++) dfs(i);
	for(int i=1;i<=n;i++)
		putchar(ans[i] ? 'b' : 'r');
}

codeforces - 582D:根据某库默尔定理,组合数 C(N, M) 含 p 的因子数量等于 M + (N - M) 在 p 进制下的进位次数。那么只需要求 x + y <= A 且 x + y 在 p 进制下进位 >= α 次的数量即可。直接 \(O(\log^2 A)\) 数位 dp 即可(虽然这么说但还是写bug写到自闭)

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

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 fun(int l, int r) {
	if( l > r ) return 0;
	else return 1LL*(l + r)*(r - l + 1)/2%MOD;
}

int f[2][3500][3500], g[2][3500][3500], b[3500], p, a, n;
void solve() {
	f[0][0][0] = 1;
	for(int i=1;i<=n;i++) {
		for(int j=0;j<i;j++) {
			f[0][i][j] = add(f[0][i][j], mul(f[0][i - 1][j], fun(1, p)));
			f[0][i][j] = add(f[0][i][j], mul(f[1][i - 1][j], fun(1, p - 1)));
			f[1][i][j + 1] = add(f[1][i][j + 1], mul(f[0][i - 1][j], fun(1, p - 1)));
			f[1][i][j + 1] = add(f[1][i][j + 1], mul(f[1][i - 1][j], fun(1, p)));
		}
	}
	for(int i=0;i<=n;i++)
		for(int j=i-1;j>=0;j--) {
			f[0][i][j] = add(f[0][i][j], f[0][i][j + 1]);
			f[1][i][j] = add(f[1][i][j], f[1][i][j + 1]);
		}
	
	g[0][n + 1][0] = 1; int ans = 0;
	for(int i=n;i>=1;i--) {
		for(int j=0;j<=n-i;j++) {
			if( a - j <= i - 1 ) {
				ans = add(ans, mul(mul(g[0][i + 1][j], f[0][i - 1][max(a - j, 0)]), fun(1, b[i])));
				ans = add(ans, mul(mul(g[0][i + 1][j], f[1][i - 1][max(a - j, 0)]), fun(1, b[i] - 1)));
			}
			if( a - j <= i - 1 ) {
				ans = add(ans, mul(mul(g[1][i + 1][j], f[0][i - 1][max(a - j, 0)]), fun(p - b[i], p - 1)));
				ans = add(ans, mul(mul(g[1][i + 1][j], f[1][i - 1][max(a - j, 0)]), fun(p - b[i] + 1, p)));
			}
			
			g[0][i][j] = add(g[0][i][j], mul(g[0][i + 1][j], b[i] + 1));
			g[0][i][j] = add(g[0][i][j], mul(g[1][i + 1][j], p - b[i] - 1));
			g[1][i][j + 1] = add(g[1][i][j + 1], mul(g[0][i + 1][j], b[i]));
			g[1][i][j + 1] = add(g[1][i][j + 1], mul(g[1][i + 1][j], p - b[i]));
		}
	}
	printf("%d\n", ans);
}

int A[1005], len;
int rem() {
	int r = 0;
	for(int i=len;i>=1;i--) {
		int t = (10LL*r + A[i]) / p;
		r = (10LL*r + A[i]) % p;
		A[i] = t;
	}
	while( len > 1 && A[len] == 0 ) len--;
	return r;
}
char S[1005];
int main() {
	scanf("%d%d%s", &p, &a, S), len = strlen(S);
	for(int i=0;i<len;i++)
		A[len - i] = S[i] - '0';
	
	while( len != 1 || A[1] != 0 )
		b[++n] = rem();
	
	b[1]++;
	for(int i=1;i<=n;i++)
		b[i + 1] += b[i] / p, b[i] %= p;
	if( b[n + 1] ) n++;
	
	solve();
}

codeforces - 521D:先覆盖后加后乘。每个元素最多覆盖一次(且是最大的覆盖),因此可以把覆盖变为加。同一个元素加总是先加大再加小,把每次加操作按从大到小的顺序对乘积的贡献改写为乘操作,发现更大加操作改写出来的乘操作也更大。所有操作都可变为乘操作,直接贪心(比较分数会爆 long long 所以用 long double)。

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

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

const int MAXN = 100000;

struct node{
	ll a, b; int i; node() {}
	node(ll _a, ll _b, int _i) : a(_a), b(_b), i(_i) {}
	friend bool operator < (const node &a, const node &b) {
		return (long double)a.a * b.b < (long double)b.a * a.b;
	}
};

vector<int>ans;
vector<node>v;
vector<pii>c[MAXN + 5];
pii d[MAXN + 5];

int a[MAXN + 5], t[MAXN + 5];
bool cmp(int x, int y) {return t[x] < t[y];}
int main() {
	int k, n, m; scanf("%d%d%d", &k, &n, &m);
	for(int i=1;i<=k;i++) scanf("%d", &a[i]), d[i] = mp(a[i], 0);
	for(int i=1;i<=n;i++) {
		int x, b; scanf("%d%d%d", &t[i], &x, &b);
		if( t[i] == 1 ) {
			if( b > d[x].fi )
				d[x] = mp(b, i);
		} else if( t[i] == 2 ) {
			c[x].push_back(mp(b, i));
		} else v.push_back(node(b, 1, i));
	}
	
	for(int i=1;i<=k;i++) {
		if( d[i].fi != a[i] )
			c[i].push_back(mp(d[i].fi - a[i], d[i].se));
		sort(c[i].begin(), c[i].end());
		
		ll s = a[i];
		for(int j=c[i].size()-1;j>=0;j--) {
			v.push_back(node(s + c[i][j].fi, s, c[i][j].se));
			s += c[i][j].fi;
		}
	}
	
	sort(v.begin(), v.end());
	for(int i=v.size()-1;v.size()-i<=m&&i>=0;i--)
		ans.push_back(v[i].i);
	
	sort(ans.begin(), ans.end(), cmp);
	printf("%d\n", ans.size());
	for(int i=0;i<ans.size();i++)
		printf("%d ", ans[i]);
}

atcoder - AGC044B:每个点的距离上界为 O(N),因此总距离上界 O(N^3)。每次修改的时候从当前点 bfs 找到那些距离严格减小的点更新。这样每次总距离严格减小,而它非负,故时间复杂度为 O(N^3)。(为什么我做不起这种sb题啊啊啊)

#include <queue>
#include <cstdio>
#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 = 500;
const int MAXM = MAXN*MAXN;
const int dx[] = {0, 0, 1, -1};
const int dy[] = {1, -1, 0, 0};

bool a[MAXN + 5][MAXN + 5];
int d[MAXN + 5][MAXN + 5], N;

void update(int x, int y) {
	queue<pii>que; que.push(mp(x, y));
	while( !que.empty() ) {
		pii f = que.front(); que.pop();
		for(int i=0;i<4;i++) {
			pii g = mp(f.fi + dx[i], f.se + dy[i]);
			if( d[f.fi][f.se] + a[f.fi][f.se] < d[g.fi][g.se] )
				d[g.fi][g.se] = d[f.fi][f.se] + a[f.fi][f.se], que.push(g);
		}
	}
}

int P[MAXM + 5], M;
int main() {
	scanf("%d", &N), M = N*N;
	for(int i=1;i<=M;i++)
		scanf("%d", &P[i]);
	for(int i=1;i<=N;i++)
		for(int j=1;j<=N;j++)
			d[i][j] = min(min(i - 1, j - 1), min(N - i, N - j)), a[i][j] = 1;
	
	int ans = 0;
	for(int i=1;i<=M;i++) {
		int x = (P[i] - 1) / N + 1, y = (P[i] - 1) % N + 1;
		ans += d[x][y], a[x][y] = 0, update(x, y);
	}
	printf("%d\n", ans);
}

loj - 2018:操作 2,3,4,5 只更改 O(1) 条边。可以根据插入时间为优先级建笛卡尔树,操作 1 加入的点会选择它前驱和后继中插入时间靠后的那个(如果被旋转到根则把优先级调至当前最低),于是 lct 乱做(当然这种题写lct其实是脑子不够的做法,直接set+线段树就够了)

#include<map>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int MAXN = 100000;
struct link_cut_tree{
	struct node{
		int siz;
		node *ch[2], *fa;
		bool dir() {return fa->ch[1] == this;}
	}pl[MAXN + 5], *ncnt, *NIL;
	void pushup(node *x) {
		x->siz = x->ch[0]->siz + x->ch[1]->siz + 1;
	}
	link_cut_tree() {
		ncnt = NIL = &pl[0];
		NIL->ch[0] = NIL->ch[1] = NIL->fa = NIL;
		NIL->siz = 0;
	}
	node *newnode() {
		node *p = (++ncnt);
		p->ch[0] = p->ch[1] = p->fa = NIL;
		p->siz = 1;
		return p;
	}
	bool is_root(node *x) {
		return x->fa->ch[0] != x && x->fa->ch[1] != x;
	}
	void set_child(node *x, node *y, int d) {
		if( x != NIL ) x->ch[d] = y;
		if( y != NIL ) y->fa = x;
	}
	void rotate(node *x) {
		node *y = x->fa; int d = x->dir();
		if( is_root(y) ) x->fa = y->fa;
		else set_child(y->fa, x, y->dir());
		set_child(y, x->ch[!d], d);
		set_child(x, y, !d);
		pushup(y);
	}
	void splay(node *x) {
		while( !is_root(x) ) {
			node *y = x->fa;
			if( is_root(y) )
				rotate(x);
			else {
				if( y->dir() == x->dir() )
					rotate(y);
				else rotate(x);
				rotate(x);
			}
		}
		pushup(x);
	}
	void access(node *x) {
		node *y = NIL;
		while( x != NIL ) {
			splay(x);
			x->ch[1] = y;
			pushup(x);
			y = x, x = x->fa;
		}
	}
	int query(node *x) {
		access(x), splay(x);
		return x->siz;
	}
}T;
link_cut_tree::node *nd[MAXN + 5];
int fa[MAXN + 5], ch[2][MAXN + 5];
void link(int a, int b, int type) {
	if( a ) ch[type][a] = b;
	if( b ) fa[b] = a, T.splay(nd[b]), nd[b]->fa = nd[a];
}
int pri[MAXN + 5], cnt;
map<int, int>mp;
map<int, int>::iterator it1, it2;
int root, m;
int main() {
	scanf("%d", &m), nd[0] = T.NIL;
	for(int i=1;i<=m;i++) {
//		T.debug();
		int op; scanf("%d", &op);
		if( op == 1 ) {
			int c; scanf("%d", &c);
			mp[c] = (++cnt);
			nd[cnt] = T.newnode(), pri[cnt] = i;
			it1 = mp.find(c), it2 = it1, it2++;
			if( it1 == mp.begin() ) {
				if( it2 == mp.end() ) root = i;
				else link(it2->second, cnt, 0);
			}
			else {
				it1--;
				if( it2 == mp.end() )
					link(it1->second, cnt, 1);
				else {
					if( pri[it1->second] > pri[it2->second] )
						link(it1->second, cnt, 1);
					else link(it2->second, cnt, 0);
				}
			}
			printf("%d\n", T.query(nd[cnt]));
		}
		else if( op == 2 || op == 4 ) {
			int p = mp.begin()->second;
			printf("%d\n", T.query(nd[p]));
			if( op == 2 && p == root ) continue;
			link(fa[p], ch[1][p], 0);
			if( fa[p] ) T.access(nd[fa[p]]);
			if( op == 2 ) {
				nd[p]->fa = T.NIL, fa[p] = 0;
				link(p, root, 1);
				root = p, pri[p] = -i;
			}
			else {
				if( root == p ) root = ch[1][p];
				mp.erase(mp.begin()->first);
				*nd[p] = *T.NIL;
			}
		}
		else {
			int p = mp.rbegin()->second;
			printf("%d\n", T.query(nd[p]));
			if( op == 3 && p == root ) continue;
			link(fa[p], ch[0][p], 1);
			if( fa[p] ) T.access(nd[fa[p]]);
			if( op == 3 ) {
				nd[p]->fa = T.NIL, fa[p] = 0;
				link(p, root, 0);
				root = p, pri[p] = -i;
			}
			else {
				if( root == p ) root = ch[0][p];
				mp.erase(mp.rbegin()->first);
				*nd[p] = *T.NIL;
			}
		}
	}
}

loj - 2019:离线。考虑 i < j 且 ki < kj 的情况(另一种同理),则 kj 是 [i, j] 中的最大值。找到 j 左边第一个 kp > kj,则 (p, j) 中的 i 都可以贡献 p1/p2。注意到满足区间两端分别为最大值/次大值的区间只有 O(n) 个,所以我们可以让 (p, j) 强制贡献 p2,最后给 O(n) 个区间贡献 p1-p2(算错值域没开longlong白给50分)

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

typedef long long ll;

const int MAXN = 200000;

namespace segtree{
	#define lch (x << 1)
	#define rch (x << 1 | 1)
	
	int le[4*MAXN + 5], ri[4*MAXN + 5];
	ll tg[4*MAXN + 5], s[4*MAXN + 5];
	void maintain(int x, ll k) {tg[x] += k, s[x] += k*(ri[x] - le[x] + 1);}
	void pushdown(int x) {
		if( tg[x] ) {
			maintain(lch, tg[x]), maintain(rch, tg[x]);
			tg[x] = 0;
		}
	}
	void pushup(int x) {s[x] = s[lch] + s[rch];}
	void build(int x, int l, int r) {
		tg[x] = s[x] = 0, le[x] = l, ri[x] = r;
		if( l == r ) return ;
		int m = (l + r) >> 1;
		build(lch, l, m), build(rch, m + 1, r);
	}
	void update(int x, int l, int r, ll k) {
		if( l > ri[x] || r < le[x] ) return ;
		if( l <= le[x] && ri[x] <= r ) {
			maintain(x, k);
			return ;
		}
		pushdown(x);
		update(lch, l, r, k), update(rch, l, r, k);
		pushup(x);
	}
	ll sum(int x, int l, int r) {
		if( l > ri[x] || r < le[x] ) return 0;
		if( l <= le[x] && ri[x] <= r ) return s[x];
		pushdown(x); return sum(lch, l, r) + sum(rch, l, r);
	}
}

struct query{
	int l, r, i; query() {}
	query(int _l, int _r, int _i) : l(_l), r(_r), i(_i) {}
};
vector<query>ql[MAXN + 5], qr[MAXN + 5];
int k[MAXN + 5], le[MAXN + 5], ri[MAXN + 5];
ll ans[MAXN + 5];
int main() {
	int n, m, p1, p2; scanf("%d%d%d%d", &n, &m, &p1, &p2);
	for(int i=1;i<=n;i++) scanf("%d", &k[i]);
	for(int i=1;i<=m;i++) {
		int a, b; scanf("%d%d", &a, &b);
		query q = query(a, b, i);
		ql[a].push_back(q), qr[b].push_back(q);
	}
	
	k[0] = n + 1;
	for(int i=1;i<=n;i++) {
		le[i] = i - 1;
		while( k[le[i]] < k[i] )
			le[i] = le[le[i]];
	}
	segtree::build(1, 1, n);
	for(int i=1;i<=n;i++) {
		if( le[i] + 1 < i ) segtree::update(1, le[i] + 1, i - 1, p2);
		if( le[i] != 0 ) segtree::update(1, le[i], le[i], p1 - p2);
		for(int j=0;j<qr[i].size();j++) {
			query q = qr[i][j];
			ans[q.i] += segtree::sum(1, q.l, q.r);
		}
	}
	
	k[n + 1] = n + 1;
	for(int i=n;i>=1;i--) {
		ri[i] = i + 1;
		while( k[ri[i]] < k[i] )
			ri[i] = ri[ri[i]];
	}
	segtree::build(1, 1, n);
	for(int i=n;i>=1;i--) {
		if( ri[i] - 1 > i ) segtree::update(1, i + 1, ri[i] - 1, p2);
		if( ri[i] != n + 1 ) segtree::update(1, ri[i], ri[i], p1 - p2);
		for(int j=0;j<ql[i].size();j++) {
			query q = ql[i][j];
			ans[q.i] += segtree::sum(1, q.l, q.r);
		}
	}
	
	for(int i=1;i<=m;i++) printf("%lld\n", ans[i]);
}

loj - 2020:把代价 \(\sum(x_i - y_i + c)^2\) 拆开,形成一个关于 c 的二次函数 - \(\sum x_iy_i\)。前一个对称轴乱算,后一个可以化卷积 ntt 做。

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

const int MAXN = 200000;
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 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 w[20], iw[20], iv[1<<20];
void init() {
	for(int i=0;i<20;i++) {
		w[i] = pow_mod(3, (MOD - 1) / (1 << i));
		iw[i] = pow_mod(w[i], MOD - 2);
		iv[1<<i] = pow_mod(1 << i, MOD - 2);
	}
}
void ntt(int *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) {
		int u = (type == 1 ? w[i] : iw[i]);
		for(int j=0;j<n;j+=s)
			for(int k=0,p=1;k<t;k++,p=mul(p,u)) {
				int x = A[j + k], y = mul(A[j + k + t], p);
				A[j + k] = add(x, y), A[j + k + t] = sub(x, y);
			}
	}
	if( type == -1 ) {
		for(int i=0;i<n;i++)
			A[i] = mul(A[i], iv[n]);
	}
}
int length(int n) {
	int len; for(len = 1; len < n; len <<= 1);
	return len;
}

int A, B, n, m;
int func(int x) {return n*x*x - 2*B*x;}

int x[MAXN + 5], y[MAXN + 5];
int main() {
	init(); scanf("%d%d", &n, &m);
	for(int i=0;i<n;i++) scanf("%d", &x[i]), A += x[i]*x[i], B += x[i];
	for(int i=0;i<n;i++) scanf("%d", &y[i]), A += y[i]*y[i], B -= y[i];
	reverse(y, y + n);
	
	int len = length(2*n - 1);
	ntt(x, len, 1), ntt(y, len, 1);
	for(int i=0;i<len;i++) x[i] = mul(x[i], y[i]);
	ntt(x, len, -1);
	
	int mx = x[n - 1];
	for(int i=0;i<n-1;i++) mx = max(mx, x[i] + x[n + i]);
	printf("%d\n", A - 2*mx + min(func(floor(1.0 * B / n)), func(ceil(1.0 * B / n))));
}

loj - 6722:关键在于从前往后构造而非从后往前构造。怎么构造手玩一下就出来了。打一下表发现值域连续且单调,所以二分。本来还有个 \(\frac{N^2}{N + K} \sim \pi\) 的优化来着,不过 loj 上好像不需要也能够 AC。所以当时比赛我是脑子里哪个部位缺根筋没想到啊。

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

typedef long long ll;

const int N = int(2E6);

int a[N + 5];
int check(int n, ll m) {
	for(int i=1;i<n;i++) {
		if( m % (i + 1) == 0 )
			a[i] = 0, m = m / (i + 1) * i;
		else {
			int r = m % (i + 1) - 1;
			a[i] = i - r, m = m / (i + 1) * i + r;
		}
		
		if( m == 0 ) return 1;
	}
	
	a[n] = n;
	if( m == 1 ) return 0;
	else return -1;
}

int main() {
	ll k; scanf("%lld", &k);
	
	int l = 1, r = N;
	while( l <= r ) {
		int mid = (l + r) >> 1, p = check(mid, mid + k);
		if( p == 0 ) {
			printf("%d\n", mid);
			for(int i=1;i<=mid;i++)
				printf("%d%c", a[i], i == mid ? '\n' : ' ');
			return 0;
		}
		else if( p == 1 ) r = mid - 1;
		else l = mid + 1;
	}
}

bzoj - 2661:最大权匹配,打个表发现是二分图。上最小费用最大流。

#include <queue>
#include <cmath>
#include <cstdio>
#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 = 1000;

namespace FlowGraph{
	const int MAXV = 1000, MAXE = 5000, 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 d[MAXV + 5], h[MAXV + 5], s, t;
	bool relabel() {
		for(int i=0;i<=t;i++)
			h[i] += d[i], d[i] = INF, cur[i] = adj[i];
		
		priority_queue<pii, vector<pii>, greater<pii> >que;
		que.push(mp(d[s] = 0, s));
		while( !que.empty() ) {
			int x = que.top().se, k = que.top().fi; que.pop();
			if( k != d[x] ) continue;
			for(edge *p=adj[x];p;p=p->nxt) {
				int c = p->cost + h[x] - h[p->to];
				if( d[p->to] > k + c && p->cap > p->flow )
					que.push(mp(d[p->to] = k + c, p->to));
			}
		}
		
		return d[t] != INF;
	}
	bool vis[MAXV + 5];
	int aug(int x, int tot) {
		if( x == t ) return tot;
		int sum = 0; vis[x] = true;
		for(edge *&p=cur[x];p;p=p->nxt) {
			int c = h[x] - h[p->to] + p->cost;
			if( d[x] + c == d[p->to] && p->cap > p->flow && !vis[p->to] ) {
				int del = aug(p->to, min(p->cap - p->flow, tot - sum));
				sum += del, p->flow += del, p->rev->flow -= del;
				if( sum == tot ) break;
			}
		}
		vis[x] = false; return sum;
	}
	
	pii min_cost_max_flow(int _s, int _t) {
		int flow = 0, cost = 0; s = _s, t = _t;
		while( relabel() ) {
			int del = aug(s, INF);
			flow += del, cost += del*(d[t] + h[t]);
		}
		return mp(flow, cost);
	}
}

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

int clr[MAXN + 5];
void dfs(int x, int c) {
	clr[x] = c;
	for(edge *p=adj[x];p;p=p->nxt)
		if( clr[p->to] == -1 ) dfs(p->to, !c);
}

bool is_square(int x) {
	int y = int(sqrt(x));
	return y * y == x;
}
int gcd(int x, int y) {
	return y == 0 ? x : gcd(y, x % y);
}
void try_add(int i, int j) {
	if( is_square(i*i - j*j) && gcd(j, i*i - j*j) == 1 )
		addedge(i, j);
}
int main() {
	int a, b; scanf("%d%d", &a, &b);
	for(int i=a;i<=b;i++)
		for(int j=a;j<i;j++)
			try_add(i, j);
	for(int i=a;i<=b;i++) clr[i] = -1;
	for(int i=a;i<=b;i++)
		if( clr[i] == -1 ) dfs(i, 0);

	int s = b - a + 1, t = b - a + 2;
	for(int i=a;i<=b;i++)
		if( clr[i] ) {
			for(edge *p=adj[i];p;p=p->nxt)
				FlowGraph::addedge(i - a, p->to - a, 1, 0);
			FlowGraph::addedge(s, i - a, 1, -i);
		}
		else FlowGraph::addedge(i - a, t, 1, -i);
	
	pii ans = FlowGraph::min_cost_max_flow(s, t);
	printf("%d %d\n", ans.fi, -ans.se);
}

bzoj - 1937:树边减,非树边加。把改变量当作变量,根据破圈法可列出线性规划,发现是个最小顶标和,直接上 KM(为此我还研究了一晚上的KM算法的O(N^3)怎么写)

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

const int MAXN = 50, MAXM = 800, INF = (1 << 30);

int A[MAXN + 5][MAXM + 5], nx, ny;
int wx[MAXN + 5], wy[MAXM + 5], matx[MAXN + 5], maty[MAXM + 5];
bool vx[MAXN + 5], vy[MAXM + 5]; int pre[MAXM + 5], slk[MAXM + 5];
int KM() {
	for(int i=1;i<=nx;i++) {
		wx[i] = 0;
		for(int j=1;j<=ny;j++)
			wx[i] = max(wx[i], A[i][j]);
	}
	for(int i=1;i<=ny;i++) wy[i] = 0;
	
	for(int i=1;i<=nx;i++) {
		for(int j=1;j<=nx;j++) vx[j] = false;
		for(int j=1;j<=ny;j++) vy[j] = false, pre[j] = 0, slk[j] = INF;
		
		int p = i; vx[p] = true;
		while( true ) {
			int del = INF, q;
			for(int j=1;j<=ny;j++) {
				if( vy[j] ) continue;
				int t = wx[p] + wy[j] - A[p][j];
				if( t < slk[j] ) slk[j] = t, pre[j] = p;
				if( slk[j] < del ) del = slk[j], q = j;
			}
			
			for(int j=1;j<=nx;j++)
				if( vx[j] ) wx[j] -= del;
			for(int j=1;j<=ny;j++) {
				if( vy[j] ) wy[j] += del;
				else slk[j] -= del;
			}
			
			if( !maty[q] ) {
				while( q ) {
					p = pre[q];
					int tmp = matx[p];
					matx[p] = q, maty[q] = p;
					q = tmp;
				}
				break;
			} else p = maty[q], vx[p] = vy[q] = true;
		}
	}
	
	int ans = 0;
	for(int i=1;i<=nx;i++) ans += wx[i];
	for(int i=1;i<=ny;i++) ans += wy[i];
	return ans;
}


int G[MAXN + 5][MAXN + 5], H[MAXN + 5][MAXN + 5], N, M;
bool dfs(int s, int f, int t, int x, int w) {
	if( s == t ) return true;
	for(int i=1;i<=N;i++)
		if( H[s][i] && i != f && dfs(i, s, t, x, w) ) {
//			printf("! %d %d %d\n", H[s][i], x, G[s][i] - w);
			A[H[s][i]][x] = max(A[H[s][i]][x], G[s][i] - w);
			return true;
		}
	return false;
}

int main() {
	scanf("%d%d", &N, &M);
	for(int i=1,u,v,w;i<=M;i++)
		scanf("%d%d%d", &u, &v, &w), G[u][v] = G[v][u] = w;
	for(int i=1,a,b;i<N;i++)
		scanf("%d%d", &a, &b), H[a][b] = H[b][a] = i;
	
	int cnt = 0;
	for(int i=1;i<=N;i++)
		for(int j=i+1;j<=N;j++)
			if( G[i][j] && !H[i][j] )
				dfs(i, -1, j, ++cnt, G[i][j]);
	
	nx = N - 1, ny = max(nx, M - N + 1); //X部大小 <= Y部大小
	printf("%d\n", KM());
}
posted @ 2020-05-04 08:02  Tiw_Air_OAO  阅读(349)  评论(0编辑  收藏  举报