SDOI2016 R1 解题报告 bzoj4513~bzoj4518

储能表

  将n, m分解为二进制,考虑一个log(n)层的trie树,n会在这颗trie树上走出了一个路径,因为 行数 $ le n$,所以在n的二进制路径上,每次往1走的时候,与m计算贡献,m同样处理,$O(Tlog(n)log(m))$

  当然可以数位dp, $f_{i, n, m, k}$分别代表考虑到第i位,与n, m, k的大小关系,不是很好写,就写了上面的方法。

 1 #include <bits/stdc++.h>
 2 #define rep(i, a, b) for (int i = a; i <= b; i++)
 3 #define drep(i, a, b) for (int i = a; i >= b; i--)
 4 #define REP(i, a, b) for (int i = a; i < b; i++)
 5 #define pb push_back
 6 #define mp make_pair
 7 #define xx first
 8 #define yy second
 9 using namespace std;
10 typedef long long ll;
11 typedef pair<int, int> pii;
12 typedef unsigned long long ull;
13 const int inf = 0x3f3f3f3f;
14 const ll INF = 0x3f3f3f3f3f3f3f3fll;
15 template <typename T> void Max(T& a, T b) { if (b > a) a = b; }
16 //*********************************
17 
18 ll n, m, k, mod;
19 
20 ll POW(ll base, ll num) {
21     ll ret = 1;
22     while (num) {
23         if (num & 1) ret = ret * base % mod;
24         base = base * base % mod;
25         num >>= 1;
26     }
27     return ret;
28 }
29 
30 ll calc(ll st, ll num) {
31     if (st < 0) { num += st; st = 0; }
32     if (num <= 0) return 0;
33     ll t1 = 2 * st + num - 1;
34     if (t1 & 1) num >>= 1;
35     else t1 >>= 1;
36     return t1 % mod * (num % mod) % mod;
37 }
38 ll solve(ll x, ll y, ll i, ll j) {
39     ll ret(0);
40     if (j > i) swap(i, j), swap(x, y);
41     ll z = x ^ (y ^ (y & ((1ll << i) - 1)));
42     ret = calc(z - k, 1ll << i);
43     return (ret * ((1ll << j) % mod)) % mod;
44 }
45 int main() {
46     /*
47     freopen("table.in", "r", stdin);
48     freopen("table.out", "w", stdout);
49     */
50     int T; scanf("%d", &T);
51     while (T--) {
52         scanf("%lld%lld%lld%lld", &n, &m, &k, &mod);
53         
54         ll x(0);
55         ll ans(0);
56         drep(i, 62, 0) if (n >> i & 1) {
57             ll y(0);
58             drep(j, 62, 0) if (m >> j & 1) {
59                 (ans += solve(x, y, i, j)) %= mod;
60                 y |= 1ll << j;
61             }
62             x |= 1ll << i;
63         }
64         
65         printf("%lld
", ans);
66     }
67 }
table

数字配对

  网络流,首先这个图不是奇环,这个图如果有环,那么结构应该是对称的?【我也不是非常清楚,求教。。。】

  把费用和容量连成边,进行二分图染色,一直增广到费用加上当前费用小于0为止,别忘了考虑最后一次的流量。

#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drep(i, a, b) for (int i = a; i >= b; i--)
#define REP(i, a, b) for (int i = a; i < b; i++)
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fll;
template <typename T> void Max(T& a, T b) { if (b > a) a = b; }
//*********************************

const int maxn = 205;

struct Ed {
    int u, v, c; ll w; int nx; Ed() {}
    Ed(int _u, int _v, int _c, ll _w, int _nx) :
    u(_u), v(_v), c(_c), w(_w), nx(_nx) {}
} E[maxn * maxn << 1];
int G[maxn], edtot = 1;
void addedge(int u, int v, int c, ll w) {
    E[++edtot] = Ed(u, v, c, w, G[u]);
    G[u] = edtot;
    E[++edtot] = Ed(v, u, 0, -w, G[v]);
    G[v] = edtot;
}

bool judge(int num) {
    int sqr = sqrt(num);
    if (num == 1) return 0;
    rep(i, 2, sqr) if (num % i == 0) return 0;
    return 1;
}
int s, t;
int knd[maxn];
int n;
int a[maxn], b[maxn], c[maxn];
void dfs(int x, int col) {
    knd[x] = col;
    for (int i = 1; i <= n; i++) if (knd[i] == -1 && (a[i] % a[x] == 0 && judge(a[i] / a[x]) || a[x] % a[i] == 0 && judge(a[x] / a[i]))) dfs(i, col ^ 1);
}

ll dis[maxn]; int pre[maxn];
bool spfa() {
    rep(i, 1, n + 2) dis[i] = -INF;
    static int que[maxn]; int qh(0), qt(0);
    static int vis[maxn]; memset(vis, 0, sizeof(vis));
    vis[que[++qt] = s] = 1; dis[s] = 0;
    while (qh != qt) {
        int x = que[++qh]; if (qh == maxn - 1) qh = 0;
        for (int i = G[x]; i; i = E[i].nx) if (E[i].c && dis[E[i].v] < dis[x] + E[i].w) {
            dis[E[i].v] = dis[x] + E[i].w; pre[E[i].v] = i;
            if (!vis[E[i].v]) {
                vis[que[++qt] = E[i].v] = 1;
                if (qt == maxn - 1) qt = 0;
            }
        }
        vis[x] = 0;
    }
    return dis[t] != -INF;
}
int ans; ll cost;
bool mcf() {
    int flow = inf; ll nm(0);
    for (int i = pre[t]; i; i = pre[E[i].u]) { flow = min(flow, E[i].c); nm += E[i].w; }
    if (nm * flow + cost < 0) {
        ans += cost / -nm;
        return 0;
    }
    ans += flow, cost += nm * flow;
    for (int i = pre[t]; i; i = pre[E[i].u]) E[i].c -= flow, E[i ^ 1].c += flow;
    return 1;
}

int main() {
    /*
    freopen("pair.in", "r", stdin);
    freopen("pair.out", "w", stdout);
    */
    scanf("%d", &n);
    rep(i, 1, n) scanf("%d", a + i);
    rep(i, 1, n) scanf("%d", b + i);
    rep(i, 1, n) scanf("%d", c + i);
    
    memset(knd, -1, sizeof(knd));
    rep(i, 1, n) if (knd[i] == -1) dfs(i, 0);
    
    rep(i, 1, n) rep(j, 1, n) if (knd[i] == 1 && knd[j] == 0) {
        if (a[i] % a[j] == 0 && judge(a[i] / a[j]) || a[j] % a[i] == 0 && judge(a[j] / a[i])) addedge(i, j, min(b[i], b[j]), 1ll * c[i] * c[j]);
    }
    s = n + 1, t = n + 2;
    rep(i, 1, n) if (knd[i]) addedge(s, i, b[i], 0); else addedge(i, t, b[i], 0);
    
    while (spfa()) if (!mcf()) break;
    
    cout << ans << endl;
}
pair

游戏

  树链剖分,考虑树链剖分时,每一次的修改均是连续的一段,我们对每一段维护标记$ax + b$x为当前点到这一段起点的距离。

  那么假如有新的标记$Ax + B$,我们讨论$Ax + B <= ax + b$的情况,如果影响范围在mid的一边,就把新的标记向一边推,如果新的标记覆盖了多余一半,我们将新标记放在当前节点上,将原来的标记向不足mid的部分下推。

  可以说是把标记永久化了。

  复杂度为$O(mlog^{3}n)$,树链剖分两个log,标记一次最多下推log层。

  1 #include <bits/stdc++.h>
  2 #define rep(i, a, b) for (int i = a; i <= b; i++)
  3 #define drep(i, a, b) for (int i = a; i >= b; i--)
  4 #define REP(i, a, b) for (int i = a; i < b; i++)
  5 #define pb push_back
  6 #define mp make_pair
  7 #define xx first
  8 #define yy second
  9 using namespace std;
 10 typedef long long ll;
 11 typedef pair<int, int> pii;
 12 typedef unsigned long long ull;
 13 const int inf = 0x3f3f3f3f;
 14 const ll INF = 0x3f3f3f3f3f3f3f3fll;
 15 template <typename T> void Max(T& a, T b) { if (b > a) a = b; }
 16 //*********************************
 17 
 18 const int maxn = 100005;
 19 
 20 struct Ed {
 21     int u, v, w, nx; Ed() {}
 22     Ed(int _u, int _v, int _w, int _nx) :
 23         u(_u), v(_v), w(_w), nx(_nx) {}
 24 } E[maxn << 1];
 25 int G[maxn], edtot;
 26 
 27 void addedge(int u, int v, int w) {
 28     E[++edtot] = Ed(u, v, w, G[u]);
 29     G[u] = edtot;
 30     E[++edtot] = Ed(v, u, w, G[v]);
 31     G[v] = edtot;
 32 }
 33 
 34 int pre[maxn], dep[maxn], son[maxn], sz[maxn];
 35 ll dis[maxn];
 36 int f[maxn][18];
 37 int dfs(int x, int fa) {
 38     pre[x] = fa, dep[x] = dep[fa] + 1; f[x][0] = fa;
 39     for (int i = 1; i <= 17; i++) f[x][i] = f[f[x][i - 1]][i - 1];
 40     son[x] = 0, sz[x] = 1;
 41     for (int i = G[x]; i; i = E[i].nx) if (E[i].v != fa) {
 42         dis[E[i].v] = dis[x] + E[i].w;
 43         sz[x] += dfs(E[i].v, x);
 44         if (sz[E[i].v] > sz[son[x]]) son[x] = E[i].v;
 45     }
 46     return sz[x];
 47 }
 48 int pos[maxn], inv[maxn], top[maxn], ndtot;
 49 void repos(int x, int tp) {
 50     top[x] = tp; pos[x] = ++ndtot; inv[ndtot] = x;
 51     if (son[x]) repos(son[x], tp);
 52     for (int i = G[x]; i; i = E[i].nx) if (E[i].v != son[x] && E[i].v != pre[x]) repos(E[i].v, E[i].v);
 53 }
 54 
 55 int LCA(int l, int r) {
 56     if (dep[l] < dep[r]) swap(l, r);
 57     int t = dep[l] - dep[r];
 58     drep(i, 17, 0) if (t >> i & 1) l = f[l][i];
 59     if (l == r) return l;
 60     drep(i, 17, 0) if (f[l][i] != f[r][i]) l = f[l][i], r = f[r][i];
 61     return f[l][0];
 62 }
 63 
 64 ll Min[maxn << 2];
 65 ll A[maxn << 2], B[maxn << 2], Flag[maxn << 2];
 66 void pushup(int o, int l, int r) {
 67     if (Flag[o]) Min[o] = min(B[o], A[o] * (dis[inv[r]] - dis[inv[l]]) + B[o]);
 68     if (l != r) Min[o] = min(Min[o], min(Min[o << 1], Min[o << 1 | 1]));
 69 }
 70 void giveFlag(int o, int l, int r, ll a, ll b) {
 71     if (!Flag[o]) { A[o] = a, B[o] = b; Flag[o] = 1; }
 72     else {
 73         int mid = l + r >> 1; ll len = dis[inv[mid + 1]] - dis[inv[l]];
 74         if (A[o] == a || l == r) B[o] = min(B[o], b);
 75         else if (a > A[o] && B[o] < b);
 76         else if (a > A[o] && B[o] >= b) {
 77             ll d = (B[o] - b) / (a - A[o]);
 78             if (d < len) giveFlag(o << 1, l, mid, a, b);
 79             else {
 80                 swap(A[o], a), swap(B[o], b);
 81                 giveFlag(o << 1 | 1, mid + 1, r, a, b + a * len);
 82             }
 83         }
 84         else if (a < A[o] && B[o] > b) swap(A[o], a), swap(B[o], b);
 85         else if (a < A[o] && B[o] <= b) {
 86             ll d = (B[o] - b - 1) / (a - A[o]) + 1;
 87             if (d >= len) giveFlag(o << 1 | 1, mid + 1, r, a, b + a * len);
 88             else {
 89                 swap(A[o], a), swap(B[o], b);
 90                 giveFlag(o << 1, l, mid, a, b);
 91             }
 92         }
 93     }
 94 
 95     pushup(o, l, r);
 96 }
 97 
 98 void modify(int o, int l, int r, int ql, int qr, ll a, ll b) {
 99     if (ql <= l && r <= qr) {
100         giveFlag(o, l, r, a, b + a * (dis[inv[l]] - dis[inv[ql]]));
101         return;
102     }
103 
104     int mid = l + r >> 1;
105     if (ql <= mid) modify(o << 1, l, mid, ql, qr, a, b);
106     if (qr > mid) modify(o << 1 | 1, mid + 1, r, ql, qr, a, b);
107     pushup(o, l, r);
108 }
109 
110 int n;
111 void modify2(int tar, int x, ll a, ll b) {
112     while (top[tar] != top[x]) {
113         int F = top[x];
114         modify(1, 1, n, pos[F], pos[x], a, b + a * (dis[F] - dis[tar]));
115         x = pre[F];
116     }
117 
118     modify(1, 1, n, pos[tar], pos[x], a, b);
119 }
120 
121 void modify(int l, int r, ll a, ll b) {
122     int lca = LCA(l, r);
123     modify2(lca, l, -a, b + a * (dis[l] - dis[lca]));
124     modify2(lca, r, a, b + a * (dis[l] - dis[lca]));
125 }
126 
127 ll query(int o, int l, int r, int ql, int qr) {
128     ll ret = INF;
129     if (Flag[o]) {
130         int L = max(l, ql), R = min(r, qr);
131         ret = min(A[o] * (dis[inv[L]] - dis[inv[l]]) + B[o], A[o] * (dis[inv[R]] - dis[inv[l]]) + B[o]);
132     }
133 
134     if (ql <= l && r <= qr) return min(ret, Min[o]);
135     else {
136         int mid = l + r >> 1;
137         if (ql <= mid) ret = min(ret, query(o << 1, l, mid, ql, qr));
138         if (qr > mid) ret = min(ret, query(o << 1 | 1, mid + 1, r, ql, qr));
139     }
140     return ret;
141 }
142 
143 ll solve(int l, int r) {
144     ll ret = INF;
145     while (top[l] != top[r]) {
146         if (dep[top[l]] < dep[top[r]]) swap(l, r);
147         ret = min(ret, query(1, 1, n, pos[top[l]], pos[l]));
148         l = pre[top[l]];
149     }
150     if (dep[l] < dep[r]) swap(l, r);
151     ret = min(ret, query(1, 1, n, pos[r], pos[l]));
152 
153     return ret;
154 }
155 
156 int main() {
157     /*
158     freopen("game.in", "r", stdin);
159     freopen("game.out", "w", stdout);
160     */
161     int m; scanf("%d%d", &n, &m);
162     REP(i, 1, n) {
163         int u, v, w; scanf("%d%d%d", &u, &v, &w);
164         addedge(u, v, w);
165     }
166 
167     dfs(1, 1), repos(1, 1);
168 
169     REP(i, 0, maxn << 2) Min[i] = 123456789123456789ll;
170 
171     while (m--) {
172         int op; scanf("%d", &op);
173         if (op == 1) {
174             int u, v, a, b; scanf("%d%d%d%d", &u, &v, &a, &b);
175             modify(u, v, a, b);
176         }
177         else {
178             int s, t; scanf("%d%d", &s, &t);
179             printf("%lld
", solve(s, t));
180         }
181     }
182 
183     return 0;
184 }
game

生成魔咒

  后缀数组,将字符串反过来,维护一个set,每次加入的字符串找到位置,计算贡献。

#include <bits/stdc++.h>
#define rep(i, a, b) for (register int i = a; i <= b; i++)
#define drep(i, a, b) for (register int i = a; i >= b; i--)
#define REP(i, a, b) for (int i = a; i < b; i++)
#define pb push_back
#define mp make_pair
#define clr(x) memset(x, 0, sizeof(x))
#define xx first
#define yy second

using namespace std;

typedef long long ll;
typedef pair<int, int> pii;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fll;
//********************************

const int maxn = 100005;

int a[maxn], hsh[maxn], cd;
int sa[maxn], t[maxn], t2[maxn], c[maxn], n;

void build_sa(int m) {
    int *x = t, *y = t2;
    rep(i, 0, m) c[i] = 0;
    rep(i, 1, n) c[x[i] = a[i]]++;
    rep(i, 1, m) c[i] += c[i - 1];
    drep(i, n, 1) sa[c[x[i]]--] = i;

    for (int k = 1; k <= n; k <<= 1) {
        int p = 0;
        rep(i, n - k + 1, n) y[++p] = i;
        rep(i, 1, n) if (sa[i] > k) y[++p] = sa[i] - k;

        rep(i, 0, m) c[i] = 0;
        rep(i, 1, n) c[x[y[i]]]++;
        rep(i, 1, m) c[i] += c[i - 1];
        drep(i, n, 1) sa[c[x[y[i]]]--] = y[i];

        swap(x, y);
        p = 1; x[sa[1]] = 0;
        rep(i, 2, n)
            x[sa[i]] = y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p - 1 : p++;
        if (p >= n) break;
        m = p;
    }
}

int rnk[maxn], height[maxn];
void build_height() {
    int k(0);
    rep(i, 1, n) rnk[sa[i]] = i;
    rep(i, 1, n) {
        if (k) k--;
        int j = sa[rnk[i] - 1];
        while (a[j + k] == a[i + k]) k++;
        height[rnk[i]] = k;
    }
}

int Min[maxn][20], Log[maxn];
void build_st() {
    Log[1] = 0;
    rep(i, 2, n) {
        Log[i] = Log[i - 1];
        if (1 << Log[i] + 1 == i) Log[i]++;
    }
    drep(i, n, 1) {
        Min[i][0] = height[i];
        for (int j = 1; i + (1 << j) - 1 <= n; j++)
            Min[i][j] = min(Min[i][j - 1], Min[i + (1 << j - 1)][j - 1]);
    }
}
int query(int l, int r) {
    int len = r - l + 1, k = Log[len];
    return min(Min[l][k], Min[r - (1 << k) + 1][k]);
}

int main() {
    scanf("%d", &n);
    rep(i, 1, n) scanf("%d", a + i), hsh[i] = a[i]; cd = n;
    sort(hsh + 1, hsh + 1 + n), cd = unique(hsh + 1, hsh + 1 + cd) - hsh - 1;
    rep(i, 1, n) a[i] = lower_bound(hsh + 1, hsh + 1 + cd, a[i]) - hsh;
    rep(i, 1, n / 2) swap(a[i], a[n - i + 1]);

    a[++n] = 0;
    a[n + 1] = inf;
    build_sa(cd);
    build_height();

    build_st();
    ll ans(0);
    set <int> S; S.insert(-inf), S.insert(inf);
    drep(i, n - 1, 1) {
        set<int> :: iterator H = S.lower_bound(rnk[i]), P = S.upper_bound(rnk[i]);
        int A, B;
        if (H != S.begin()) A = *--H; else A = -inf;
        B = *P;
        if (A != -inf) ans += query(min(rnk[i], A) + 1, max(rnk[i], A));
        if (B != inf) ans += query(min(rnk[i], B) + 1, max(rnk[i], B));
        if (A != -inf && B != inf) ans -= query(min(A, B) + 1, max(A, B));
        printf("%lld
", 1ll * (n - i) * (n - i - 1) / 2 + n - i - ans);
        S.insert(rnk[i]);
    }
    return 0;
}
incantation

排列计数

  组合数*错排数

    $f_{n} = (n - 1)(f_{n - 1} + f_{n - 2})$

 1 #include <bits/stdc++.h>
 2 #define rep(i, a, b) for (long long i = a; i <= b; i++)
 3 #define drep(i, a, b) for (long long i = a; i >= b; i--)
 4 #define REP(i, a, b) for (long long i = a; i < b; i++)
 5 #define pb push_back
 6 #define mp make_pair
 7 #define xx first
 8 #define yy second
 9 using namespace std;
10 typedef long long ll;
11 typedef pair<int, int> pii;
12 const int inf = 0x3f3f3f3f;
13 const ll INF = 0x3f3f3f3f3f3f3f3fll;
14 template <typename T> void Max(T& a, T b) { if (b > a) a = b; }
15 //*********************************
16 
17 const int maxn = 1000005;
18 const int mod = 1e9 + 7;
19 
20 ll fac[maxn];
21 ll f[maxn];
22 void prepare() {
23     f[0] = 1, f[1] = 0, f[2] = 1;
24     rep(n, 3, 1000000) f[n] = ((n - 1) * (n - 1) % mod * f[n - 2] % mod + (n - 1) * (n - 2) % mod * f[n - 3] % mod) % mod;
25 
26     fac[0] = 1;
27     rep(n, 1, 1000000) fac[n] = fac[n - 1] * n % mod;
28 }
29 
30 ll POW(ll base, int num) {
31     ll ret = 1;
32     while (num) {
33         if (num & 1) ret = ret * base % mod;
34         base = base * base % mod;
35         num >>= 1;
36     }
37     return ret;
38 }
39 ll C(int n, int m) {
40     ll ret = fac[n];
41     ret = ret * POW(fac[n - m], mod - 2) % mod;
42     ret = ret * POW(fac[m], mod - 2) % mod;
43     return ret;
44 }
45 int main() {
46     prepare();
47     int T; scanf("%d", &T);
48     while (T--) {
49         int n, m; scanf("%d%d", &n, &m);
50         if (n < m) puts("0");
51         else printf("%lld
", C(n, m) * f[n - m] % mod);
52     }
53     return 0;
54 }
permutation

征途

  $s^{2} = frac{sumlimits_{i = 1}^{m}(x_{i} - overline{x})^{2}}{m}$,将$(x_{i} - overline{x})^{2}$展开,会发现我们其实要最小化$sumlimits_{i = 1}^{m}x_{i}^{2}$

  设$f_{i,j}$代表dp到第i位(包括i),是第j段的最小平方和。

  那么$f_{i,j} = min(f_{k,j - 1}) + (s_{i}-s_{k})^{2}$, s为前缀和。

  固定j以后发现只与k有关,斜率优化即可。

#include <bits/stdc++.h>
#define rep(i, a, b) for (int i = a; i <= b; i++)
#define drep(i, a, b) for (int i = a; i >= b; i--)
#define REP(i, a, b) for (int i = a; i < b; i++)
#define pb push_back
#define mp make_pair
#define xx first
#define yy second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fll;
template <typename T> void Max(T& a, T b) { if (b > a) a = b; }
//*********************************

const int maxn = 3005;

ll f[maxn], s[maxn], a[maxn];
inline ll p(ll num) { return num * num; }

struct HH {
    ll x, y;
};
double k(HH A, HH B) { return (double)(A.y - B.y) / (A.x - B.x); }

int main() {
    int n, m; scanf("%d%d", &n, &m);
    rep(i, 1, n) scanf("%lld", a + i), s[i] = s[i - 1] + a[i];

    rep(i, 1, n) f[i] = p(s[i]);

    rep(j, 2, m) {
        static HH que[maxn]; int qh = 1, qt(0);
        rep(i, 1, n) {
            HH t = (HH) {s[i], f[i] + p(s[i])};
            while (qt > qh && k(que[qt], t) < k(que[qt - 1], que[qt])) qt--;
            que[++qt] = t;

            while (qt > qh && k(que[qh], que[qh + 1]) < 2 * s[i]) qh++;

            f[i] = que[qh].y - p(que[qh].x) + p(s[i] - que[qh].x);
        }
    }

    cout << m * f[n] - p(s[n]) << endl;

    return 0;
}
journey

 

原文地址:https://www.cnblogs.com/y7070/p/5406839.html