• 【2019.8.23】测试题目订正


    T2:最大值与最小值

    众所周知,小葱同学擅长计算,尤其组合数但这个题和组合数什么关系。

    给定一张有向图,每个点有点权。试找到一条路径,使得该路径上的点权最大值减去点权最小值最大,问这个差最大是多少。

       缩点后在DAG上DP,对每个dcc维护四个信息preMin/preMax/nxtMin/nxtMax,分别表示所有前驱、后继中的最大、最小点权,都要把这个dcc本身算在内。分别使用拓扑排序、记忆化搜索来更新前驱和后继信息,目标状态即为max{max(preMax[u] - nxtMin[u], nxtMax[u] - preMin[u])},可以保证组合得到最优路径上的差值。

    1. #include <iostream>  
    2. #include <cstdio>  
    3. #include <cstring>  
    4. #include <queue>  
    5. #include <vector>  
    6. #include <cctype>  
    7. #define maxn 100010  
    8. #define maxm 500010  
    9. using namespace std;  
    10. void open_file(string s) {  
    11.     string In = s + ".in", Out = s + ".out";  
    12.     freopen(In.c_str(), "r", stdin);  
    13.     freopen(Out.c_str(), "w", stdout);  
    14. }  
    15. template <class T>  
    16. void read(T &x) {  
    17.     x = 0;  
    18.     int f = 1;  
    19.     char ch = getchar();  
    20.     while (!isdigit(ch)) {  
    21.         if (ch == '-') f = -1;  
    22.         ch = getchar();  
    23.     }  
    24.     while (isdigit(ch)) {  
    25.         x = x * 10 + (ch ^ 48);  
    26.         ch = getchar();  
    27.     }  
    28.     x *= f;  
    29. }  
    30. int n, m;  
    31. int w[maxn], c[maxn], maxx[maxn], minn[maxn];  
    32. struct E {  
    33.     int to, nxt;  
    34. } edge[maxm << 1];  
    35. int head[maxn], head2[maxn], top;  
    36. inline void insert(int* a, int u, int v) {  
    37.     edge[++top] = (E) {v, a[u]};  
    38.     a[u] = top;  
    39. }  
    40. int dfn[maxn], low[maxn], tmr, stp, sta[maxn], cnt;  
    41. bool ins[maxn];  
    42. void tarjan(int u) {  
    43.     dfn[u] = low[u] = ++tmr;  
    44.     sta[++stp] = u, ins[u] = true;  
    45.     for (int i = head[u]; i; i = edge[i].nxt) {  
    46.         int v = edge[i].to;  
    47.         if (!dfn[v]) {  
    48.             tarjan(v);  
    49.             low[u] = min(low[u], low[v]);  
    50.         } else if (ins[v])  
    51.             low[u] = min(low[u], dfn[v]);  
    52.     }  
    53.     if (dfn[u] == low[u]) {  
    54.         ++cnt;  
    55.         int x;  
    56. //      printf("# %d: ", cnt);  
    57.         do {  
    58.             x = sta[stp--];  
    59. //          printf("%d ", x);  
    60.             ins[x] = false;  
    61.             c[x] = cnt;  
    62.             maxx[cnt] = max(maxx[cnt], w[x]);  
    63.             minn[cnt] = min(minn[cnt], w[x]);  
    64.         } while (x != u);  
    65. //      puts("");  
    66.     }  
    67. }  
    68. int ind[maxn];  
    69. void rebuild() {  
    70.     for (int u = 1; u <= n; ++u)  
    71.         for (int i = head[u]; i; i = edge[i].nxt) {  
    72.             int v = edge[i].to;  
    73.             if (c[u] == c[v]) continue;  
    74.             insert(head2, c[u], c[v]), ++ind[c[v]];  
    75.         }  
    76. }  
    77. int ans = -0x3f3f3f3f;  
    78. int preMax[maxn], preMin[maxn], nxtMax[maxn], nxtMin[maxn];  
    79. bool vis[maxn];  
    80. vector<int> st;  
    81. void dfs(int u) { //ºó¼Ì   
    82.     if (vis[u]) return;  
    83.     vis[u] = true;  
    84.     nxtMax[u] = maxx[u], nxtMin[u] = minn[u];  
    85.     for (int i = head2[u]; i; i = edge[i].nxt) {  
    86.         int v = edge[i].to;  
    87.         dfs(v);  
    88.         nxtMax[u] = max(nxtMax[u], nxtMax[v]);  
    89.         nxtMin[u] = min(nxtMin[u], nxtMin[v]);  
    90.     }  
    91. }  
    92. void dp() { //Ç°Çý   
    93.     queue<int> que;  
    94.     for (int i = 1; i <= cnt; ++i) {  
    95.         preMax[i] = maxx[i], preMin[i] = minn[i];  
    96.         if (!ind[i]) que.push(i), st.push_back(i);  
    97.     }  
    98.     while (que.size()) {  
    99.         int u = que.front(); que.pop();  
    100.         for (int i = head2[u]; i; i = edge[i].nxt) {  
    101.             int v = edge[i].to;  
    102.             preMax[v] = max(preMax[v], preMax[u]);  
    103.             preMin[v]=  min(preMin[v], preMin[u]);  
    104.             if (!(--ind[v])) que.push(v);  
    105.         }  
    106.     }  
    107.     return;  
    108. }  
    109. void init() {  
    110.     memset(maxx, -0x3f, sizeof(maxx));  
    111.     memset(minn, 0x3f, sizeof(minn));  
    112.     memset(preMax, -0x3f, sizeof(preMax));  
    113.     memset(preMin, 0x3f, sizeof(preMin));  
    114.     memset(nxtMax, -0x3f, sizeof(nxtMax));  
    115.     memset(nxtMin, 0x3f, sizeof(nxtMin));  
    116. }  
    117. int main() {  
    118.     open_file("b");  
    119.     init();  
    120.     read(n), read(m);  
    121.     for (int i = 1; i <= n; ++i)  
    122.         read(w[i]);  
    123.     int u, v;  
    124.     for (int i = 1; i <= m; ++i) {  
    125.         read(u), read(v);  
    126.         insert(head, u, v);  
    127.     }  
    128.       
    129.     for (int i = 1; i <= n; ++i)   
    130.         if (!dfn[i]) tarjan(i);  
    131.     rebuild();  
    132.     dp();  
    133.     for (int i = 0; i < st.size(); ++i)  
    134.         dfs(st[i]);  
    135.     for (int i = 1; i <= n; ++i)  
    136.         ans = max(ans, max(nxtMax[i] - preMin[i], preMax[i] - nxtMin[i]));  
    137.     printf("%d ", ans);  
    138.     return 0;  

      

    T3:两把刷子

    众所周知,小葱同学擅长计算,尤其擅长组合数,但这个题和组合数没什么关系。

    有N个人 ,每都有两把刷子,每个刷子都有一个属性值。如果说一个人拿着的两把刷子的属性值之差的绝对值超过了c,则这个人无法使用他的两把刷 。现在你可以选择交换不同人的某把刷子, 使得每个人都能够使用他们的刷子, 问最小所需要的交换次数。

      状压dp,状态k的各位表示这个人是否在当前集合中。定义把集合k分成若干个不重不漏的子集称为集合k的一个独立子集划分,独立集满足其内部本身可以通过交换成为合法的状态。找规律得一个大小为m的集合的最少交换次数cnt = m - f[k],其中f[k]表示k最大的独立子集划分所包含的子集数。显然,若一个集合本身可以经过交换成为合法,则自身至少能够作为一个独立集,f初值设为1,否则为-inf。(f[0] = 0)

      转移方程f[k] = max(f[i] + f[k ^ i]),其中i是k的子集,i^k是对应的补集。由于不容易确定转移顺序,使用记忆化搜索进行转移。对每一个状态sort后暴力两两作差判断是否能够成为独立集。

    1. #include <iostream>  
    2. #include <cstdio>  
    3. #include <vector>  
    4. #include <cmath>  
    5. #include <algorithm>  
    6. const int inf = 0x3f3f3f3f;  
    7. using namespace std;  
    8. int a[2][17], n, c;  
    9. int f[1 << 17];  
    10. vector<int> tmp;  
    11. int check(int k) {  
    12.     tmp.clear();  
    13.     for (int i = 0; i < n; ++i)  
    14.         if ((1 << i) & k) tmp.push_back(a[0][i + 1]), tmp.push_back(a[1][i + 1]);  
    15.     sort(tmp.begin(), tmp.end());  
    16.     for (int i = 0; i < tmp.size(); i += 2)  
    17.         if (abs(tmp[i] - tmp[i + 1]) > c)      
    18.             return -inf;  
    19.     return 1;  
    20. }  
    21. void dfs(int k) {  
    22.     if (f[k] || !k) return;  
    23.     f[k] = check(k);  
    24.     for (int i = k; i; i = k & (i - 1)) {  
    25.         dfs(i); dfs(k ^ i);  
    26.         f[k] = max(f[k], f[i] + f[k^i]);  
    27.     }  
    28.     return;  
    29. }  
    30. int main() {  
    31.     freopen("c.in", "r", stdin);  
    32.     freopen("c.out", "w", stdout);  
    33.     cin >> n >> c;  
    34.     for (int i = 1; i <= n; ++i)  
    35.         cin >> a[0][i] >> a[1][i];  
    36.     dfs((1 << n) - 1);  
    37.     printf("%d", n - f[(1 << n) - 1]);  
    38.     return 0;  
    39. }  

    T4:fib数列

    众所周知,小葱同学擅长计算,尤其擅长计算组合数,但这个题和组合数没什么关系。

    给定 N个数,M次操作 ,操作有如下两种 :

    1. 给定 l,r,询问第l个数到第r个数的和。

    2. 给定 l,r,x,将这段区间的第i个数加上f(i+x),其中f(i+x)代表的是斐波那契 数列的第 i + x + 1项 。 对于斐波那契数列我们有 f(0)=0,f(1)=1,f(n)=f(n-1)+f(n-2)。

       

      考虑使用线段树,需要解决的有如下两个问题:

      1、如何设置标记tag,使其符合可加性并且能够由之快速推出当前区间的累加值?

      2、如何下推这个标记?

      如果我们记录每个区间所累计的fib数的前两项,显然由这两个值和数列长度可以推出整个区间所加的数列。并且这个tag是可以累加的,证明略。那么问题就集中在了如何用两个任意的数作为首项,快速地求出对应“广义斐波那契数列”的前n项和。

      设f表示经典斐波那契数列,f[0] = 0, f[1] = 1, 数列S表示其前缀和;定义广义斐波那契数列F,F[0] = a,F[1] = b,F[i] = F[i - 1] + F[i - 2](i >= 2);令S’表示其前缀和。

      F、S'满足如下通项式:

      F[n] = f[n-1] * a + f[n] * b

      S'[n] = S[n-1] * a + S[n] * b = (f[n + 1] - 1)a + (f[n + 2] - 1)b

      利用这两个式子,我们可以O(1)求出某个区间的累计加和与它右儿子的两个递推首项,那么施加标记和下推标记就解决了。以下的代码中tag维护的是当前区间累计的最左端项b和它前面的一项a,原理相同。有些难调……

      给出的正解为用矩阵快速幂来推广义fib数列的通项,常数较大,但是相应地不需要记这个结论。但是你要会写矩阵快速幂

    1. #include <iostream>  
    2. #include <cstdio>  
    3. #include <vector>  
    4. #include <cmath>  
    5. #include <ctime>  
    6. #define BUG puts("$$$")  
    7. #define A first  
    8. #define B second  
    9. #define mp make_pair  
    10. #define LL long long  
    11. using namespace std;  
    12. const LL mod = 1e9 + 7; const int maxn = 1e5 + 10;  
    13. using namespace std;  
    14. pair<LL, LL> operator + (pair<LL, LL> a, pair<LL, LL> b) {   
    15.     return mp((a.A + b.A) % mod, (a.B + b.B) % mod);  
    16. }  
    17. void open_file(string s) {  
    18.     string In = s + ".in", Out = s + ".out";  
    19.     freopen(In.c_str(), "r", stdin);  
    20.     freopen(Out.c_str(), "w", stdout);  
    21. }  
    22. template <class T>  
    23. void read(T& x) {  
    24.     x = 0;  
    25.     T f = 1;  
    26.     char ch = getchar();  
    27.     while (!isdigit(ch)) {  
    28.         if (ch == '-')  
    29.             f = -1;  
    30.         ch = getchar();  
    31.     }  
    32.     while (isdigit(ch)) {  
    33.         x = x * 10 + (ch ^ 48);  
    34.         ch = getchar();  
    35.     }  
    36.     x *= f;  
    37. }  
    38. int n, m;  
    39. LL fib[maxn << 1];  
    40. void init() {  
    41.     fib[0] = 0, fib[1] = 1;  
    42.     for (register int i = 2; i < (maxn << 1); ++i) {  
    43.         fib[i] = (fib[i - 1] + fib[i - 2]) % mod;  
    44.     }  
    45. }  
    46. inline LL get_Sn(pair<LL, LL> op, LL N) {  //广义Sn
    47.     return (((op.A * (fib[N + 1] - 1)) + (op.B * (fib[N + 2] - 1)))) % mod;  
    48. }  
    49. inline LL get_F(pair<LL, LL> op, LL N) {  //广义fib
    50.     if (N == 0)  
    51.         return op.A;  
    52.     return ((op.A * fib[N - 1]) % mod + (op.B * fib[N])) % mod;  
    53. }  
    54. LL a[maxn];  
    55. namespace Segment_tree {  
    56.     #define lc (nd << 1)  
    57.     #define rc ((nd << 1) | 1)  
    58.     #define mid ((l + r) >> 1)  
    59.     struct node {  
    60.         LL dat; int len;  
    61.         inline friend node operator + (node a, node b) {  
    62.             return (node){(a.dat + b.dat) % mod, a.len + b.len};  
    63.         }  
    64.     } seg[maxn << 2];  
    65.     pair<LL, LL> tag[maxn << 2];  
    66.     inline void update(int nd) {  
    67.         seg[nd] = seg[lc] + seg[rc];  
    68.     }  
    69.     inline void put_tag(int nd, pair<LL, LL> op) {  
    70.         seg[nd].dat = (seg[nd].dat + get_Sn(op, seg[nd].len)) % mod;  
    71.         tag[nd] = tag[nd] + op;  
    72.     }  
    73.     inline void push_down(int nd) {  
    74.         put_tag(lc, tag[nd]);  
    75.         put_tag(rc, mp(get_F(tag[nd], seg[lc].len), get_F(tag[nd], seg[lc].len + 1)));  //右儿子的tag需要通项公式得出
    76.         tag[nd] = mp(0, 0);  
    77.     }  
    78.     void build(int nd, int l, int r) {  
    79.         if (l == r) {  
    80.             seg[nd] = (node) {a[l] % mod, 1};  
    81.             return;  
    82.         }  
    83.         build(lc, l, mid);  
    84.         build(rc, mid + 1, r);  
    85.         update(nd);  
    86.     }  
    87.     void modify(int nd, int l, int r, const int& ql, const int& qr, const int& id) {  
    88.         if (l >= ql && r <= qr) {  
    89.             LL x = l - ql;
    90.             put_tag(nd, mp(fib[x + id], fib[x + id + 1]));  
    91.             return;  
    92.         } else if (l > qr || r < ql)  
    93.             return;  
    94.         push_down(nd);  
    95.         modify(lc, l, mid, ql, qr, id);  
    96.         modify(rc, mid + 1, r, ql, qr, id);  
    97.         update(nd);  
    98.     }  
    99.     LL query(int nd, int l, int r, const int& ql, const int& qr) {  
    100.         if (l >= ql && r <= qr) {  
    101.             return seg[nd].dat;  
    102.         } else if (l > qr || r < ql)  
    103.             return 0;  
    104.         push_down(nd);  
    105.         return (query(lc, l, mid, ql, qr) + query(rc, mid + 1, r, ql, qr)) % mod;  
    106.     }  
    107. using namespace Segment_tree;  
    108. int main() {  
    109.     open_file("d");  
    110.     read(n), read(m);  
    111.     for (register int i = 1; i <= n; ++i)  
    112.         read(a[i]);  
    113.     init();  
    114.     build(1, 1, n);  
    115.     int l, r, opt, x;  
    116.     for (register int i = 1; i <= m; ++i) {  
    117.         read(opt), read(l), read(r);  
    118.         if (opt == 1) {  
    119.             printf("%lld ", query(1, 1, n, l, r));  
    120.         } else {  
    121.             read(x);  
    122.             modify(1, 1, n, l, r, x - 1);  
    123.         }  
    124.     }  
    125.     return 0;  
    126. }  
  • 相关阅读:
    bzoj5137 [Usaco2017 Dec]Standing Out from the Herd
    bzoj2434 [Noi2011]阿狸的打字机
    【20181024T2】小C的序列【GCD性质+链表】
    【20181024T3】小C的宿舍【分治】
    【20181024T1】小C的数组【二分+dp】
    【20181023T2】行星通道计划【二维BIT】
    【20181023T1】战争【反向并查集】
    【20181020T1】蛋糕
    【20181019T2】硬币【矩阵快速幂优化DP】
    【20181019T3】比特战争【最小生成树思想】
  • 原文地址:https://www.cnblogs.com/TY02/p/11405545.html
Copyright © 2020-2023  润新知