目录

noip模拟38

目录

死磕T1战术失败?

T1 a

一看到在矩阵上统计符合条件的子矩形个数就想到了前缀和,然后就不会了。剩下的3h想了从dp到线段树的各种方法就是没想到天天挂在嘴边(?)的双指针。如果是暴力 $O(n^2m^2)$ 铁定过不了,考虑如何降低复杂度。因为n最大仅为30,所以可以暴力枚举每个块的上下边界,而对于左右边界就使用双指针进行优化。找到第一个1的个数大于l的位置设为第一个指针(代码中为ll),最后一个1的个数小于r的位置设为第二个指针(代码中为rr)。等到写代码的时候会发现二维前缀和并不香,于是对其进行降维打击,使用一维前缀和,将在上边界和下边界之间的行拍成1行再求两个指针即(不)可。

TLE Code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <bits/stdc++.h>
#define For(i, l, r) for (long long i = (l), i##end = (r); i <= i##end; ++i)
#define Fordown(i, r, l) for (long long i = (r), i##end = (l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
const long long maxn = 50010;
const long long mod = 1e9 + 7;

template<typename type>
type read() {
  type s = 0, f = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    s = s * 10 + ch - '0';
    ch = getchar();
  }
  return s * f;
}

int n, m;
int mat[31][maxn];
int qz[31][maxn];
int l, r;
int ans = 0;

void WinRAR(int up, int dn) {
  int tmp[maxn] = {0};
  For(kk, 1, m) {
    For(i, up, dn) {
      tmp[kk] += qz[i][kk];
    }
  }
  int ll = 1, rr = 0;
  For(i, 0, m) {
    ll = 1, rr = 1;
    while ((tmp[ll] - tmp[i] < l || ll <= i) && ll < m) ll++;
    while (tmp[rr] - tmp[i] <= r && rr < m) rr++;
    if (ll == i)
      break;
    if (tmp[rr] - tmp[i] > r) rr--;
    if(tmp[ll] - tmp[i] >= l && tmp[rr] - tmp[i] <= r) {
      ans += (rr - ll + 1) ;
    }
  }
}
int main() {
  n = read<int>();
  m = read<int>();

  For(i, 1, n) {
    char in[maxn];
    scanf("%s", in + 1);
    For(j, 1, m) {
      mat[i][j] = in[j] - '0';
      qz[i][j] =  qz[i][j - 1] + mat[i][j];
    }
  }
  l = read<int>();
  r = read<int>();
  For(i, 1, n) {
    For(j, i, n) {
      WinRAR(i, j);
    }
  }
  cout << ans << endl;
  return 0;
}

嗯??怎么TLE了?接着优化!因为随着i向右移动,两个指针一定会单调右移,因此将初值放在外面即可。

Accepted Code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <bits/stdc++.h>
#define For(i, l, r) for (long long i = (l), i##end = (r); i <= i##end; ++i)
#define Fordown(i, r, l) for (long long i = (r), i##end = (l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
const long long maxn = 50010;
const long long mod = 1e9 + 7;

template<typename type>
type read() {
  type s = 0, f = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    s = s * 10 + ch - '0';
    ch = getchar();
  }
  return s * f;
}

long long n, m;
long long mat[31][maxn];
long long qz[31][maxn];
long long l, r;
long long ans = 0;

void WinRAR(long long up, long long dn) {
  long long tmp[maxn] = {0};
  For(kk, 1, m) {
    For(i, up, dn) {
      tmp[kk] += qz[i][kk];
    }
  }
  long long ll = 1, rr = 1;
  For(i, 0, m) {
    while ((tmp[ll] - tmp[i] < l || ll <= i) && ll < m) ll++;
    while (tmp[rr] - tmp[i] <= r && rr < m) rr++;
    if (ll == i)
      break;
    if (tmp[rr] - tmp[i] > r) rr--;
    if(tmp[ll] - tmp[i] >= l && tmp[rr] - tmp[i] <= r) {
      ans += (rr - ll + 1) ;
    }
  }
}
int main() {
  n = read<long long>();
  m = read<long long>();

  For(i, 1, n) {
    char in[maxn];
    scanf("%s", in + 1);
    For(j, 1, m) {
      mat[i][j] = in[j] - '0';
      qz[i][j] =  qz[i][j - 1] + mat[i][j];
    }
  }
  l = read<long long>();
  r = read<long long>();
  For(i, 1, n) {
    For(j, i, n) {
      WinRAR(i, j);
    }
  }
  cout << ans << endl;
  return 0;
}

