2023武汉联训衡水版
2023省选武汉联测9
A. 背包问题模板
首先二进制分组,注意剩余部分也拆分成 \(\sum 2^i\) 的形式
然后搞一个所谓二进制数位背包 \(DP\)
可以看这个题 https://www.luogu.com.cn/problem/P3188
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef __int128_t LLL;
typedef pair<int, LLL> piL;
ll read(){
ll x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
ll n, m;
LLL f[64][805], g[64][805];
vector<piL>v[64];
int p[105], top;
void print(LLL ans){
while(ans)p[++top] = ans % 10, ans /= 10;
if(!top)p[++top] = 0;
for(int i = top; i >= 1; --i)putchar('0' + p[i]); putchar('\n');
}
int main(){
freopen("bag.in","r",stdin);
freopen("bag.out","w",stdout);
n = read();
for(int i = 1; i <= n; ++i){
ll a = read(), b = read(), c = read();
for(int j = 0; ; ++j){
ll val = (1ll << j);
if(val > a)break;
v[j].push_back(piL(b, (LLL)val * c));
a -= val;
}
for(int j = 0; a; ++j)if((a >> j) & 1){
a ^= (1ll << j);
v[j].push_back(piL(b, (LLL)(1ll << j) * c));
}
}
m = read();
for(int i = 0; i <= __lg(m); ++i){
for(piL now : v[i]){
for(int j = 800; j >= now.first; --j)
g[i][j] = max(g[i][j], g[i][j - now.first] + now.second);
}
}
for(int i = 1; i <= 800; ++i)f[0][i] = g[0][i];
for(int i = 1; i <= __lg(m); ++i){
int s = (m >> (i - 1)) & 1;
for(int j = 0; j <= 800; ++j)
for(int k = 0; k <= j; ++k)
f[i][j] = max(f[i][j], f[i - 1][((j - k) << 1) + s] + g[i][k]);
}
print(f[__lg(m)][1]);
return 0;
}
B. 南河泄露
设 \(f_{l, r, 1 / 0}\) 表示在区间 \([l, r]\) 在左/右端点的贡献
枚举选择点 \(k\)
$ f_{l, r, 1 / 0} $ 在 \(l / r\) 一端固定时随着另一端单调
于是可以双指针得到 \(k\)
现在的问题是如何取 \(max\)
一侧可以直接取,另一侧可以斜率优化
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
ll read(){
ll x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 3005;
int n, s[maxn];
ll d[maxn], f[maxn][maxn][2], rmx[maxn];
ll sq(ll x){return x * x;}
int pr[maxn];
struct tb{
int x[maxn], top; ll y[maxn];
void push_l(int nx, ll ny){
while(top > 1 && (y[top] - ny) * (x[top - 1] - x[top]) <= (y[top - 1] - y[top]) * (x[top] - nx))--top;
x[++top] = nx; y[top] = ny;
}
void push_r(int nx, ll ny){
while(top > 1 && (y[top] - y[top - 1]) * (nx - x[top]) <= (x[top] - x[top - 1]) * (ny - y[top]))--top;
x[++top] = nx; y[top] = ny;
}
ll query(int k){
if(top == 0)return -0x3f3f3f3f3f3f3f3f;
while(top > 1 && y[top] - 1ll * k * x[top] < y[top - 1] - 1ll * k * x[top - 1])--top;
return y[top] - 1ll * x[top] * k;
}
void clear(){top = 0;}
}tmp, R[maxn];
int main(){
freopen("leak.in","r",stdin);
freopen("leak.out","w",stdout);
n = read();
for(int i = 1; i <= n; ++i)s[i] = read();
for(int i = 1; i <= n; ++i)d[i] = read();
for(int l = n + 1; l >= 1; --l){
pr[l] = l - 1; tmp.clear();
int p = l; ll mx = 0;
for(int r = l; r <= n + 1; ++r){
while(p <= r - 1 && f[l][p][1] < f[p + 1][r][0]){
mx = max(mx, f[l][p][1] + sq(s[p] - s[l - 1]) + d[p]);
tmp.push_r(2 * s[p], f[l][p][1] + sq(s[p]) + d[p]);
++p;
}
while(pr[r] >= p){
rmx[r] = max(rmx[r], f[pr[r] + 1][r][0] + 1ll * sq(s[r] - s[pr[r]]) + d[pr[r]]);
R[r].push_l(2 * s[pr[r]], f[pr[r] + 1][r][0] + sq(s[pr[r]]) + d[pr[r]]);
--pr[r];
}
f[l][r][0] = max(mx, R[r].query(s[l - 1]) + sq(s[l - 1]));
f[l][r][1] = max(rmx[r], tmp.query(s[r]) + sq(s[r]));
}
}
printf("%lld\n",f[1][n + 1][0]);
return 0;
}
C. 最短路问题模板
虽然但是,直接用类似倍增的方法优化建图就能过,常数更小的是树剖+线段树优化
正解咕了
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> pli;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 5e5 + 55, mx = 18;
struct edge{int to, net, val;}e[maxn * 30];
int n, m, head[maxn * 30], tot;
priority_queue<pli, vector<pli>, greater<pli>>q;
ll dis[maxn * 30]; bool vis[maxn * 30];
vector<int>g[maxn];
int cnt, id[maxn][mx], fa[maxn][mx], sub[maxn], dep[maxn];
void add(int u, int v, int w){
if(!u || !v)return;
e[++tot] = {v, head[u], w};
head[u] = tot;
}
void dfs(int x){
for(int i = 1; i < mx; ++i)fa[x][i] = fa[fa[x][i - 1]][i - 1];
for(int i = 1; i < mx; ++i)id[x][i] = ++cnt, add(id[x][i], id[x][i - 1], 0), add(id[x][i], id[fa[x][i - 1]][i - 1], 0);
sub[x] = ++cnt; add(sub[x], x, 0);
for(int v : g[x])if(v != fa[x][0]){
fa[v][0] = x; id[v][0] = x; dep[v] = dep[x] + 1;
dfs(v); add(sub[x], sub[v], 0);
}
}
void link(int x){
int u = read(), v = read(), w = read();
add(x, u, w); add(x, v, w);
if(dep[u] < dep[v])swap(u, v);
for(int i = mx - 1; i >= 0; --i)if(dep[u] - dep[v] >= (1 << i))add(x, id[u][i], w), u = fa[u][i];
if(u == v)return;
for(int i = mx - 1; i >= 0; --i)if(fa[u][i] != fa[v][i])add(x, id[u][i], w), add(x, id[v][i], w), u = fa[u][i], v = fa[v][i];
u = fa[u][0]; add(x, u, w); return;
}
int main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
cnt = n = read(), m = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read(), w = read();
g[u].push_back(v); g[v].push_back(u);
add(u, v, w); add(v, u, w);
}
dfs(1);
for(int i = 1; i <= m; ++i){
int op = read(), x = read();
if(op & 1)link(x);
else{int y = read(); add(x, sub[y], read());}
}
memset(dis, 0x3f, sizeof(dis)); dis[1] = 0; q.push(pli(0, 1));
while(!q.empty()){
int x = q.top().second; q.pop();
if(vis[x])continue; vis[x] = true;
for(int i = head[x]; i; i = e[i].net){
int v = e[i].to;
if(dis[v] > dis[x] + e[i].val){
dis[v] = dis[x] + e[i].val;
q.push(pli(dis[v], v));
}
}
}
for(int i = 1; i <= n; ++i)printf("%lld\n",dis[i]);
return 0;
}
2023省选武汉联测10
A. 矩阵
随机一个 \(1 \times n\) 的向量 \(v\)
判断 $v \times A \times B $ 是否等于 $ v \times C$
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int mod = 998244353, maxn = 3005;
mt19937 rd((ull)&maxn);
int sint(int l, int r){return uniform_int_distribution<>(l, r)(rd);}
int n, a[maxn][maxn], b[maxn][maxn], c[maxn][maxn], v[maxn], v1[maxn], v2[maxn];
bool solve(){
n = read(); for(int i = 1; i <= n; ++i)v[i] = sint(0, mod - 1), v1[i] = v2[i] = 0;
for(int i = 1; i <= n; ++i)for(int j = 1; j <= n; ++j)a[i][j] = read();
for(int i = 1; i <= n; ++i)for(int j = 1; j <= n; ++j)b[i][j] = read();
for(int i = 1; i <= n; ++i)for(int j = 1; j <= n; ++j)c[i][j] = read();
for(int k = 1; k <= n; ++k)for(int j = 1; j <= n; ++j)v1[j] = (v1[j] + 1ll * v[k] * a[k][j]) % mod;
for(int k = 1; k <= n; ++k)for(int j = 1; j <= n; ++j)v2[j] = (v2[j] + 1ll * v[k] * c[k][j]) % mod;
for(int j = 1; j <= n; ++j)v[j] = v1[j], v1[j] = 0;
for(int k = 1; k <= n; ++k)for(int j = 1; j <= n; ++j)v1[j] = (v1[j] + 1ll * v[k] * b[k][j]) % mod;
for(int i = 1; i <= n; ++i)if(v1[i] != v2[i])return false; return true;
}
int main(){
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
int t = read(); for(int i = 1; i <= t; ++i)if(solve())printf("Yes\n"); else printf("No\n");
return 0;
}
B. 错排
设 \(f_{n, m}\) 表示 \(n\) 个数, \(m\) 个没有限制的错排
\(m = 0\) 时为普通错排
枚举至少几个位置不满足限制,简单容斥得到
因为
代入上面,变形整理一下,并用 \(m + 1\) 代替 \(m\)
得到
也可以由组合意义直接得到,考虑 \(p_m = m\) 则为 \(f_{n - 1, m - 1}\) 否则为 \(f_{n, m - 1}\)
把 \(f_{n, m}\) 看成多项式
设定块长 \(B\), 预处理 \(F_{kB}\), 每次用组合数和 \(F_{m / B}\)计算 \(f_{n, m}\) 即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int mod = 998244353, maxn = 2e5 + 55, mx = 2e5, mxb = 50, B = 4000, deg = 262144;
int qpow(int x, int y){int ans = 1; for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod; return ans;}
int wn[maxn * 2], rev[maxn * 2];
void init(){
wn[0] = 1; wn[1] = qpow(3, (mod - 1) / deg);
for(int i = 1; i < deg; ++i){
wn[i] = 1ll * wn[i - 1] * wn[1] % mod;
rev[i] = rev[i >> 1] >> 1;
if(i & 1)rev[i] |= (deg >> 1);
}
}
struct poly{
int f[maxn * 2];
int &operator [] (const int &i){return f[i];}
void ntt(){
for(int i = 1; i < deg; ++i)if(i < rev[i])swap(f[i], f[rev[i]]);
for(int l = 2, hl = 1; l <= deg; l <<= 1, hl <<= 1)
for(int i = 0; i < deg; i += l)
for(int j = i, x, y; j < i + hl; ++j){
x = f[j], y = 1ll * wn[deg / l * (j - i)] * f[j + hl] % mod;
f[j] = (x + y) % mod; f[j + hl] = (x - y + mod) % mod;
}
}
void intt(){
ntt(); reverse(f + 1, f + deg); int Inv = qpow(deg, mod - 2);
for(int i = 0; i < deg; ++i)f[i] = 1ll * f[i] * Inv % mod;
}
}f[mxb + 5], fb;
int fac[maxn], ifac[maxn], c[B + 5][B + 5];
int calc(int n, int m){
int res = 0, b = m / B, r = m % B;
for(int i = 0; i <= min(r, n); ++i)res = (res + 1ll * c[r][i] * f[b][n - i]) % mod;
return res;
}
int sol(){
int n = read(), m = read();
if(m + m > n)return 0;
if(m == 0 || m == 1)return f[0][n];
return 1ll * calc(n - m, m) * fac[n - m] % mod * ifac[n - m - m] % mod;
}
void prework(){
fac[0] = ifac[0] = 1; for(int i = 1; i <= mx; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[mx] = qpow(fac[mx], mod - 2); for(int i = mx - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
f[0][0] = f[0][2] = 1; for(int i = 3; i <= mx; ++i)f[0][i] = 1ll * (i - 1) * (f[0][i - 1] + f[0][i - 2]) % mod;
for(int i = 0; i <= B; ++i){
c[i][0] = 1;
for(int j = 1; j <= i; ++j)c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % mod;
}
for(int i = 0; i <= B; ++i)fb[i] = c[B][i];
init(); fb.ntt(); f[0].ntt();
for(int i = 1; i <= mxb; ++i)
for(int j = 0; j < deg; ++j)f[i][j] = 1ll * f[i - 1][j] * fb[j] % mod;
for(int i = 0; i <= mxb; ++i)f[i].intt();
}
int main(){
// freopen("qwq.in","r",stdin);
// freopen("qwq.out","w",stdout);
prework(); int t = read(); for(int i = 1; i <= t; ++i)printf("%d\n",sol());
return 0;
}
C. 异或图
先考虑 \(m = 0\) 怎么做,如果一个数在某一位可以填 \(1\) 但是填了 \(0\) 后面可以任意填,那么让其他数任意填,该数有唯一方案使得异或为 \(c\), 那么可以直接在这里计算贡献
那么枚举满足上面条件的最高位,可以在 \(O(nlogc)\) 内得到答案
现在考虑边的限制,钦定一个连通块权值相等,那么他对答案的贡献根据连通块大小的奇偶性可以等效成 \(0 / min(a)\) 直接暴力可以做到 \(2^mnlogw\)
考虑正解,枚举下一个拼上的连通块,容斥系数为 \((-1)^{|边集|}\), 这个边集要求满足使得连通块联通
用类似修建游乐园的方法可以 \(3^n\) 容斥出系数
然后 \(f_{i, j}\) 表示当前连通块集合 \(i\), 有效点(连通块大小为奇数中 \(a\) 最小的点)集合为 \(j\) 的容斥系数之和
发现可以三进制装压,转移枚举下一个拼上的连通块即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
ll read(){
ll x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int mod = 998244353, maxn = 20;
int qpow(int x, int y){
int ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
int n, m, ans;
ll c, a[maxn];
int p3[maxn], pb[maxn][65], *b[maxn], e[maxn];
int calc(int s){
ll val = 0;
for(int i = 0; i < n; ++i)if((s >> i) & 1)val ^= a[i];
int res = (val == c);
for(int d = 0; d <= 59; ++d){
int f0 = 1, f1 = 0, tp = 1; ll o = 0;
for(int i = 0; i < n; ++i)if((s >> i) & 1){
int t0 = f0, t1 = f1;
f0 = (1ll * t0 * b[i][d - 1] + 1ll * t1 * ((a[i] & (1ll << d)) % mod)) % mod;
f1 = (1ll * t1 * b[i][d - 1] + 1ll * t0 * ((a[i] & (1ll << d)) % mod)) % mod;
tp = (1ll * tp * b[i][d - 1]) % mod; o ^= (a[i] >> d);
}
f0 = (f0 - tp + mod) % mod;
if((o ^ (c >> d)) == 0)res = (res + 1ll * f0 * qpow((1ll << d) % mod, mod - 2)) % mod;
if((o ^ (c >> d)) == 1)res = (res + 1ll * f1 * qpow((1ll << d) % mod, mod - 2)) % mod;
}
return res;
}
int mis[1 << 15], f[1 << 15], g[1 << 15];
int mp[14348908], h[14348908];
int main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
n = read(), m = read(), c = read();
for(int i = 0; i < n; ++i)a[i] = read();
p3[0] = 1; for(int i = 1; i <= n; ++i)p3[i] = 3 * p3[i - 1];
for(int i = 1; i <= m; ++i){
int u = read() - 1, v = read() - 1;
e[u] |= (1 << v); e[v] |= (1 << u);
}
for(int i = 0; i < n; ++i){
b[i] = pb[i] + 1; b[i][-1] = 1;
for(int j = 0; j <= 59; ++j)b[i][j] = (b[i][j - 1] + (a[i] & (1ll << j))) % mod;
}
if(m == 0){printf("%d\n",calc((1 << n) - 1)); return 0;}
for(int s = 1; s < (1 << n); ++s){
int val = 0;
for(int j = 0; j < n; ++j)if((s >> j) & 1)val |= (e[j] & s), mis[s] += p3[j];
f[s] = g[s] = (!val); int ss = s ^ (s & -s);
for(int t = ss; ;t = (t - 1) & ss){
g[s] = (g[s] - 1ll * g[(s & -s) | t] * f[ss ^ t] + mod) % mod;
if(!t)break;
}
}
a[n] = LLONG_MAX;
for(int s = 1; s < (1 << n); ++s){
int id = n; for(int j = 0; j < n; ++j)if(((s >> j) & 1) && a[j] < a[id])id = j;
if(__builtin_parity(s))mis[s] += p3[id];
else g[s] = 1ll * g[s] * ((a[id] + 1) % mod) % mod;
}
for(int s = 0; s < p3[n]; ++s){
if(s * 3 < p3[n])mp[s * 3] = mp[s] << 1;
if(s * 3 + 1 < p3[n])mp[s * 3 + 1] = mp[s] << 1 | 1;
if(s * 3 + 2 < p3[n])mp[s * 3 + 2] = mp[s] << 1 | 1;
}
h[0] = 1;
for(int s = 0; s < p3[n]; ++s){
int S = mp[s];
S = ((1 << n) - 1) ^ S;
int low = S & -S, SS = S ^ low;
for(int T = SS; ; T = (T - 1) & SS){
h[s + mis[T ^ low]] = (h[s + mis[T ^ low]] + 1ll * h[s] * g[T ^ low]) % mod;
if(!T)break;
}
}
int all = 0;
for(int i = 0; i < n; ++i)all += p3[i];
for(int s = 0; s < (1 << n); ++s){
int t = all;
for(int i = 0; i < n; ++i)if((s >> i) & 1)t += p3[i];
ans = (ans + 1ll * calc(s) * h[t]) % mod;
}
printf("%d\n",ans);
return 0;
}
2023省选武汉联测11
A. 游戏
发现 \(u, v, w\) 中间会存在一个点,一个点走向另外两个点的路径在这里分叉
每个点到该中心点的距离好求
找到从每个点出发的向不同子树走的最长的三条链
然后三维偏序找到任意一个满足三个距离的点即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 2e5 + 55;
int n, q, fa[maxn][29], dep[maxn];
vector<int>g[maxn], f[maxn], all;
int LCA(int u, int v){
if(dep[u] < dep[v])swap(u, v);
for(int i = 18; i >= 0; --i)if(dep[u] - dep[v] >= (1 << i))u = fa[u][i];
if(u == v)return u;
for(int i = 18; i >= 0; --i)if(fa[u][i] != fa[v][i])u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
int kfa(int x, int k){
for(int i = 18; i >= 0; --i)if((k >> i) & 1)x = fa[x][i];
return x;
}
int dis(int u, int v){return dep[u] + dep[v] - 2 * dep[LCA(u, v)];}
void dfs(int x){
for(int i = 1; i <= 18; ++i)fa[x][i] = fa[fa[x][i - 1]][i - 1];
for(int v : g[x])if(v != fa[x][0]){
dep[v] = dep[x] + 1;
fa[v][0] = x; dfs(v);
}
}
int now;
bool cmp(int x, int y){return dis(now, x) > dis(now, y);}
void up(int x){
for(int v : g[x])if(v != fa[x][0]){up(v); f[x].push_back(f[v][0]);}
now = x; sort(f[x].begin(), f[x].end(), cmp);
while(f[x].size() < 3)f[x].push_back(x); f[x].resize(3);
}
void down(int x){
now = x; sort(f[x].begin(), f[x].end(), cmp);
while(f[x].size() < 3)f[x].push_back(x); f[x].resize(3);
for(int v : g[x])if(v != fa[x][0]){
if(dis(f[x][0], x) > dis(f[x][0], v))f[v].push_back(f[x][1]);
else f[v].push_back(f[x][0]);
down(v);
}
}
struct data{
int pos, val;
friend data operator + (const data &x, const data &y){
return x.val == y.val ? (x.pos > y.pos ? x : y) : (x.val > y.val ? x : y);
}
};
struct seg{
data t[maxn << 2 | 1];
void modify(int x, int l, int r, int pos, data val){
if(l == r){t[x] = t[x] + val; return;}
int mid = (l + r) >> 1;
if(pos <= mid)modify(x << 1, l, mid, pos, val);
else modify(x << 1 | 1, mid + 1, r, pos, val);
t[x] = t[x << 1] + t[x << 1 | 1];
}
data query(int x, int l, int r, int L, int R){
if(L <= l && r <= R)return t[x];
int mid = (l + r) >> 1;
if(R <= mid)return query(x << 1, l, mid, L, R);
if(L > mid)return query(x << 1 | 1, mid + 1, r, L, R);
return query(x << 1, l, mid, L, R) + query(x << 1 | 1, mid + 1, r, L, R);
}
}T;
int rid[maxn], tmp[maxn];
struct node{
int u, v, w, d[3], pos, id;
void init(){
int uv = read(), uw = read(), vw = read();
u = (uv + uw - vw) / 2, v = (vw + uv - uw) / 2, w = (uw + vw - uv) / 2;
d[0] = u; d[1] = v; d[2] = w; sort(d, d + 3); reverse(d, d + 3);
}
friend bool operator < (const node &x, const node &y){return x.d[0] > y.d[0];}
}d[maxn];
bool cmp1(int x, int y){return tmp[x] < tmp[y];}
int rem[3];
int find(int x, int v, int dis){
if(dis == 0)return x;
int lca = LCA(x, v);
if(lca == x)return kfa(v, dep[v] - dep[x] - dis);
if(lca == v)return kfa(x, dis);
if(dep[x] - dep[lca] >= dis)return kfa(x, dis);
dis = dis - dep[x] + dep[lca];
return kfa(v, dep[v] - dep[lca] - dis);
}
void print(node &x){
for(int i = 0; i < 3; ++i)rem[i] = find(x.pos, f[x.pos][i], x.d[i]);
for(int i = 0; i < 3; ++i)if(x.d[i] == x.u){printf("%d ",rem[i]); x.d[i] = -1; break;}
for(int i = 0; i < 3; ++i)if(x.d[i] == x.v){printf("%d ",rem[i]); x.d[i] = -1; break;}
for(int i = 0; i < 3; ++i)if(x.d[i] == x.w){printf("%d",rem[i]); x.d[i] = -1; break;}
printf("\n");
}
int main(){
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
n = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
g[u].push_back(v); g[v].push_back(u);
}
dfs(1); up(1); down(1);
q = read(); for(int i = 1; i <= q; ++i)d[i].init(), d[i].id = i;
for(int i = 1; i <= n; ++i)tmp[i] = dis(i, f[i][0]);
sort(d + 1, d + q + 1); for(int i = 1; i <= q; ++i)rid[d[i].id] = i;
for(int i = 1; i <= n; ++i)all.push_back(i); sort(all.begin(), all.end(), cmp1);
int p = 1;
for(int i = n; i >= 0; --i){
while(!all.empty() && dis(all.back(), f[all.back()][0]) == i){
T.modify(1, 0, n, dis(all.back(), f[all.back()][1]), data{all.back(), dis(all.back(), f[all.back()][2])});
all.pop_back();
}
while(p <= q && d[p].d[0] == i){
d[p].pos = T.query(1, 0, n, d[p].d[1], n).pos;
++p;
}
}
for(int i = 1; i <= q; ++i)print(d[rid[i]]);
return 0;
}
B. 马戏团里你最忙
首先用 \(fwt\) 暴力 \(f_{i, s}\) 表示第 \(i\) 轮,得到的数为 \(s\) 的期望
\(f_i = \sum_{j = 1}^{m}f_{i - j}a_{j - 1}\)
满足线性递推
先暴力出前几项,然后高斯消元得到系数。
阿巴阿巴阿巴阿巴
code
C. 树
考虑用类似倍增的东西处理
如果是一条链
\(f_{i, j}\) 表示从 \(i\) 开始 \(2^j\) 个数的对 \(i\) 的答案
在树上可以维护每个点不停走右儿子左侧的答案
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1e6 + 55;
int n, m, v[maxn];
int fa[maxn], son[maxn][21], las[maxn], dep[maxn], pre[maxn];
ll f[maxn][21], si[maxn], sum[maxn][21];
vector<int>g[maxn], dx[maxn];
void dfs(int x){
son[x][0] = las[dep[x] + 1];
pre[x] = las[dep[x]];
las[dep[x]] = x;
dx[dep[x]].push_back(x);
for(int v : g[x])dep[v] = dep[x] + 1, dfs(v), son[x][0] = v;
}
void rdfs(int x){
for(int v : g[x])rdfs(v);
si[x] += si[son[x][0]];
for(int i = 0; i <= 20; ++i)sum[x][i] += sum[son[x][0]][i];
}
ll calc(int x, int k){
if(x == 0)return 0;
++k;
ll res = 0; int t = x;
for(int i = 20; i >= 0; --i)if(k >> i & 1)res += f[x][i], x = son[x][i];
int v = x; x = t;
for(int i = 20; i >= 0; --i)if(k >> i & 1)x = son[x][i], res += (si[x] - si[v]) * (1 << i) - (sum[x][i] - sum[v][i]) * (1 << (i + 1));
return res;
}
int main(){
// freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
n = read();
for(int i = 1; i <= n; ++i)v[i] = read();
for(int i = 2; i <= n; ++i)g[fa[i] = read()].push_back(i);
dep[1] = 1; dfs(1);
for(int d = 1; d <= n; ++d)for(int p = 0; p < dx[d].size(); ++p){
int x = dx[d][p];
f[x][0] = v[x]; si[x] = 1;
for(int i = 0; i <= 20; ++i)sum[x][i] = (v[x] >> i & 1);
if(p == 0)continue;
int pr = dx[d][p - 1];
si[x] += si[pr]; f[x][0] += f[pr][0];
for(int i = 0; i <= 20; ++i)sum[x][i] += sum[pr][i];
}
rdfs(1);
for(int i = 1; i <= 20; ++i)
for(int x = 1; x <= n; ++x){
son[x][i] = son[son[x][i - 1]][i - 1];
f[x][i] = f[x][i - 1] + f[son[x][i - 1]][i - 1] + (si[son[x][i - 1]] - si[son[x][i]]) * (1 << (i - 1)) - (sum[son[x][i - 1]][i - 1] - sum[son[x][i]][i - 1]) * (1 << i);
}
m = read();
for(int i = 1; i <= m; ++i){
int x = read(), k = read();
printf("%lld\n",calc(x, k) - calc(pre[x], k));
}
return 0;
}
2023省选武汉联测12
A. 图案
直接 \(kmp\) 跳 \(nxt\) 得到每个周期判断就能过
复杂度显然不太对,但是没啥特别好的卡法
而且好像有个什么结论可以把 \(border\) 划分成 \(log\) 个等差数列,优化一下说不定复杂度是对的??
不过大部分的做法是直接枚举长度 \(hash\) 然后找 \(lcp\) 贡献
垃圾做法
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1e6 + 55;
int n, k, nxt[maxn];
char s[maxn];
int main(){
freopen("pattern.in","r",stdin);
freopen("pattern.out","w",stdout);
scanf("%d%d",&n,&k); scanf("%s", s + 1);
for(int i = 2, j = 0; i <= n; ++i){
while(j && s[j + 1] != s[i])j = nxt[j];
if(s[j + 1] == s[i])++j;
nxt[i] = j;
}
for(int i = 1; i <= n; ++i){
int now = nxt[i];
while(true){
int len = i - now;
if((i / len) % k == 0 || (i % len == 0 && (i / len) % (k + 1) == 0)){
printf("1"); break;
}
if((i / len) < k || len == i){
printf("0"); break;
}
now = nxt[now];
}
}
printf("\n");
return 0;
}
B. 树点购买
我是sb
发现一棵子树内最多一个点不知道 \(f_{x, 1 / 0}\) 表示子树内有没有一个不知道的最小代价
设一个 \(g_{x, 1 / 0}\) 作为方案数
不过我考场是另一个思路,解决前两问比较方便
考虑一个子树内有某些点的权值可以通过外面的点作差得到,称这样的点是可替换的,先把所有贡献统计下来,再记录可替换的最大权值,考虑是否替换即可
第二问在每个点维护两个 \(vector\) 分别表示子树内已经确定的点和可以替换的点,启发式合并即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1e6 + 55, mod = 998244353;
int qpow(int x, int y){
int ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
int n, c[maxn], fa[maxn];
vector<int>g[maxn], rem[maxn], tmp[maxn];
ll f[maxn]; int mx[maxn];
void merge(vector<int>&a, vector<int>&b){
if(b.size() > a.size())swap(a, b);
for(int v : b)a.push_back(v); b.clear();
}
void dfs(int x){
bool leaf = true; int mxp = 0;
// cf[x] = ctmp[x] = 1;
for(int v : g[x])if(v != fa[x]){
fa[v] = x; dfs(v);
f[x] += f[v]; leaf = false;
if(mx[v] > mx[x]){mxp = v, mx[x] = mx[v]; }
else if(mx[v] == mx[x]){mxp = -1;}
merge(rem[x], rem[v]);
}
if(leaf){f[x] = mx[x] = c[x]; tmp[x].push_back(x);}
else{
if(mx[x] > c[x]){
f[x] = f[x] + c[x] - mx[x];
mx[x] = c[x];
tmp[x].push_back(x);
for(int v : g[x])if(v != fa[x] && v != mxp){merge(rem[x], tmp[v]); }
}else if(mx[x] == c[x]){
tmp[x].push_back(x);
for(int v : g[x])if(v != fa[x]){
if(v == mxp){merge(tmp[x], tmp[v]);}
else {merge(rem[x], tmp[v]); }
}
}else{
for(int v : g[x])if(v != fa[x]){
if(v == mxp){merge(tmp[x], tmp[v]);}
else {merge(rem[x], tmp[v]);}
}
}
}
}
ll val[maxn][2];
int cf[maxn][2];
bool can[maxn];
int pre[maxn], suf[maxn];
void solve(int x){
bool leaf = true; cf[x][0] = 1;
int las = 1;
for(int v : g[x])if(v != fa[x]){
leaf = false;
solve(v);
val[x][0] += val[v][0];
val[x][1] = min(val[x][1], val[v][1] - val[v][0]);
cf[x][0] = 1ll * cf[x][0] * cf[v][0] % mod;
pre[v] = las; las = 1ll * las * cf[v][0] % mod;
}
if(leaf){
cf[x][0] = can[x];
val[x][0] = c[x];
cf[x][1] = 1;
return;
}
las = 1;
for(int i = g[x].size() - 1; i >= 0; --i)if(g[x][i] != fa[x]){
int v = g[x][i]; suf[v] = las; las = 1ll * las * cf[v][0] % mod;
}
for(int v : g[x])if(v != fa[x]){
if(val[x][1] == val[v][1] - val[v][0])cf[x][1] = (cf[x][1] + 1ll * pre[v] * suf[v] % mod * cf[v][1]) % mod;
}
val[x][1] = val[x][0] + val[x][1];
if(val[x][0] > val[x][1] + c[x])val[x][0] = val[x][1] + c[x], cf[x][0] = cf[x][1];
else if(val[x][1] + c[x] == val[x][0])cf[x][0] = (cf[x][0] + cf[x][1]) % mod;
}
int main(){
freopen("purtree.in","r",stdin);
freopen("purtree.out","w",stdout);
n = read();for(int i = 1; i <= n; ++i)c[i] = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
g[u].push_back(v); g[v].push_back(u);
}
dfs(1);
int k = read();
if(k >= 1)printf("%lld\n",f[1]);
if(k >= 2){
merge(rem[1], tmp[1]);
sort(rem[1].begin(), rem[1].end());
for(int v : rem[1])printf("%d ",v); printf("\n");
}
if(k >= 3){
for(int v : rem[1])can[v] = true;
solve(1);
// printf("%lld\n",val[1][0]);
printf("%d\n",cf[1][0]);
}
return 0;
}
C. 舰队游戏
https://www.luogu.com.cn/problem/AT_arc016_4
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 105;
const double eps = 1e-8, inf = 1e12;
int n, m, h, d[maxn];
vector<int>g[maxn];
double base, f[maxn][maxn];
bool vis[maxn];
void dfs(int x){
vis[x] = true; if(x == n)return;
for(int i = 1; i <= h; ++i)f[x][i] = 0;
int deg = g[x].size();
for(int v : g[x]){
if(!vis[v])dfs(v);
for(int i = 1; i <= h; ++i){
if(i > d[v])f[x][i] += f[v][i - d[v]];
else f[x][i] = inf;
}
}
for(int i = 1; i <= h; ++i)
if(!deg)f[x][i] = base + h - i;
else if(x == 1 && i == h)f[x][i] = f[x][i] / deg + 1;
else f[x][i] = min(f[x][i] / deg + 1, base + h - i);
}
double solve(double mid){
base = mid;
for(int i = 1; i <= n; ++i)vis[i] = false;
dfs(1);
if(!vis[n])return inf;
return f[1][h];
}
int main(){
freopen("kancolle.in","r",stdin);
freopen("kancolle.out","w",stdout);
n = read(), m = read(), h = read();
for(int i = 1; i <= m; ++i){
int u = read(), v = read();
g[u].push_back(v);
}
for(int i = 1; i <= n; ++i)d[i] = read();
double l = 0, r = 1e6 + 5;
while(r - l > eps){
double mid = (l + r) / 2;
if(solve(mid) > mid) l = mid;
else r = mid;
}
if(l > 1e6)printf("-1");
else printf("%.10lf\n",l);
return 0;
}
2023省选武汉联测13
两个构造一个交互,你管这个叫省选模拟?
A. 构树
没啥可说的
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
const int maxn = 1005;
int n, l[maxn], r[maxn];
int mp[maxn][maxn];
int f[maxn], cnt;
int fa(int x){return f[x] == x ? x : f[x] = fa(f[x]);}
bool merge(int x, int y){x = fa(x); y = fa(y); if(x == y)return false; f[y] = x; return true;}
bool add(int x, int y){
if(mp[x][y])return false;
if(merge(x, y) == false)return true;
mp[x][y] = mp[y][x] = true; ++cnt;
return false;
}
bool solve(){
for(int i = 1; i <= n; ++i){
if(i < l[l[i]] || i > r[l[i]])return false;
if(i < l[r[i]] || i > r[r[i]])return false;
if(add(i, l[i]))return false;
if(add(i, r[i]))return false;
}
for(int i = 1; i <= n; ++i)
for(int j = l[i] + 1; j < r[i]; ++j)if(l[j] <= i && r[j] >= i && fa(i) != fa(j))add(i, j);
return cnt == n - 1;
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n = read();
for(int i = 1; i <= n; ++i)f[i] = i;
for(int i = 1; i <= n; ++i)l[i] = read(), r[i] = read();
if(solve()){
for(int i = 1; i <= n; ++i)
for(int j = i + 1; j <= n; ++j)
if(mp[i][j])printf("%d %d\n",i, j);
}
else printf("-1\n");
return 0;
}
B. 交互
依次考虑每个点,维护一个类似右链的东西
查询当前点是不是叶子,如果不是先不管
是的话,查询上个栈里的点是不是他的父亲,如果不是,下一个没有加入的数就是他的父亲
处理一下边界情况即可
code
#include<bits/stdc++.h>
#include<cstdlib>
#include"interact.h"
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int maxn = 105;
int st[maxn], top, l[maxn], r[maxn];
void guess(int n){
for(int i = 1; i <= n; ++i)l[i] = r[i] = i;
for(int i = 1; i <= n; ++i){
if(query(i, l[i], i)){
if(top){
st[++top] = i;
while(top > 1 && query(st[top - 1], l[st[top - 1]], r[st[top]])){
report(st[top - 1], st[top]);
r[st[top - 1]] = r[st[top]];
--top;
}
if(top && i != n){
report(i + 1, st[top]);
l[i + 1] = l[st[top]];
--top;
}
}else{
if(i == n || query(i, 1, n)){
l[i] = 1; r[i] = n; st[++top] = i;
}else{
report(i + 1, i);
l[i + 1] = l[i];
}
}
}else st[++top] = i;
}
for(int i = top; i > 1; --i)report(st[i], st[i - 1]);
}
C. 构图
首先不难发现只用度数为 \(k\) 和 \(k + 1\) 的点就能构造出最优方案
度数为 \(k\) 的点只能向 \(k + 1\) 的点连边,总度数又要求最小,所以找到最小的数量 \(x\), 使得 \((k + 1)x >= k(n - x)\) 此时达到最优情况
然后连边的话什么奇怪做法都有,我的连边好像有问题,但是过了。
code
// ubsan: undefined
// accoders
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
int read(){
int x = 0; char c = getchar();
while(!isdigit(c))c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return x;
}
int n, k;
vector<pii>edge;
void print(){
printf("%d\n",(int)edge.size());
for(pii v : edge)printf("%d %d\n",v.first, v.second);
}
void sol1(){
for(int i = 1; i <= n; i += k + k + 1){
for(int j = 0; j < k; ++j)
for(int p = k; p < k + k + 1; ++p)
edge.push_back(pii(i + j, i + p));
}
}
void sol2(){
if(n % (k + k + 1) == 1){
n -= 6;
for(int i = 1; i <= 4; ++i)
for(int j = 5; j <= 6; ++j)
edge.push_back(pii(n + i, n + j));
}
if(n % (k + k + 1) == 2){
n -= 7;
edge.push_back(pii(n + 1, n + 4));
edge.push_back(pii(n + 1, n + 5));
edge.push_back(pii(n + 2, n + 5));
edge.push_back(pii(n + 2, n + 6));
edge.push_back(pii(n + 3, n + 6));
edge.push_back(pii(n + 3, n + 7));
edge.push_back(pii(n + 4, n + 5));
edge.push_back(pii(n + 6, n + 7));
edge.push_back(pii(n + 4, n + 7));
}
if(n % (k + k + 1) == 3){
n -= 4;
for(int i = 1; i <= 2; ++i)
for(int j = 3; j <= 4; ++j)
edge.push_back(pii(n + i, n + j));
edge.push_back(pii(n + 3, n + 4));
n -= 4;
for(int i = 1; i <= 2; ++i)
for(int j = 3; j <= 4; ++j)
edge.push_back(pii(n + i, n + j));
edge.push_back(pii(n + 3, n + 4));
}
if(n % (k + k + 1) == 4){
n -= 4;
for(int i = 1; i <= 2; ++i)
for(int j = 3; j <= 4; ++j)
edge.push_back(pii(n + i, n + j));
edge.push_back(pii(n + 3, n + 4));
}
sol1();
}
const int maxn = 1e5 + 55;
vector<int>vec;
void solve(){
int k1 = (k & 1) && (n & 1);
while(k1 * (k + 1) < (n - k1) * k)k1 += 2;
for(int j = 1; j <= k + 1; ++j)
for(int i = 1; i <= k1; ++i)
vec.push_back(i);
for(int i = k1 + 1; i <= n; ++i)
for(int j = 1; j <= k; ++j){
edge.push_back(pii(vec.back(), i));
vec.pop_back();
}
while(vec.size()){
int u = vec.back(); vec.pop_back();
int v = vec.back(); vec.pop_back();
edge.push_back(pii(u, v));
}
}
int main(){
freopen("graph.in","r",stdin);
freopen("graph.out","w",stdout);
n = read(), k = read();
if(n % (k + k + 1) == 0)sol1();
else{
if(k == 2)sol2();
else{
solve();
}
}
print();
return 0;
}