开局G题不顺,然后卡D题,F水题没去切导致打崩了…其实好多可写题,只是这些可写题我不大擅长…
A. Lightning Routing I
待补
题目大意:给定一个边权树,要求支持动态修改边权,询问到某个点最远的点的距离
update:我去,怎么又是一道能写但是没读题的题,我如果卡题我们队就凉了我去…维护树的直径我在杭电多校9写过一个类似的:2019 Multi-University Training Contest 9 1007 Rikka with Travels,首先两颗子树合并,其新的树直径两端点必定在原来两颗子树直径的四个端点之中,知道了这个就很好想了,带修改我们只要一个树剖就可以解决树上任意两点距离了,然后用一个线段树维护树上dfs序的直径,就完事了,思维上也没啥难度
#include<bits/stdc++.h>
#define pi pair<int, int>
#define mk make_pair
#define ll long long
#define ls o * 2
#define rs o * 2 + 1
#define mid (l + r) / 2
using namespace std;
const int maxn = 1e5 + 10;
vector<pi> G[maxn];
pi W[maxn];
int dep[maxn], son[maxn], f[maxn], sz[maxn], cnt;
int id[maxn], top[maxn], n, val[maxn];
ll sum[maxn * 4];
void up(int o, int l, int r, int k, int v) {
if (l == r) {
sum[o] = v;
return;
}
if (k <= mid)
up(ls, l, mid, k, v);
else
up(rs, mid + 1, r, k, v);
sum[o] = sum[ls] + sum[rs];
}
ll qu(int o, int l, int r, int ql, int qr) {
if (ql > qr)
return 0;
if (l >= ql && r <= qr)
return sum[o];
ll res = 0;
if (ql <= mid)
res += qu(ls, l, mid, ql, qr);
if (qr > mid)
res += qu(rs, mid + 1, r, ql, qr);
return res;
}
void dfs(int u, int fa, int deep, int v)
{
val[u] = v;
f[u] = fa;
dep[u] = deep;
sz[u] = 1;
son[u] = 0;
for(auto tmp : G[u]) {
int v = tmp.first;
if(v == fa)
continue;
dfs(v, u, deep + 1, tmp.second);
sz[u] += sz[v];
if(sz[son[u]] < sz[v])
son[u] = v;
}
}
void dfs1(int u, int rt)
{
id[u] = ++cnt;
top[u] = rt;
up(1, 1, n, cnt, val[u]);
if(son[u])
dfs1(son[u], rt);
for(auto tmp : G[u]) {
int v = tmp.first;
if(v == f[u] || v == son[u])
continue;
dfs1(v, v);
}
}
ll dist(int x, int y) {
ll res = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]])
swap(x, y);
res += qu(1, 1, n, id[top[x]], id[x]);
x = f[top[x]];
}
if (id[x] > id[y])
swap(x, y);
return res + qu(1, 1, n, id[x] + 1, id[y]);
}
struct node {
int x, y;
ll len;
node operator+(const node& t) const {
node tmp = t;
if (len > t.len)
tmp = node{x, y, len};
ll dist1 = dist(x, t.x);
ll dist2 = dist(x, t.y);
ll dist3 = dist(y, t.x);
ll dist4 = dist(y, t.y);
if (dist1 > tmp.len)
tmp = node{x, t.x, dist1};
if (dist2 > tmp.len)
tmp = node{x, t.y, dist2};
if (dist3 > tmp.len)
tmp = node{y, t.x, dist3};
if (dist4 > tmp.len)
tmp = node{y, t.y, dist4};
return tmp;
}
} tree[maxn * 4];
int w[maxn], L[maxn], R[maxn], dfn, rk[maxn];
void dfs2(int u, int fa) {
L[u] = ++dfn;
rk[dfn] = u;
for (auto tmp : G[u])
if (tmp.first != fa)
dfs2(tmp.first, u);
R[u] = dfn;
}
void build(int o, int l, int r) {
if (l == r) {
tree[o] = node{rk[l], rk[l], -1};
return;
}
build(ls, l, mid);
build(rs, mid + 1, r);
tree[o] = tree[ls] + tree[rs];
}
void update(int o, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr)
return;
if (ql <= mid)
update(ls, l, mid, ql, qr);
if (qr > mid)
update(rs, mid + 1, r, ql, qr);
tree[o] = tree[ls] + tree[rs];
}
int main() {
int u, v, q, x, val;
char c;
scanf("%d", &n);
for (int i = 1; i < n; i++) {
scanf("%d%d%d", &u, &v, &w[i]);
G[u].push_back(mk(v, w[i]));
G[v].push_back(mk(u, w[i]));
W[i] = mk(u, v);
}
dfs(1, 0, 0, 0);
dfs1(1, 1);
dfs2(1, 0);
build(1, 1, n);
scanf("%d", &q);
while (q--) {
getchar();
scanf("%c", &c);
if (c == 'Q') {
scanf("%d", &u);
printf("%lld\n", max(dist(u, tree[1].x), dist(u, tree[1].y)));
}
else {
scanf("%d%d", &x, &val);
u = W[x].first;
v = W[x].second;
if (f[u] != v)
swap(u, v);
up(1, 1, n, id[u], val);
update(1, 1, n, L[u], R[u]);
}
}
}
题意:从三个数组中各选一个数,要求三个数当中最大的数要小于等于另外两个数的和,问有多少种选法。
解法:和 hdu 4609 3-idiots解法类似,枚举每个数作为最大数mx的贡献,用fft求出另外两个数组的多项式乘积,前缀和预处理一下,然后sum[n] - sum[mx - 1]求出两数之和大于最大数mx的数量,然后简单去去重就可以了,不难,不过有点注意的是当 n 比较小的时候暴力更优
#include<bits/stdc++.h>
#define dob complex<double>
const int maxn = 4e5 + 10, N = 2e5;
using namespace std;
const double pi = acos(-1.0);
typedef long long ll;
ll num[maxn];
ll work(int *a, int *b, int *c, int n) {
ll ans = 0;
for (int i = 1; i <= n; i++) {
ans += num[N] - num[c[i] - 1];
int t1 = lower_bound(a + 1, a + 1 + n, c[i]) - a;
int t2 = lower_bound(b + 1, b + 1 + n, c[i]) - b;
t1 = n - t1 + 1;
t2 = n - t2 + 1;
ans += 1ll * t1 * t2;
ans -= 1ll * (t1 + t2) * n;
}
return ans;
}
int r[maxn];
ll cnt1[maxn], cnt2[maxn], cnt3[maxn];
dob arr1[maxn], arr2[maxn], arr3[maxn], arr[maxn];
void fft(dob *A, int opt, int n) {
for (int i = 0; i < n; i++)
if (i < r[i])
swap(A[i], A[r[i]]);
for (int i = 1; i < n; i <<= 1) {
dob wn(cos(pi / i), sin(opt * pi / i)), x, y;
for (int j = 0; j < n; j += (i << 1)) {
dob w(1, 0);
for (int k = 0; k < i; k++, w *= wn) {
x = A[j + k], y = w * A[i + j + k];
A[j + k] = x + y;
A[i + j + k] = x - y;
}
}
}
}
ll cat(dob *A, dob *B, int *a, int *b, int *c, int n, int m) {
for (int i = 0; i < n; i++)
arr[i] = A[i] * B[i];
fft(arr, -1, n);
for (int i = 0; i < n; i++)
num[i] = ll(arr[i].real() / n + 0.5);
for (int i = 1; i <= N; i++)
num[i] += num[i - 1];
return work(a, b, c, m);
}
int a[maxn], b[maxn], c[maxn];
ll fuck(int n) {
int m = n;
ll ans = 0;
int mx = 2e5 + 1, bit = 0;
memset(cnt1, 0, sizeof(cnt1));
memset(cnt2, 0, sizeof(cnt2));
memset(cnt3, 0, sizeof(cnt3));
for (int i = 1; i <= n; i++) {
cnt1[a[i]]++;
cnt2[b[i]]++;
cnt3[c[i]]++;
}
for (n = 1; n <= mx; n <<= 1)
bit++;
for (int i = 0; i < n; i++)
r[i] = (r[i >> 1] >> 1) | (i & 1) << (bit - 1);
for (int i = 0; i < n; i++) {
arr1[i] = cnt1[i];
arr2[i] = cnt2[i];
arr3[i] = cnt3[i];
}
fft(arr1, 1, n);
fft(arr2, 1, n);
fft(arr3, 1, n);
ans += cat(arr1, arr2, a, b, c, n, m);
ans += cat(arr2, arr3, b, c, a, n, m);
ans += cat(arr3, arr1, c, a, b, n, m);
return ans;
}
ll calc(int *a, int *b, int *c, int n) {
memset(num, 0, sizeof(num));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
num[a[i] + b[j]]++;
for (int i = 1; i <= N; i++)
num[i] += num[i - 1];
return work(a, b, c, n);
}
ll gao(int n) {
ll ans = 0;
ans += calc(a, b, c, n);
ans += calc(c ,a, b, n);
ans += calc(b, c, a, n);
return ans;
}
ll gank(int n) {
ll ans = 0;
for (int i = 1; i <= n; i++) {
int t1 = lower_bound(b + 1, b + 1 + n, a[i]) - b;
int t2 = lower_bound(c + 1, c + 1 + n, a[i]) - c;
int t3 = lower_bound(b + 1, b + 1 + n, a[i] + 1) - b;
int t4 = lower_bound(c + 1, c + 1 + n, a[i] + 1) - c;
if (b[t1] == a[i] && c[t2] == a[i])
ans += 1ll * (t3 - t1) * (t2 - 1) + 1ll * (t4 - t2) * (t1 - 1) + 1ll * (t3 - t1) * (t4 - t2);
else if (b[t1] == a[i])
ans += 1ll * (t3 - t1) * (t2 - 1);
else if (c[t2] == a[i])
ans += 1ll * (t4 - t2) * (t1 - 1);
int k1 = lower_bound(c + 1, c + 1 + n, b[i]) - c;
int k2 = lower_bound(a + 1, a + 1 + n, b[i]) - a;
int k3 = lower_bound(c + 1, c + 1 + n, b[i] + 1) - c;
if (c[k1] == b[i])
ans += 1ll * (k3 - k1) * (k2 - 1);
}
return ans;
}
int main() {
int T, Case = 0;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &b[i]);
for (int i = 1; i <= n; i++)
scanf("%d", &c[i]);
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + n);
sort(c + 1, c + 1 + n);
a[n + 1] = b[n + 1] = c[n + 1] = 0;
printf("Case #%d: ", ++Case);
if (n <= 1000)
printf("%lld\n", gao(n) + gank(n));
else
printf("%lld\n", fuck(n) + gank(n));
}
}
题意:统计有多少个序列满足和等于积
解法:刚开始想dp写这题,想了巨久想不出来有效的dp,后来才想着暴力试一试,然后发现跑的贼快,既然是暴力题,那就没什么好说的了…
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 6001, mod = 1e9 + 7;
int ans[maxn], p[maxn], inv[maxn];
void add(int &x, int y) {
x += y;
if (x >= mod)
x -= mod;
if (x < 0)
x += mod;
}
ll ksm(ll x, int y) {
ll res = 1;
while (y) {
if (y & 1)
res = res * x % mod;
x = x * x % mod;
y /= 2;
}
return res;
}
void dfs(int x, int n, int val, int num, int len, int mu, int sum) {
if (val > 2 * n)
return;
if (val > 1 && val > sum) {
int m = val - sum + len;
if (m > 3000)
return;
int tmp = 1ll * p[m] * mu % mod * inv[num] % mod * inv[val - sum] % mod;
add(ans[m], tmp);
}
dfs(x, n, val * x, num + 1, len + 1, mu, sum + x);
for (int i = x + 1; i <= n; i++)
dfs(i, n, val * i, 1, len + 1, 1ll * mu * inv[num] % mod, sum + i);
}
int main() {
p[0] = inv[0] = 1;
for (int i = 1; i <= 6000; i++) {
p[i] = 1ll * p[i - 1] * i % mod;
inv[i] = ksm(p[i], mod - 2);
}
dfs(2, 3000, 1, 0, 0, 1, 0);
int T;
ans[2] = 1;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d", &n);
printf("%d\n", ans[n]);
}
}
题意:问有多少个长度为n的序列,每个数的范围是1~m, 偶数在序列中出现次数必须是偶数
解法:生成函数裸题,我们求出偶数数量 ,构造生成函数:
简单变形:
最终化简:
很明显:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 2e5 + 10, mod = 1e9 + 7;
ll p[maxn], inv[maxn];
ll ksm(ll x, ll y) {
ll res = 1;
while (y) {
if (y & 1)
res = res * x % mod;
x = x * x % mod;
y /= 2;
}
return res;
}
void init(int n) {
p[0] = inv[0] = 1;
for (int i = 1; i <= n; i++)
p[i] = p[i - 1] * i % mod, inv[i] = ksm(p[i], mod - 2);
}
void add(ll &x, ll y) {
x += y;
if (x >= mod)
x -= mod;
if (x < 0)
x += mod;
}
ll C(int n, int m) {
return p[n] * inv[m] % mod * inv[n - m] % mod;
}
int main() {
init(2e5);
int T;
cin>>T;
while (T--) {
ll n, m, ans = 0;
cin>>n>>m;
int t = m / 2;
for (int i = 0; i <= t; i++) {
ll tmp = C(t, i) * ksm(m - 2 * i, n) % mod;
add(ans, tmp);
}
ans = ans * ksm(ksm(2, t), mod - 2) % mod;
cout<<ans<<'\n';
}
}
题意:构造一个长度为n合法字符串,第pos(pos < n)位的字符范围是A到A + pos - 1,最后一个字符范围是A到max(已经构造了的字符)+1,要求输出字典序第k小的合法字符串
解法:设dp[i][j]为已经构造了的最大字符为A+ i,有dp[i][j]个合法方案数构造剩下的 j 个位置的字符,下一位我可以填A到A + i:dp[i][j] += (i + 1)*dp[i][j -1],下一位我也可以填A + i +1:dp[i][j] += dp[i +1][j - 1],接下来我们从第一位开始从小到大构造,加入我要填A,那么我们用dp计算一下填A之后还剩多少合法方案数能填完,如果方案数x大于等于k,那么说明可以填A,否则说明不能填A,那么k-=x,然后假设取填B,以此类推
#include<bits/stdc++.h>
#define ll __int128
//#define ll long long
using namespace std;
ll d[27][27], k;
char s[30];
ll dfs(int i, int j) {
if (d[i][j])
return d[i][j];
if (j == 1)
return i + 2;
d[i][j] = dfs(i, j - 1) * (i + 1) + dfs(i + 1, j - 1);
return d[i][j];
}
int main() {
int T, Case = 0;
scanf("%d", &T);
while (T--) {
int n;
scanf("%d%s", &n, s + 1);
k = 0;
for (int i = 1; s[i]; i++)
k = k * 10 + s[i] - '0';
printf("Case #%d: ", ++Case);
int mx = 0;
for (int i = 1; i <= n; i++) {
if (i != n) {
for (int j = 0; j < i; j++)
if (j <= mx) {
if (k > dfs(mx, n - i))
k -= dfs(mx, n - i);
else {
printf("%c", 'A' + j);
break;
}
}
else {
if (k > dfs(j, n - i))
k -= dfs(j, n - i);
else {
printf("%c", 'A' + j);
mx = j;
break;
}
}
}
else
printf("%c", k - 1 + 'A');
}
puts("");
}
}
题意: 题意:给了一个母串S, 每次循问给了一个模板串,问模板串在母 串中“匹配”了多少次?“匹配”的意思就是首字母和尾字母一样, 中间字母顺序可以换
解法:首先我们把字符串每个字符的数量去hash一下,就可以做到O(1)匹配,然后我们知道询问的串的不同长度种类数最多只有根号n,证明: ,那我们离线存好每个询问串hash值,我们枚举长度len,然后取出母串对应长度的所有子串hash值并排序,然后处理所有长度等于len的询问串,通过二分去查询每个询问串hash值在母串出现次数即可,复杂度nsqrt(n)logn
#include<bits/stdc++.h>
#define ull unsigned long long
#define pi pair<ull, int>
#define mk make_pair
using namespace std;
const int maxn = 1e5 + 5, base = 1e9 + 7;
ull p[400], h[maxn];
char s[maxn];
int L[maxn], N, m, a[maxn], ans[maxn];
vector<pi> G[maxn];
void gao() {
scanf("%s", s + 1);
N = strlen(s + 1);
for (int i = 1; i <= N; i++)
a[i] = s[i] - 'a';
}
void gaogao() {
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
scanf("%s", s + 1);
int n = strlen(s + 1);
L[i] = n;
ull val = 0;
for (int i = 2; i < n; i++)
val += p[s[i] - 'a'];
val += p[300] * (s[1] - 'a' + 1) + p[301] * (s[n] - 'a' + 1);
G[n].push_back(mk(val, i));
}
}
int main() {
p[0] = 1;
for (int i = 1; i < 400; i++)
p[i] = p[i - 1] * base;
int T;
scanf("%d", &T);
while (T--) {
gao();
gaogao();
sort(L + 1, L + 1 + m);
int sz = unique(L + 1, L + 1 + m) - L - 1;
for (int i = 1; i <= sz; i++) {
int n = L[i], cnt = 0;
ull val = 0;
for (int i = 1; i <= N; i++) {
if (i >= n) {
val -= p[a[i - n + 1]];
ull tmp = val + p[300] * (a[i - n + 1] + 1) + p[301] * (a[i] + 1);
h[++cnt] = tmp;
}
val += p[a[i]];
}
sort(h + 1, h + 1 + cnt);
for (auto tmp : G[n]) {
int t2 = upper_bound(h + 1, h + 1 + cnt, tmp.first) - h;
int t1 = lower_bound(h + 1, h + 1 + cnt, tmp.first) - h;
ans[tmp.second] = t2 - t1;
}
G[n].clear();
}
for (int i = 1; i <= m; i++)
printf("%d\n", ans[i]);
}
}