@一句话题解 - 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());
}