zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 40 (Rated for Div. 2) Solution

    Problem A Diagonal Walking

    题目大意

      给定一个只包含'U'和'R'的字符串,你可以将"RU"或者'UR"替换为"D"。问能使串长能变成的最小值。

      直接把 RU 或者 UR 替换为 D 即可。

    Code

    /**
     * Codeforces
     * Problem#954A
     * Accepted
     * Time: 31ms
     * Memory: 0k
     */
    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    int n;
    char s[233];
    
    int main() {
    	scanf("%d", &n);
    	scanf("%s", s + 1);
    	int ans = n;
    	for (int i = 1; i < n; i++) {
    		if (s[i] ^ s[i + 1]) {
    			i++, ans--;
    			continue;
    		}
    	}
    	printf("%d
    ", ans);
    	return 0;
    }

    Problem B String Typing

    题目大意

      有一台打字机,要输入1个字符串,有两种操作:

    1. 向末尾输入一个字符
    2. 将当前输入的字符串复制一遍粘在后面

      操作2只能使用1次。问最少的操作次数。

      没看到只能用1次,傻傻地写了个dp。并成功获得了:

      然后急急忙忙去加个状态。

      其实只能用1次直接贪心就好了。当减少的操作次数最多的时候用操作2.

    Code

     1 /**
     2  * Codeforces
     3  * Problem#954B
     4  * Accepted
     5  * Time: 31ms
     6  * Memory: 0k
     7  */
     8 #include <bits/stdc++.h>
     9 using namespace std;
    10 typedef bool boolean;
    11 
    12 int n;
    13 char str[105];
    14 int f[105][2];
    15 
    16 inline void init() {
    17     scanf("%d", &n);
    18     scanf("%s", str + 1);
    19 }
    20 
    21 boolean equal(int l1, int l2, int len) {
    22     for (int i = 0; i < len; i++)
    23         if (str[l1 + i] != str[l2 + i])
    24             return false;
    25     return true;
    26 }
    27 
    28 inline void solve() {
    29     f[0][1] = 2000;
    30     for (int i = 1; i <= n; i++) {
    31         f[i][0] = f[i - 1][0] + 1;
    32         f[i][1] = f[i - 1][1] + 1;
    33         if (!(i & 1) && (equal(1, (i >> 1) + 1, i >> 1)))
    34             f[i][1] = min(f[i][1], f[i >> 1][0] + 1);
    35     }
    36     printf("%d
    ", min(f[n][0], f[n][1]));
    37 }
    38 
    39 int main() {
    40     init();
    41     solve();
    42     return 0;
    43 }
    Problem B

    Problem C Matrix Walk

    题目大意

      有一个$x imes y$的网格图,但是不知道$x, y$的值。规定$i$行$j$列的格子的标号为$(i - 1)y + j$。

      给定一个长度为$n$的经过的格子序列,规定只能走相邻的格子,且不能走出边界,也不能停留在同一个格子。问是否可能存在一组$x, y$使得路径合法,如果存在,输出这一组。

      找最大的一组相邻的差作为$y$。

      $x$足够大就好了(为什么?因为没影响)。

      然后代进去检验是否合法。

    Code

     1 /**
     2  * Codeforces
     3  * Problem#954C
     4  * Accepted
     5  * Time: 61ms
     6  * Memory: 800k
     7  */
     8 #include <bits/stdc++.h>
     9 using namespace std;
    10 typedef bool boolean;
    11 
    12 int n;
    13 int *ar;
    14 
    15 inline void init() {
    16     scanf("%d", &n);
    17     ar = new int[(n + 1)];
    18     for (int i = 1; i <= n; i++)
    19         scanf("%d", ar + i);
    20 }
    21 
    22 int mxd = 1;
    23 set<int> s;
    24 void termin() {
    25     puts("NO");
    26     exit(0);
    27 }
    28 
    29 inline void solve() {
    30     for (int i = 1; i < n; i++) {
    31         int dif = abs(ar[i + 1] - ar[i]);
    32         if (!dif)
    33             termin();
    34         if (mxd > 1 && dif > 1 && dif != mxd)
    35             termin();
    36         if (dif > 1)
    37             mxd = dif;
    38     }
    39     
    40     int ly = ar[1] % mxd;
    41     if (!ly) ly = mxd;
    42     int lx = (ar[1] - ly) / mxd + 1;
    43     for (int i = 2, cx, cy; i <= n; i++) {
    44         cy = ar[i] % mxd;
    45         if (!cy) cy = mxd;
    46         cx = (ar[i] - cy) / mxd + 1;
    47         if (abs(cx - lx) + abs(cy - ly) != 1)
    48             termin();
    49         lx = cx, ly = cy;
    50     }
    51     puts("YES");
    52     printf("%d %d", 1000000000, mxd);
    53 }
    54 
    55 int main() {
    56     init();
    57     solve();
    58     return 0;
    59 }
    Problem C

    Problem D Fight Against Traffic

    题目大意

      给定$n$个点$m$条边的无向连通简单图。每条边边权均为1。

      要求加一条原本不存在的边,使得新图没有自环,$s, t$之间的距离不改变。

      问方案数。

      看这数据范围挺小的。

      分别跑出$s, t$的最短路径生成树。

      然后枚举边的两个端点,判断加入后新产生的路径长度是否合法。

    Code

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef bool boolean;
     4 
     5 const int N = 1e3 + 5;
     6 
     7 int n, m, s, t;
     8 boolean g[N][N];
     9 int f1[N], f2[N];
    10 
    11 inline void init() {
    12     scanf("%d%d%d%d", &n, &m, &s, &t);
    13     for (int i = 1, u, v; i <= m; i++) {
    14         scanf("%d%d", &u, &v);
    15         g[u][v] = true, g[v][u] = true;
    16     }
    17 }
    18 
    19 typedef class Node {
    20     public:
    21         int p, dis;
    22 
    23         Node(int p = 0, int dis = 0):p(p), dis(dis) {    }
    24 
    25         boolean operator < (Node b) const {
    26             return dis > b.dis;
    27         }
    28 }Node;
    29 
    30 priority_queue<Node> que;
    31 void dijstra(int s, int* f) {
    32     memset(f, 0x7f, sizeof(int) * (n + 1));
    33     que.push(Node(s, f[s] = 0));
    34     while (!que.empty()) {
    35         Node e = que.top();
    36         que.pop();
    37         if (e.dis != f[e.p])
    38             continue;
    39         for (int i = 1; i <= n; i++)
    40             if (g[e.p][i] && e.dis + 1 < f[i])
    41                 que.push(Node(i, f[i] = e.dis + 1));
    42     }
    43 }
    44 
    45 int ans = 0;
    46 inline void solve() {
    47     dijstra(s, f1);
    48     dijstra(t, f2);
    49     for (int i = 1; i <= n; i++)
    50         for (int j = i + 1; j <= n; j++)
    51             if (!g[i][j] && f1[i] + f2[j] + 1 >= f1[t] && f1[j] + f2[i] + 1 >= f1[t])
    52                 ans++;
    53     printf("%d", ans);
    54 }
    55 
    56 int main() {
    57     init();
    58     solve();
    59     return 0;
    60 }
    Problem D

    Problem E Water Taps

    题目大意

      给定$n, T, a_{i}, t_{i}$,找出一组$x_{i}$满足$0leqslant x_{i} leqslant a_{i}$,且$frac{sum_{i = 1}^{n}x_{i}t_{i}}{sum_{i = 1}^{n}x_{i}} = T$。问$sum_{i = 1}^{n}x_{i}$的最大值。无解输出0。

      移项,排序后贪心即可。

    Code

    /**
     * Codeforces
     * Problem#954E
     * Accepted
     * Time: 93ms
     * Memory: 4400k
     */
    #include <bits/stdc++.h>
    using namespace std;
    typedef bool boolean;
    
    const int N = 2e5 + 5;
    
    #define ll long long
    #define pii pair<int, int>
    
    int n, T;
    ll sum = 0;
    int a[N];
    vector<pii> vL, vR;
    
    int main() {
    	scanf("%d%d", &n, &T);
    	double ans = 0;
    	for (int i = 1; i <= n; i++) {
    		scanf("%d", a + i);
    		ans += a[i];
    	}
    	for (int i = 1, t; i <= n; i++) {
    		scanf("%d", &t);
    		sum += 1ll * a[i] * (t - T);
    		if (t <= T) {
    			vL.emplace_back(T - t, a[i]);
    		} else {
    			vR.emplace_back(t - T, a[i]);
    		}
    	}
    	if (sum < 0) {
    		sum = -sum;
    	} else {
    		vL = vR;
    	}
    	sort(vL.begin(), vL.end(), greater<pii>());
    	for (auto e : vL) {
    		if (!sum)
    			break;
    		ll tmp = 1ll * e.first * e.second;
    		if (tmp > sum) {
    			ans -= 1.0 * sum / e.first;
    			break;
    		} else {
    			sum -= tmp;
    			ans -= e.second;
    		}
    	}
    	printf("%.9lf
    ", ans);
    	return 0;
    }

    Problem F Runner's Problem

    题目大意

      给定一个$3 imes m$的棋盘,在$(2, 1)$的地方有一个棋子,棋子每次只能移动到下一列的相邻的行或者当前行。

      棋盘上有$n$段障碍,第$i$段障碍是在第$a_{i}$行的$l_{i}$列到第$r_{i}$列。棋子不能到障碍格上。

      问棋子从$(2, 1)$到$(2, m)$的方案数。(答案模$10^{9} + 7$)

      随便写写dp方程,把转移表变成矩阵。跑快速幂。

    Code

      1 /**
      2  * Codeforces
      3  * Problem#954F
      4  * Accepted
      5  * Time: 390ms
      6  * Memory: 1700k
      7  */
      8 #include <bits/stdc++.h>
      9 #ifndef WIN32
     10 #define Auto "%lld"
     11 #else
     12 #define Auto "%I64d"
     13 #endif
     14 using namespace std;
     15 typedef bool boolean;
     16 
     17 #define ll long long
     18 #define pli pair<ll, int>
     19 
     20 typedef class Segment {
     21     public:
     22         ll l, r;
     23         int sta;
     24 
     25         Segment() {        }
     26         Segment(ll l, ll r, int sta):l(l), r(r), sta(sta) {    }
     27 }Segment;
     28 
     29 const int M = 1e9 + 7;
     30 
     31 typedef class Matrix {
     32     public:
     33         int r, c;
     34         int a[3][3];
     35 
     36         Matrix(int r = 0, int c = 0):r(r), c(c) {}
     37 
     38         Matrix operator * (Matrix b) {
     39             Matrix rt(r, b.c);
     40             assert(c == b.r);
     41             for (int i = 0; i < r; i++)
     42                 for (int j = 0; j < b.c; j++) {
     43                     rt[i][j] = 0;
     44                     for (int k = 0; k < c; k++)
     45                         rt[i][j] = (rt[i][j] + a[i][k] * 1ll * b[k][j]) % M;
     46                 }
     47             return rt;
     48         }
     49 
     50         int* operator [] (int p) {
     51             return a[p];
     52         }
     53 }Matrix;
     54 
     55 const int N = 3e4 + 6;
     56 
     57 Matrix qpow(Matrix a, ll p) {
     58     assert(p >= 0);
     59     Matrix pa = a, rt(3, 3);
     60     for (int i = 0; i < 3; i++)
     61         for (int j = 0; j < 3; j++)
     62             rt[i][j] = (i == j);
     63     for ( ; p; p >>= 1, pa = pa * pa)
     64         if (p & 1)
     65             rt = rt * pa;
     66     return rt;
     67 }
     68 
     69 int n;
     70 ll m;
     71 pli ad[N], rm[N];
     72 Segment ss[N];
     73 
     74 inline void init() {
     75     scanf("%d"Auto, &n, &m);
     76     int a;
     77     ll l, r;
     78     for (int i = 1; i <= n; i++) {
     79         scanf("%d"Auto""Auto, &a, &l, &r);
     80         ad[i].first = l, ad[i].second = a - 1;
     81         rm[i].first = r + 1, rm[i].second = a - 1;
     82     }
     83 }
     84 
     85 Matrix mktrans(int os, int ns) {
     86     Matrix rt(3, 3);
     87     for (int i = 0; i < 3; i++)
     88         for (int j = 0; j < 3; j++)
     89             rt[i][j] = (abs(i - j) <= 1 && !(os & (1 << i)) && !(ns & (1 << j)));
     90     return rt;
     91 }
     92 
     93 int ts[4], tp = 0;
     94 inline void solve() {
     95     sort(ad + 1, ad + n + 1);
     96     sort(rm + 1, rm + n + 1);
     97     int ca = 1, cb = 1;
     98     ll st = 1, ed;
     99     while (ca <= n || cb <= n) {
    100         while (ca <= n && ad[ca].first == st)
    101             ts[ad[ca].second]++, ca++;
    102         while (cb <= n && rm[cb].first == st)
    103             ts[rm[cb].second]--, cb++;
    104         
    105         int sta = 0;
    106         for (int i = 0; i < 3; i++)
    107             if (ts[i])
    108                 sta |= (1 << i);
    109 
    110         ed = m + 1;
    111         if (ca <= n)
    112             ed = ad[ca].first;
    113         if (cb <= n)
    114             ed = min(ed, rm[cb].first);
    115         ss[++tp] = Segment(st, ed - 1, sta);
    116         st = ed; 
    117     }
    118 
    119     Matrix f(1, 3);
    120     f[0][0] = f[0][2] = 0, f[0][1] = 1;
    121     Matrix T = mktrans(ss[1].sta, ss[1].sta);
    122     f = f * qpow(T, ss[1].r - ss[1].l);
    123 //        cerr << f[0][0] << " " << f[0][1] << " " << f[0][2] << endl;
    124     for (int i = 2; i <= tp; i++) {
    125         T = mktrans(ss[i - 1].sta, ss[i].sta);
    126         f = f * T;
    127         T = mktrans(ss[i].sta, ss[i].sta);
    128         f = f * qpow(T, ss[i].r - ss[i].l);
    129 //        cerr << ss[i].l << " " << ss[i].r << endl;
    130 //        cerr << f[0][0] << " " << f[0][1] << " " << f[0][2] << endl;
    131     }
    132     printf("%d
    ", f[0][1]);
    133 }
    134 
    135 int main() {
    136     init();
    137     solve();
    138     return 0;
    139 }
    Problem F

    Problem G Castle Defense

    题目大意

      有片城墙被分为$n$个防守段。第$i$段有$a_{i}$个弓箭手。定义一段防守段的防御力是距离它的距离不超过$r$的所有防守段的弓箭手数量之和。定义这片城墙的稳定程度是每个防守段的防御力的最小值。

      现在有$k$个后备弓箭手,你可将他们分配任何防守段,但不能调动原有的弓箭手。问调动后最大的稳定程度。

      显然二分答案。

      考虑怎么check。对于一个防御值小于$mid$的防守段$i$,能对它产生贡献的一些防守段中分配的后备弓箭手总数不得小于$mid - def[i[$。

      然后设在第$i$个防守段设置的弓箭手数量为$a_{i}$,对它求一个前缀和$s$。

      显然使用差分约束,然后连边就很显然了:

    • $s_{r} - s_{l - 1} leqslant mid - def[i], (def[i] < mid)$
    • $s[i]leqslant s[i + 1]$

      由于图比较特殊,不必用最短路算法。直接一个for,完事。

      (感觉把防守段替换为烽火台食用更佳,总之原题说的是section,实在不知道怎么翻译比较好)。

    Code

     1 /**
     2  * Codeforces
     3  * Problem#954G
     4  * Accepted
     5  * Time: 358ms
     6  * Memory: 13700k
     7  */
     8 #include <bits/stdc++.h>
     9 #ifndef WIN32
    10 #define Auto "%lld"
    11 #else
    12 #define Auto "%I64d"
    13 #endif
    14 using namespace std;
    15 typedef bool boolean;
    16 
    17 #define ll long long
    18 
    19 const int N = 5e5 + 5;
    20 
    21 int n, r;
    22 ll k;
    23 int ar[N];
    24 ll ps[N], def[N];
    25 ll f[N];
    26 
    27 inline void init() {
    28     scanf("%d%d"Auto, &n, &r, &k);
    29     for (int i = 1; i <= n; i++)
    30         scanf("%d", ar + i);
    31     for (int i = 1; i <= n; i++)
    32         ps[i] = ps[i - 1] + ar[i];
    33 }
    34 
    35 boolean check(ll mid) {
    36     memset(f, 0, sizeof(f));
    37     for (int i = 1; i <= n; i++) {
    38         f[i] = max(f[i - 1], f[i]);
    39         if (def[i] >= mid)
    40             continue;
    41         int l = max(i - ::r, 1), r = min(i + ::r, n);
    42         f[r] = max(f[l - 1] + mid - def[i], f[r]);
    43     }
    44     return f[n] <= k;
    45 }
    46 
    47 inline void solve() {
    48     for (int i = 1; i <= n; i++) {
    49         int l = max(i - ::r, 1), r = min(i + ::r, n);
    50         def[i] = ps[r] - ps[l - 1];
    51     }
    52     
    53     ll l = 0, r = 2e18;
    54     while (l <= r) {
    55         ll mid = (l + r) >> 1;
    56         if (check(mid))
    57             l = mid + 1;
    58         else
    59             r = mid - 1;
    60     }
    61     printf(Auto, l - 1);
    62 }
    63 
    64 int main() {
    65     init();
    66     solve();
    67     return 0;
    68 }
    Problem G

    Problem H Path Counting

    题目大意

      给定一棵深度为$n$的树,根节点的深度为1,深度为$i$的点的度数为$a_{i}$,保证$a_{n} = 0$。要求对于每个$1leqslant d leqslant 2n - 2$,输出长度为$d$的简单无向路径数量对模$10^9 + 7$的剩余。

      现在说一下一个非常常见的计数对象:取路径的最高点。

      发现每个深度的每个子树的情况完全相同,因此只需要对每个深度$i$计算答案,然后乘上一个常数$c_{i}$。

      设$g_{i,d}$表示在深度为$i$的一个子树中,距离根节点$d$条边的点的数量。

      那么有:

    $ans_{d} = sum_{i = 1}^{n}left[g_{i, d} + left(sum_{j = 1}^{left lfloor d/2 ight floor}g_{i + 1, j - 1}cdot g_{i +1, d - j - 1} ight )a_{i}(a_{i} - 1) - [2mid d]g_{i + 1,d/2 - 1}^2 ight ]c_{i}$

      然后来慢慢解释这个式子。

      $g_{i, d}$是从当前点出发的路径数量,$中间求和是拼接两条路径,为了防止算重,选择两个不同的子树,并要求第一个子树选择的路径长度小于等于第二个子树中选择,当选择路径长度不同时,正反都可以,所以答案乘2,再减去选择路径长度相同的一部分的答案就行了。

      然后开心地发现这个式子是三方的。

      接着发现$g, c$都是可以直接算的:

    $g_{i, d}=prod_{j = 0}^{d - 1}a_{i + j}$

    $c_{i} = prod_{j = 1}^{i - 1}a_{j}$

      中间的一坨求和式子可以看成,枚举任意$i$,然后在$i + 1$后面选择两段连续的$a$,两段都要包含$a_{i + 1}$,长度和为$d - 2$,再把这两段的积乘起来。

      然后很容易发现,设$f_{i, d} = sum_{j = 0}^{left lfloor d/2 ight floor}g_{i, j}cdot g_{i, d - j}$,那么算$f_{i + 1, d - 2}$的时候可以把$j = 0$的一部分先刨开,然后除以$a_{i}^{2}$就行了。

    $f_{i + 1, d - 2} = (f_{i, d} - g_{i, d})a_{i}^{-2}$

      于是这个zz做法可以实现$O(n^{2})$。常数贼大,跑得贼慢,sad。。。

      然后看了看标算。标算取路径两端点作为计数点,这样每条路径会被计算2次,答案除以2就行了。对于以某个点为端点的路径可以分成两种,第一种是向上走(子树外),第二种是向下走(子树内)。第二部分显然,第一部分转移考虑它走到父节点后的方向,时间复杂度一样,但常数小很多。

      1 /**
      2  * Codeforces
      3  * Problem#954H
      4  * Accepted
      5  * Time: 1544ms
      6  * Memory: 200k
      7  */
      8 #include <bits/stdc++.h>
      9 using namespace std;
     10 typedef bool boolean;
     11 
     12 const int N = 5005, M = 1e9 + 7;
     13 
     14 void exgcd(int a, int b, int& x, int& y) {
     15     if (!b)
     16         x = 1, y = 0;
     17     else {
     18         exgcd(b, a % b, y, x);
     19         y -= (a / b) * x;
     20     }
     21 }
     22 
     23 int inv(int a, int n) {
     24     int x, y;
     25     exgcd(a, n, x, y);
     26     return (x < 0) ? (x + n) : (x);
     27 }
     28 
     29 int n;
     30 int inv2 = (M + 1) >> 1;
     31 int ar[N], br[N];
     32 int pro[N];
     33 int ans[N << 1];
     34 int f[2][N << 1];
     35 
     36 int add(int a, int b) {
     37     a += b;
     38     if (a >= M)
     39         a -= M;
     40     return a;
     41 }
     42 
     43 int sub(int a, int b) {
     44     a -= b;
     45     if (a < 0)
     46         a += M;
     47     return a;
     48 }
     49 
     50 inline void init() {
     51     scanf("%d", &n);
     52     for (int i = 1; i < n; i++)
     53         scanf("%d", ar + i);
     54     ar[n] = 0;
     55     for (int i = 1; i <= n; i++)
     56         br[i] = inv(ar[i], M);
     57 }
     58 
     59 int pow2(int x) {
     60     return x * 1ll * x % M;
     61 }
     62 
     63 inline void solve() {
     64     int cur = 0, nxt = 1, P = 1;
     65     pro[0] = 1;
     66     for (int i = 1; i <= n; i++)
     67         pro[i] = pro[i - 1] * 1ll * ar[i] % M;
     68     f[cur][0] = 1;
     69     for (int d = 1; d <= ((n - 1) << 1); d++)
     70         for (int i = max(0, d - n); i <= n && i <= d && i <= d - i; i++)
     71             f[cur][d] = add(f[cur][d], pro[i] * 1ll * pro[d - i] % M);
     72     for (int i = 1; i < n; i++) {
     73         if (i > 1) {
     74             pro[0] = 1;
     75             for (int j = 0; i + j <= n; j++)
     76                 pro[j + 1] = pro[j] * 1ll * ar[i + j] % M;
     77         }
     78         for (int d = 2, x, C; d <= ((n - i) << 1); d++) {
     79             x = ((d <= n - i) ? (pro[d]) : (0));
     80             C = ar[i] * 1ll * (ar[i] - 1) % M * P % M;
     81             f[nxt][d - 2] = sub(f[cur][d], x) * 1ll * br[i] % M * 1ll * br[i] % M;
     82             ans[d] = add(ans[d], f[nxt][d - 2] * 1ll * C % M);
     83             if (!(d & 1))
     84                 ans[d] = sub(ans[d], pow2(pro[d >> 1] * 1ll * br[i] % M) * 1ll * C % M * inv2 % M);
     85         }
     86         for (int d = 1; d <= n - i; d++)
     87             ans[d] = add(ans[d], pro[d] * 1ll * P % M);
     88         P = P * 1ll * ar[i] % M;
     89         swap(cur, nxt);
     90     }
     91 
     92     for (int i = 1; i <= (n - 1) << 1; i++)
     93         printf("%d ", ans[i]);
     94 }
     95 
     96 int main() {
     97     init();
     98     solve();
     99     return 0;
    100 }
    Problem H

    Problem I Yet Another String Matching Problem

    题目大意

      定义一个操作是将串内的一种字符改成另一种字符。定义两个串的编辑距是使得这两个串相等的最少操作数。

      给定两个串$S, T left(|S|leqslant |T| ight)$,询问$S$的每个串长等于$|T|$的子串和$|T|$的编辑距。字符集 'a' ~ 'f' 

      进行一种操作相等于让两种字符等价。这样我们可以将1次操作看作在无向图中将两个字符连接起来。

      考虑对于$S_{i} eq T_{j}$,那么$S_{i}$需要和$T_{j}$等价,那么在无向图中连一条边。答案是字符集大小减去连通块个数。

      然后讲标算。标算比较套路,注意到边的数量不超过30条,因此想办法判断每条边在每一次询问中是否存在。

      考虑枚举这条边$(a, b)$,将$S$出现$a$的地方标为1,将$T$出现$b$的地方也标为1。如果$S_{i} = a wedge T_{j} = b$,那么以$i - j$作为起点的地方就会存在这样一条边。

      然后就是套路了,翻转串$T$,原来的$j$变为了$|T| - j$,贡献的位置就是$|T| + i - j$,然后跑FFT算答案。

      剩下的就是暴力了。可以dfs,也可以dsu算连通块的个数。

      由于我很懒,不想写FFT,就成了可恶的嘴巴AC选手。

      然后来膜一下Execution Time榜rk 1同学的Hash做法。

      考虑按顺序枚举$S$的每个子串$S'$,注意到我们只关心连通块的个数。

      不幸的是我们暂时不能区分多个连通块,但是我们有办法当前的连通子图不是极大的情况。

      由于字符集很小,我们可以尝试暴力枚举一个连通块由哪些字符组成。考虑选择了一个非极大连通子图,那么存在一个点有一条边连向另一个字符。

      因此这个选定的字符集在$S'$中出现的所有位置和$T$中出现的所有位置不相同。因此通过判断出现位置的集合是否相同可以判断非极大连通子图。

      如何判断选定的字符集是否连成一个连通块?我们按照字符集包含的字符个数的顺序来枚举,然后对于找的一个极大连通子图,我们把些字符ban掉。这样就能保证了。

      判断出现位置集合是否相同可以用Hash,每做完一个询问更新一下位置集合的Hash值。

      好像还有一个神奇的dfs做法?没看懂。qwq

      口胡另一个做法,考虑最多有 $|Sigma|$ 条边,可以用 Hash 表示出每一种字符出现的所有位置,即 $h_c(s) = sum_{i = 0}^{|s| - 1} x^i [s_i = c]$。用并查集维护已经有的边形成的连通块。考虑用二分求出下一对不同的字符,每次求每个字符的 Hash 值乘上它的所在连通块的根的标号。 可以做到 $O(n|Sigma|^2 log n)$ 或者 $O(nB_{|Sigma|}  + n|Sigma|log n)$

    Code

     1 /**
     2  * Codeforces
     3  * Problem#954I
     4  * Accepted
     5  * Time: 62ms
     6  * Memory: 1200k
     7  */
     8 #include <algorithm>
     9 #include <iostream>
    10 #include <cstdlib>
    11 #include <cstring>
    12 #include <cstdio>
    13 using namespace std;
    14 typedef bool boolean;
    15 
    16 #define ull unsigned long long
    17 
    18 const int N = 125005, S = 1 << 6;
    19 const ull base = 5;
    20 
    21 int n, m;
    22 char sa[N], sb[N];
    23 ull pow7[N];
    24 ull ps[S], pt[S];
    25 int bit[S], sta[S];
    26 
    27 inline void init() {
    28     scanf("%s%s", sa, sb);
    29     n = strlen(sa), m = strlen(sb);
    30     pow7[0] = 1;
    31     for (int i = 1; i < n; i++)
    32         pow7[i] = pow7[i - 1] * base;
    33 }
    34 
    35 void update(ull *ar, int c, ull val, int sgn) {
    36     int temp = (1 << c);
    37     for (int s = temp; s < S; s = (s + 1) | temp)
    38         ar[s] += val * sgn;
    39 }
    40 
    41 boolean cmp(const int& a, const int& b) {
    42     return bit[a] < bit[b];
    43 }
    44 
    45 inline void prepare() {
    46     bit[0] = 0;
    47     for (int i = 1; i < S; i++)
    48         bit[i] = bit[i - (i & (-i))] + 1;
    49     for (int i = 0; i < S; i++)
    50         sta[i] = i;
    51     sort(sta, sta + S, cmp);
    52 }
    53 
    54 inline void solve() {
    55     for (int i = 0; i < m; i++)
    56         update(ps, sa[i] - 'a', pow7[i], 1);
    57     for (int i = 0; i < m; i++)
    58         update(pt, sb[i] - 'a', pow7[i], 1);
    59 
    60     for (int i = 0; i <= n - m; i++) {
    61         int ban = 0, res = 0;
    62         for (int j = 0; j < S; j++) {
    63             int s = sta[j];
    64             if (!(s & ban)) {
    65                 if (!ps[s] && !pt[s])
    66                     ban |= s;
    67                 else if (pt[s] * pow7[i] == ps[s]) {
    68                     res += bit[s] - 1;
    69                     ban |= s;
    70                 }
    71             }
    72         }
    73         printf("%d ", res);
    74         if (i + m < n) {
    75             update(ps, sa[i] - 'a', pow7[i], -1);
    76             update(ps, sa[i + m] - 'a', pow7[i + m], 1);
    77         }
    78     }
    79 }
    80 
    81 int main() {
    82     init();
    83     prepare();
    84     solve();
    85     return 0;
    86 }
    Problem I
  • 相关阅读:
    AngularJS中的Provider们:Service和Factory等的区别
    解决Eclipse建立Maven项目后无法建立src/main/java资源文件夹的办法
    关于EL表达式不起作用的问题
    Tomcat+Nginx 负载均衡配置,Tomcat+Nginx集群,Tomcat集群
    Java WebService 简单实例
    火狐浏览器中表单内容在表单刷新时候不重置表单信息
    ie文本框内容不居中问题
    javascript call和apply方法
    javascript的词法作用域
    C++提高编程 deque容器
  • 原文地址:https://www.cnblogs.com/yyf0309/p/9310653.html
Copyright © 2011-2022 走看看