T2 b

官方题解:

这里记a的最大值为x。将问题转化为“对i∈[1,1e5],求多少种选择方案使得 $gcd=i$ ”。继续转化为“对i∈[1,1e5],求多少种选择方案使得 $i|gcd$ ”。这里可以 $O(x)$ 或直接暴力 $O(x;ln;x)$ 容斥回去。等价于“对i∈[1,1e5],求多少种选择方案使得选的所有数均为 $i$ 的倍数”。 $O(n(m+x;ln;x))$ 处理出第 $i$ 行有多少个数是 $j$ 的倍数,记为 $cnt_{i,j}$ 。那么,共有 $\Pi^n_{i=1}(cnt_{i,j} + 1) − 1$ 种方案,使得选的所有数均为 $j$ 的倍数,这一步为 $O(nm)$ 。

还需要去重,去掉每个数×2及以上出现的个数,最后还要把个数乘上当前的数再加到ans里

Show you the code
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <bits/stdc++.h>
#define For(i, l, r) for (long long i = (l), i##end = (r); i <= i##end; ++i)
#define Fordown(i, r, l) for (long long i = (r), i##end = (l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
const long long maxn = 100010;
const long long mod = 1e9 + 7;

template<typename type>
type read() {
  type s = 0, f = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    s = s * 10 + ch - '0';
    ch = getchar();
  }
  return s * f;
}


long long n, m;
long long num[21][maxn];
long long cnt[21][maxn];
long long bkt[21][maxn];
long long sum[maxn];

int32_t main() {
  n = read<long long>();
  m = read<long long>();
  long long maxx = 0;
  For(i, 1, n) {
    For(j, 1, m) {
      num[i][j] = read<long long>();
      bkt[i][num[i][j]]++;
      maxx = max(maxx, num[i][j]);
    }
  }

  For(i, 1, n) {
    For(j, 1, maxn) {
      for (long long mult = 1; mult * j <= maxn; mult++) {
        cnt[i][j] += bkt[i][j * mult];
      }
    }
  }
  long long ans = 1;
  Fordown(j, maxx, 1) {
    sum[j] = 1;
    For(i, 1, n) {
      sum[j] *= (cnt[i][j] + 1);
      sum[j] %= mod;
    }
    sum[j]--;
    for (long long k = 2; k * j <= maxn; k++) {
      sum[j] = (sum[j] - sum[k * j] + mod) % mod;
    }
    ans += sum[j] * j;
    ans %= mod;
  }
  cout << ans - 1 << endl;
  return 0;
}

T3 c

点分治题不会,尝试搞懂

std:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include <bits/stdc++.h>
using namespace std;

const int maxn = 5e5 + 10;
int n, m, q, fa[maxn], ans[maxn];
vector<pair<int, int>> vec[maxn];
vector<pair<int, vector<int>>> g[maxn];

void dfs0(int pos, int fa) {
  sort(vec[pos].begin(), vec[pos].end());
  vec[pos].erase(unique(vec[pos].begin(), vec[pos].end()), vec[pos].end());
  for (int i = 0, j, v; i < vec[pos].size(); i = j) {
    v = vec[pos][i].first;
    j = i + 1;
    while (j < vec[pos].size() && vec[pos][j].first == v) ++j;
    if (v == fa) continue;
    g[pos].push_back(make_pair(v, vector<int>()));
    g[v].push_back(make_pair(pos, vector<int>()));
    for (int k = i; k < j && k < i + 3; ++k) {
      g[pos].back().second.push_back(vec[pos][k].second);
      g[v].back().second.push_back(vec[pos][k].second);
    }
    dfs0(v, pos);
  }
}// 去重

