• @一句话题解


    发现我 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
    ", a[x] + tg[f[x]]);
    		} else printf("%lld %lld
    ", -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
    ", 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
    ", 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
    ", 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 ? '
    ' : ' ');
    	}
    }
    

    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
    ", 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
    1
    ", 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
    %d
    ", ans);
    		}
    	} else printf("1
    1
    ");
    }
    

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

    codeforces - 575A:用线段树处理矩阵的区间积然后 (O(8 imes nlog 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", T.query(nd[cnt]));
    		}
    		else if( op == 2 || op == 4 ) {
    			int p = mp.begin()->second;
    			printf("%d
    ", 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
    ", 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
    ", 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
    ", 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
    ", mid);
    			for(int i=1;i<=mid;i++)
    				printf("%d%c", a[i], i == mid ? '
    ' : ' ');
    			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
    ", 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
    ", 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
    ", 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
    ", KM());
    }
    
  • 相关阅读:
    Swoole从入门到入土(27)——协程[协程容器]
    Swoole从入门到入土(26)——多进程[进程间锁]
    Swoole从入门到入土(25)——多进程[进程间无锁计数器]
    Swoole从入门到入土(24)——多进程[进程管理器ProcessManager]
    Swoole从入门到入土(23)——多进程[进程池ProcessPool]
    Swoole从入门到入土(22)——多进程[Process]
    Swoole从入门到入土(21)——毫秒定时器
    Swoole从入门到入土(20)——WebSocket服务器[协程版本]
    Swoole从入门到入土(19)——WebSocket服务器[文件传输]
    文字超过一定长度后省略号处理总结
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12825198.html
Copyright © 2020-2023  润新知