省选测试8
A GCD和LCM
题目大意 : 求满足gcd(i,j)<a的i,j的lcm(i,j)的和
- 套个莫比乌斯反演,然后把询问离线下来,按a排个序,用树状数组维护即可
Code
Show Code
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5, M = 1e9 + 7;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
bool v[N];
int t[N], pri[N], tot, mu[N], ans[N];
struct Node {
int n, m, a, id;
}q[N];
bool operator < (const Node &x, const Node &y) {
return x.a < y.a;
}
void Add(int x, int w) {
for (; x < N; x += x & -x)
if ((t[x] += w) >= M) t[x] -= M;
}
int Ask(int x, int s = 0) {
for (; x; x -= x & -x)
if ((s += t[x]) >= M) s -= M;
return s;
}
int Sum(int n) {
return 1ll * n * (n + 1) / 2 % M;
}
int main() {
mu[1] = 1;
for (int i = 2; i < N; ++i) {
if (!v[i]) pri[++tot] = i, mu[i] = -1;
for (int j = 1; j <= tot && i * pri[j] < N; ++j) {
v[i*pri[j]] = 1;
if (i % pri[j] == 0) break;
mu[i*pri[j]] = -mu[i];
}
}
int T = read();
for (int i = 1; i <= T; ++i)
q[i] = (Node) {read(), read(), read(), i};
sort(q + 1, q + T + 1);
for (int k = 1; k <= T; ++k) {
int n = q[k].n, m = q[k].m, s = 0;
if (n > m) swap(n, m);
for (int i = q[k-1].a + 1; i <= q[k].a; ++i)
for (int j = i; j < N; j += i)
Add(j, (1ll * j * j / i * mu[j/i] % M + M) % M);
for (int l = 1, r; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
if ((s += 1ll * Sum(n / l) * Sum(m / l) % M * (Ask(r) - Ask(l-1) + M) % M) >= M) s -= M;
}
ans[q[k].id] = s;
}
for (int i = 1; i <= T; ++i)
printf("%d\n", ans[i]);
return 0;
}
B 平面图
题目大意 : 在一个平面图上删边问联通块数量,询问两点是否联通,强制在线
-
询问联通块数量的话就维护对偶图联通性,每出现一个环就代表有一部分断开,联通块会多一个
-
询问是否联通的话可以启发式分裂,每次把一个联通块分成两个的时候就给两个同时BFS,把小的块重新编号即可
Code
Show Code
#include <set>
#include <map>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
set<int> s[N];
vector<int> t[N];
map<int, int> mp[N];
set<int>::iterator it1, it2;
int n, m, tot, f[N*6], ans, c[N], cnt, q1[N], q2[N], v[N];
int Find(int x) {
return x == f[x] ? x : f[x] = Find(f[x]);
}
int main() {
n = read(); m = read();
for (int x = 1; x <= n; ++x) {
for (int k = read(); k; --k) {
int y = read();
s[x].insert(y); t[x].push_back(y);
mp[x][y] = ++tot; f[tot] = tot;
}
}
//mp[x][y]表示从x到y这条单向边右面的对偶图上点的编号
for (int x = 1; x <= n; ++x)
for (int i = 0; i < t[x].size(); ++i)
f[Find(mp[x][t[x][i]])] = Find(mp[t[x][(i+1)%t[x].size()]][x]);
cnt = 1;
while (m--) {
char od; scanf(" %c", &od);
int x = read() ^ ans, y = read() ^ ans;
if (od == '?') printf("%d\n", ans = (c[x] == c[y]));
else {
s[x].erase(y); s[y].erase(x);
int fx = Find(mp[x][y]), fy = Find(mp[y][x]);
if (fx != fy) f[fx] = fy;
else {
int l1, r1, l2, r2;
v[x] = v[y] = ++tot;
q1[l1=r1=1] = x; q2[l2=r2=1] = y;
it1 = s[x].begin(); it2 = s[y].begin();
while (l1 <= r1 && l2 <= r2) {
while (l1 <= r1) {
if (it1 == s[q1[l1]].end()) it1 = s[q1[++l1]].begin();
else if (v[*it1] == tot) ++it1;
else { v[*it1] = tot; q1[++r1] = *it1; ++it1; break; }
}
while (l2 <= r2) {
if (it2 == s[q2[l2]].end()) it2 = s[q2[++l2]].begin();
else if (v[*it2] == tot) ++it2;
else { v[*it2] = tot; q2[++r2] = *it2; ++it2; break; }
}
}
cnt++;
if (l1 > l2) for (int i = 1; i <= r1; ++i) c[q1[i]] = cnt;
else for (int i = 1; i <= r2; ++i) c[q2[i]] = cnt;
}
printf("%d\n", ans = cnt);
}
}
return 0;
}
C 路径 (Unaccepted)
题目大意 :
- 咕了
Code
Show Code