inline void chkmax(int& a, int b) {
  if (a < b) a = b;
}
namespace work {
struct query {
  int id, x, y;
};
vector<query> vec[maxn];
int size[maxn], sfa[20][maxn], sdep[maxn], dp[maxn][3][3], tmp[maxn];
bool vis[maxn];
void dfs1(int pos, int fa, int& rt, int tot) {
  int maxson = 0;
  size[pos] = 1;
  for (int i = 0, v; i < g[pos].size(); ++i)
    if (!vis[v = g[pos][i].first] && v != fa) {
      dfs1(v, pos, rt, tot);
      size[pos] += size[v];
      chkmax(maxson, size[v]);
    }
  if (maxson <= tot / 2 && tot - size[pos] <= tot / 2) rt = pos;
}
void solve0(int pos, int tot, int fa) {
  dfs1(pos, 0, pos, tot);
  vis[pos] = true;
  sfa[0][pos] = fa;
  sdep[pos] = sdep[sfa[0][pos]] + 1;
  for (int i = 1; i < 20; ++i) sfa[i][pos] = sfa[i - 1][sfa[i - 1][pos]];
  for (int i = 0, v; i < g[pos].size(); ++i)
    if (!vis[v = g[pos][i].first])
      if (size[v] <= size[pos])
        solve0(v, size[v], pos);
      else
        solve0(v, tot - size[pos], pos);
}
inline int lca(int u, int v) {
  if (sdep[u] < sdep[v]) swap(u, v);
  for (int i = 19; ~i; --i)
    if (sdep[sfa[i][u]] >= sdep[v]) u = sfa[i][u];
  if (u == v) return u;
  for (int i = 19; ~i; --i)
    if (sfa[i][u] != sfa[i][v]) u = sfa[i][u], v = sfa[i][v];
  return sfa[0][u];
}
vector<int> vfa[maxn];
void dfs2(int pos, int fa, int rt) {
  tmp[pos] = rt;
  for (int i = 0, v; i < g[pos].size(); ++i)
    if (!vis[v = g[pos][i].first] && v != fa) {
      vfa[v] = g[pos][i].second;
      for (int j = 0; j < vfa[rt].size(); ++j)
        for (int k = 0; k < vfa[v].size(); ++k) {
          dp[v][j][k] = -1e9;
          for (int l = 0; l < vfa[pos].size(); ++l)
            chkmax(dp[v][j][k], dp[pos][j][l] + (vfa[pos][l] != vfa[v][k]));
        }
      dfs2(v, pos, rt);
    }
}
void solve(int pos, int tot) {
  dfs1(pos, 0, pos, tot);
  vis[pos] = true;
  for (int i = 0, v; i < g[pos].size(); ++i)
    if (!vis[v = g[pos][i].first]) {
      vfa[v] = g[pos][i].second;
      for (int j = 0; j < vfa[v].size(); ++j)
        for (int k = 0; k < vfa[v].size(); ++k) dp[v][j][k] = j == k ? 0 : -1e9;
      dfs2(v, pos, v);
    }
  for (int i = 0; i < vec[pos].size(); ++i) {
    int x = vec[pos][i].x, y = vec[pos][i].y;
    if (y == pos) swap(x, y);
    if (x == y)
      ans[vec[pos][i].id] = 0;
    else if (x == pos) {
      for (int j = 0; j < vfa[tmp[y]].size(); ++j)
        for (int k = 0; k < vfa[y].size(); ++k)
          chkmax(ans[vec[pos][i].id], dp[y][j][k]);
      ++ans[vec[pos][i].id];
    } else {
      for (int j = 0; j < vfa[x].size(); ++j)
        for (int k = 0; k < vfa[tmp[x]].size(); ++k)
          for (int l = 0; l < vfa[tmp[y]].size(); ++l)
            for (int m = 0; m < vfa[y].size(); ++m)
              chkmax(ans[vec[pos][i].id],
                     dp[x][k][j] + dp[y][l][m] +
                         (vfa[tmp[x]][k] != vfa[tmp[y]][l]));
      ++ans[vec[pos][i].id];
    }
  }
  for (int i = 0, v; i < g[pos].size(); ++i)
    if (!vis[v = g[pos][i].first])
      if (size[v] <= size[pos])
        solve(v, size[v]);
      else
        solve(v, tot - size[pos]);
}
int main() {
  solve0(1, n, 0);
  scanf("%d", &q);
  for (int i = 1, u, v; i <= q; ++i) {
    scanf("%d%d", &u, &v);
    vec[lca(u, v)].push_back((query){i, u, v});
  }
  memset(vis, false, sizeof(vis));
  solve(1, n);
  for (int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
}
}  // namespace work

int main() {
  freopen("c.in", "r", stdin);
  freopen("c.out", "w", stdout);
  scanf("%d%d", &n, &m);
  for (int i = 1, u, v, w; i <= m; ++i) {
    scanf("%d%d%d", &u, &v, &w);
    vec[u].push_back(make_pair(v, w));
    vec[v].push_back(make_pair(u, w));
  }
  dfs0(1, 0);
  work::main();
  return 0;
}