2023冲刺国赛模拟2
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 = 5e5 + 55;
int n, a, b;
vector<int>g[maxn], chain, tmp[maxn];
bool is[maxn];
int mxd[maxn], fa[maxn], f[maxn];
void get_chain(int x){for(int v : g[x])if(v != fa[x]){fa[v] = x; get_chain(v);}}
void dfs(int x, int fa){
tmp[x].clear();
for(int v : g[x])if(v != fa && !is[v]){
dfs(v, x); tmp[v].clear();
tmp[x].push_back(f[v]);
}
f[x] = 1;
sort(tmp[x].begin(), tmp[x].end());
reverse(tmp[x].begin(), tmp[x].end());
for(int i = 0; i < tmp[x].size(); ++i){
if(tmp[x][i] + i + 1 >= f[x]){
f[x] = tmp[x][i] + i + 1;
mxd[x] = tmp[x][i];
}
}
}
int calc(int i){
int resl = f[chain[i - 1]], resr = f[chain[i]];
for(int j = i - 2; j >= 0; --j){
int v = chain[j];
resl = max(f[v] + (resl >= mxd[v]), resl + 1);
}
for(int j = i + 1; j < chain.size(); ++j){
int v = chain[j];
resr = max(f[v] + (resr >= mxd[v]), resr + 1);
}
return max(resl, resr) - 1;
}
int query(int i){
int resl = f[chain[i - 1]], resr = f[chain[i]];
for(int j = i - 2; j >= 0; --j){
int v = chain[j];
resl = max(f[v] + (resl >= mxd[v]), resl + 1);
}
for(int j = i + 1; j < chain.size(); ++j){
int v = chain[j];
resr = max(f[v] + (resr >= mxd[v]), resr + 1);
}
return resl - resr;
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
n = read(), a = read(), b = read();
for(int i = 1; i < n; ++i){
int u = read(), v = read();
g[u].push_back(v); g[v].push_back(u);
}
get_chain(a);
int now = b;
while(now){
is[now] = true;
chain.push_back(now);
now = fa[now];
}
for(int v : chain)dfs(v, 0);
int l = 1, r = chain.size() - 1;
while(r - l >= 2){
int mid = (l + r) >> 1, val = query(mid);
if(val == 0)l = r = mid;
else if(val > 0)r = mid;
else l = mid;
}
int ans = INT_MAX;
for(int i = l; i <= r; ++i)ans = min(ans, calc(i));
printf("%d\n",ans);
return 0;
}
B. 最小生成树
考虑给树边确定好大小关系,那么非树边需要大于其连接的两个点树上路径的最大值,可以唯一确定
那么这个限制关系就是一个
有一个长度为 的链表示树边,非树边挂在下面
那么问题可以转化成,有若干黑白球,白球任意放,黑球只能放最前面的方案数
放球的顺序是 个白球, 个黑球, 个白球, 个黑球.....
记录四元组表示当前有个球,个黑球,方案数为,黑球位置和为。在加入球的过程中更新四元组。
- 加入一个白球:
- 加入个白球:
- 加入一个黑球:
那么通过枚举前条边的权值大小顺序,可以得到一个的做法
使用状压可以 , 问题在于求
每次枚举一条边加上时,需要的 是能和未加入的边集和当前边组成环的,
那么如果 表示能与 组成环的集合大小那么 即为
那么这个东西可以预处理每条非树边对应的树边的集合,做子集和卷积即可
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 = 1e9 + 7, mx = 1 << 20, maxn = 405;
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, fac[maxn], ifac[maxn];
void add(int &x, int y){x += y; if(x >= mod)x -= mod;}
int f[mx], g[mx], h[mx], a[mx];
int fa[25], dep[25], id[25];
vector<pii>e[25];
void dfs(int x){
for(pii v : e[x])if(v.first != fa[x]){
fa[v.first] = x; id[v.first] = v.second;
dep[v.first] = dep[x] + 1;
dfs(v.first);
}
}
int main(){
freopen("mst.in","r",stdin);
freopen("mst.out","w",stdout);
n = read(), m = read();
fac[0] = ifac[0] = 1; for(int i = 1; i <= 200; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[200] = qpow(fac[200], mod - 2);
for(int i = 199; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
for(int i = 1; i < n; ++i){
int u = read(), v = read();
e[u].push_back(pii(v, i));
e[v].push_back(pii(u, i));
}
dfs(1);
for(int i = n; i <= m; ++i){
int u = read(), v = read();
int s = 0;
while(u != v){
if(dep[u] > dep[v])s |= 1 << id[u] - 1, u = fa[u];
else s |= 1 << id[v] - 1, v = fa[v];
}
++a[s];
}
for(int l = 2, hl = 1; l <= (1 << n - 1); hl = l, l <<= 1)
for(int i = 0; i < (1 << n - 1); i += l)
for(int j = i; j < i + hl; ++j)
add(a[j + hl], a[j]);
g[0] = 1;
for(int s = 0; s < (1 << n - 1); ++s){
int cnt = __builtin_popcount(s);
for(int i = 1; i < n; ++i)if(!((s >> i - 1) & 1)){
int w = a[((1 << n - 1) - 1) ^ s] - a[((1 << n - 1) - 1) ^ (s | (1 << i - 1))];
int ng = 1ll * g[s] * fac[h[s] + w] % mod * ifac[h[s]] % mod;
add(g[s | (1 << i - 1)], ng);
add(f[s | (1 << i - 1)], (1ll * f[s] * fac[h[s] + w + 1] % mod * ifac[h[s] + 1] % mod + 1ll * (cnt + 1) * ng) % mod);
h[s | (1 << i - 1)] = h[s] + w + 1;
}
}
printf("%d\n", f[(1 << n - 1) - 1]);
return 0;
}
C. 矩阵
行和列相对独立,分开维护
每次查询到如果行列都是原序列上的值可以直接输出
而对于修改过的值,其值只用关心后修改的那部分,找到当前第 大的点,然后查询在修改的那个时刻该点的排名即为答案
用到了历史信息,所以用可持久化平衡树维护
查询历史版本某个点的排名需要一个权值,用替罪羊树维护,每次在历史版本上查询即可
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; bool f = false; char c = getchar();
while(!isdigit(c))f = c == '-', c = getchar();
do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
return f ? -x : x;
}
const int maxn = 4e5 + 55;
mt19937 rd((ull)&maxn);
int sint(){return uniform_int_distribution<>(INT_MIN, INT_MAX)(rd);}
int n, p, q;
struct data{
double val[maxn];
struct scapegoat_tree{
const double alpha = 0.75;
struct node{
int l, r, si, id;
}t[maxn];
int cnt, st[maxn], top;
double *val;
void pre(double *p){val = p;}
void push_up(int x){t[x].si = t[t[x].l].si + t[t[x].r].si + 1;}
void dfs(int x){
if(t[x].l)dfs(t[x].l);
st[++top] = x;
if(t[x].r)dfs(t[x].r);
}
int build(int l, int r, double ll, double rr){
if(l > r)return 0;
int mid = (l + r) >> 1, x = st[mid]; val[t[x].id] = (ll + rr) / 2;
t[x].l = build(l, mid - 1, ll, val[t[x].id]);
t[x].r = build(mid + 1, r, val[t[x].id], rr);
push_up(x); return x;
}
void rebuild(int &x, double l, double r){
if(!x)return; top = 0; dfs(x);
x = build(1, top, l, r);
}
bool check(int x){return alpha * t[x].si <= max(t[t[x].l].si, t[t[x].r].si);}
void insert(int &x, int k, int id, double l, double r){
double mid = (l + r) / 2;
if(!x){
x = ++cnt;
t[x].si = 1;
t[x].id = id;
val[id] = mid;
return;
}
if(t[t[x].l].si + 1 >= k)insert(t[x].l, k, id, l, mid);
else insert(t[x].r, k - t[t[x].l].si - 1, id, mid, r);
push_up(x); if(check(x))rebuild(x, l, r);
}
}st;
struct FHQ_Treap{
double *val;
void pre(double *p){val = p;}
struct node{
int l, r, si, id, key;
}t[maxn * 30];
int cnt;
int New(int id){++cnt; t[cnt].si = 1; t[cnt].id = id; t[cnt].key = sint(); return cnt;}
void push_up(int x){t[x].si = t[t[x].l].si + t[t[x].r].si + 1;}
int merge(int x, int y){
if(!x || !y)return x | y;
int now = ++cnt;
if(t[x].key < t[y].key){
t[now] = t[x];
t[now].r = merge(t[x].r, y);
}else{
t[now] = t[y];
t[now].l = merge(x, t[y].l);
}
push_up(now); return now;
}
void split(int x, int k, int &l, int &r){
if(!x){l = r = 0; return;}
int now = ++cnt; t[now] = t[x];
if(t[t[x].l].si + 1 <= k){
split(t[now].r, k - t[t[x].l].si - 1, t[now].r, r);
l = now;
}else{
split(t[now].l, k, l, t[now].l);
r = now;
}
push_up(now);
}
void insert(int &rt, int k, int id){
int l = 0, r = 0; split(rt, k - 1, l, r);
rt = merge(merge(l, New(id)), r);
}
int kth(int x, int k){
while(true){
if(t[t[x].l].si + 1 == k)return t[x].id;
if(t[t[x].l].si >= k)x = t[x].l;
else k -= t[t[x].l].si + 1, x = t[x].r;
}
}
int rank(int x, int k){
int ans = 0;
while(true){
if(val[t[x].id] == val[k])return ans + t[t[x].l].si + 1;
if(val[t[x].id] < val[k])ans += t[t[x].l].si + 1, x = t[x].r;
else x = t[x].l;
}
return ans;
}
}ft;
data(){st.pre(val); ft.pre(val);}
int rt[maxn], srt;
void insert(int id, int k, int ver){
st.insert(srt, k, id, 0, 10000);
ft.insert(rt[ver], k, id);
}
}t1, t2;
const int mv = 1e5 + 1;
int main(){
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
n = read(); int cnt = 2e5 + 1;
for(int i = 1; i <= cnt; ++i)t1.insert(i, i, 0), t2.insert(i, i, 0);
for(int i = 1; i <= n; ++i){
int op = read(), x = read();
t1.rt[i] = t1.rt[i - 1];
t2.rt[i] = t2.rt[i - 1];
if(op == 1)t1.insert(cnt + i, x + p + q + mv, i);
else if(op == 2)t2.insert(cnt + i, x + p + q + mv, i);
else{
int y = read();
x += p + q; y += p - q;
int a = t2.ft.kth(t2.rt[i], x + mv), b = t1.ft.kth(t1.rt[i], y + mv);
if(a <= cnt && b <= cnt)p = a - mv, q = b - mv;
else if(a > b){
p = a - cnt; q = t1.ft.rank(t1.rt[a - cnt], b) - mv;
}else{
p = t2.ft.rank(t2.rt[b - cnt], a) - mv; q = b - cnt;
}
printf("%d %d\n",p, q);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)