【做题记录】csp2025-提高组生成树和笛卡尔树专题

A. [COCI2009-2010#7] SVEMIR

显然 boruvka。将所有点分别按照 \(x\)\(y\)\(z\) 排序,更新最小边。时间复杂度 \(O(n\log^2 n)\)

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
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
#include<cstdio> #include<iostream> #include<utility> #include<algorithm> #include<climits> #define ll long long #define il inline #define pii pair<int,int> #define mp make_pair #define fir first #define sec second using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=1e5+5; int n,fa[maxn],sz[maxn]; pii mne[maxn]; struct node{ int x,y,z,id; }a[maxn]; il int find(int x){ return x!=fa[x]?fa[x]=find(fa[x]):x; } il void merge(int u,int v){ u=find(u),v=find(v); if(u==v){ return ; } if(sz[u]>sz[v]){ sz[u]+=sz[v],fa[v]=u; } else{ sz[v]+=sz[u],fa[u]=v; } } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n; for(int i=1;i<=n;i++){ cin>>a[i].x>>a[i].y>>a[i].z; a[i].id=fa[i]=i,sz[i]=1; } ll ans=0; for(;;){ // puts("666"); for(int i=1;i<=n;i++){ mne[i]=mp(INT_MAX,0); } sort(a+1,a+n+1,[](const node &p,const node &q){return p.x<q.x;}); for(int i=1,u,v,w;i<n;i++){ u=find(a[i].id),v=find(a[i+1].id),w=a[i+1].x-a[i].x; if(u!=v){ mne[u]=min(mne[u],mp(w,v)); mne[v]=min(mne[v],mp(w,u)); } } sort(a+1,a+n+1,[](const node &p,const node &q){return p.y<q.y;}); for(int i=1,u,v,w;i<n;i++){ u=find(a[i].id),v=find(a[i+1].id),w=a[i+1].y-a[i].y; if(u!=v){ mne[u]=min(mne[u],mp(w,v)); mne[v]=min(mne[v],mp(w,u)); } } sort(a+1,a+n+1,[](const node &p,const node &q){return p.z<q.z;}); for(int i=1,u,v,w;i<n;i++){ u=find(a[i].id),v=find(a[i+1].id),w=a[i+1].z-a[i].z; if(u!=v){ mne[u]=min(mne[u],mp(w,v)); mne[v]=min(mne[v],mp(w,u)); } } bool flag=0; for(int i=1;i<=n;i++){ if(find(i)==i&&mne[i].fir<INT_MAX&&find(i)!=find(mne[i].sec)){ flag=1; merge(i,mne[i].sec); ans+=mne[i].fir; } } if(!flag){ break; } } cout<<ans; return 0; } } int main(){return asbt::main();}

B. [SCOI2012] 滑雪

对于第一问,按照高度决定连边方向,跑个 bfs 即可。
对于第二问,显然 prim 可做。但是在优先队列内,需要按照点的高度从大到小为第一关键字,距离从小到大为第二关键字。原因是我们建的是一张有向图,有些边从较低的点向较高的点是更新不到的。而如果依次考虑由高向低的点,则一定可以保证每条边都被考虑到,且考虑到每个点时指向它的边一定已经被考虑完了。时间复杂度 \(O(m\log n)\)

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
  • 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
#include<bits/stdc++.h> #define ll long long #define il inline #define pii pair<int,int> #define mp make_pair #define fir first #define sec second #define pb push_back using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=1e5+5,maxm=1e6+5; int n,m,a[maxn],fa[maxn],sz[maxn]; ll dis[maxn]; bool vis[maxn],viss[maxn]; vector<pii> e[maxn]; queue<int> q; struct cmp{ il bool operator()(const pii &x,const pii &y)const{ if(a[x.sec]!=a[y.sec]){ return a[x.sec]<a[y.sec]; } return x.fir>y.fir; } }; priority_queue<pii,vector<pii>,cmp> p; struct node{ int u,v,w; il bool operator<(const node &x)const{ return w<x.w; } }ed[maxm<<1]; il int find(int x){ return x!=fa[x]?fa[x]=find(fa[x]):x; } il void merge(int u,int v){ u=find(u),v=find(v); if(u==v){ return ; } if(sz[u]>sz[v]){ sz[u]+=sz[v],fa[v]=u; } else{ sz[v]+=sz[u],fa[u]=v; } } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); // cout<<cplx::usdmem(); cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; fa[i]=i,sz[i]=1; } for(int i=1,u,v,w;i<=m;i++){ cin>>u>>v>>w; if(a[u]>=a[v]){ e[u].pb(mp(v,w)); } if(a[v]>=a[u]){ e[v].pb(mp(u,w)); } } int ans1=0; q.push(1),vis[1]=1; while(q.size()){ int u=q.front(); ans1++,q.pop(); for(pii i:e[u]){ int v=i.fir; if(!vis[v]){ q.push(v),vis[v]=1; } } } memset(dis,0x3f,sizeof dis); dis[1]=0,p.push(mp(0,1)); while(p.size()){ int u=p.top().sec; // cout<<u<<"\n"; p.pop(),viss[u]=1; for(pii i:e[u]){ int v=i.fir,w=i.sec; if(!vis[v]){ continue; } if(!viss[v]&&dis[v]>w){ dis[v]=w; p.push(mp(w,v)); } } } ll ans2=0; for(int i=1;i<=n;i++){ if(vis[i]){ ans2+=dis[i]; } } cout<<ans1<<" "<<ans2; return 0; } } int main(){return asbt::main();}

C. [USACO01OPEN] Earthquake

\[\begin{aligned} &ans=\frac{f-\sum_{i\in S}c_i}{\sum_{i\in S}t_i}\\ \Leftrightarrow&f=\sum_{i\in S}t_i\times ans+c_i \end{aligned} \]

其中 \(S\) 为最终选择的边集。
则对于任意一个生成树 \(K\) 我们有:

\[\begin{aligned} &ans\ge\frac{f-\sum_{i\in K}c_i}{\sum_{i\in K}t_i}\\ \Leftrightarrow&f\le \sum_{i\in K}t_i\times ans+c_i \end{aligned} \]

于是对于任意实数 \(x\),考虑它与 \(ans\) 的大小关系:

  • \(x>ans\)
    \(\forall K,f<\sum_{i\in K}t_i\times x+c_i\)
  • \(x=ans\)
    \(\forall K,f\le\sum_{i\in K}t_i\times x+c_i\)
  • \(x<ans\)
    \(\exist K,f>\sum_{i\in K}t_i\times x+c_i\)

于是二分答案,跑最小生成树 check 即可。时间复杂度 \(O(m\log^2)\)
因为输出要求精确到小数点后第 \(4\) 位,所以要求二分出来的答案精确到小数点后第 \(5\) 位,所以 \(\varepsilon\) 应该赋为 \(10^{-6}\)

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
#include<bits/stdc++.h> #define ll long long #define il inline using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=405,maxm=1e4+5; const double eps=1e-6; int n,m,f,fa[maxn],sz[maxn]; struct node{ int u,v,c,t; }e[maxm]; il int find(int x){ return fa[x]!=x?fa[x]=find(fa[x]):x; } il void merge(int u,int v){ u=find(u),v=find(v); if(u==v){ return ; } if(sz[u]>sz[v]){ sz[u]+=sz[v],fa[v]=u; } else{ sz[v]+=sz[u],fa[u]=v; } } il double kruskal(double x){ sort(e+1,e+m+1,[=](const node &p,const node &q){return p.t*x+p.c<q.t*x+q.c;}); for(int i=1;i<=n;i++){ fa[i]=i,sz[i]=1; } double res=0; for(int i=1,u,v;i<=m;i++){ u=e[i].u,v=e[i].v; if(find(u)!=find(v)){ merge(u,v); res+=e[i].t*x+e[i].c; } } // cout<<res<<"\n"; return res; } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m>>f; for(int i=1;i<=m;i++){ cin>>e[i].u>>e[i].v>>e[i].c>>e[i].t; } double l=0,r=1e14; while(r-l>eps){ double mid=(l+r)/2; // cout<<mid<<" "; if(kruskal(mid)>=f){ r=mid; } else{ l=mid; } } printf("%.4f",l); return 0; } } int main(){return asbt::main();}

D. [蓝桥杯 2023 省 A] 网络稳定性

建出最大生成树,然后树剖 \(+\) 线段树链查询最小值即可。时间复杂度 \(O(q\log^2n)\)

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
  • 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
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
#include<bits/stdc++.h> #define ll long long #define il inline #define pii pair<int,int> #define mp make_pair #define fir first #define sec second #define pb push_back #define lid id<<1 #define rid id<<1|1 using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=1e5+5,inf=0x3f3f3f3f; int n,m,q,fa[maxn],sz[maxn],dis[maxn]; int dep[maxn],hes[maxn],top[maxn]; int dfn[maxn],idx[maxn],cnt,tr[maxn<<2]; struct node{ int u,v,w; il bool operator<(const node &x)const{ return w>x.w; } }a[maxn*3]; vector<pii> e[maxn]; struct{ int fa[maxn],sz[maxn]; il void init(){ for(int i=1;i<=n;i++){ fa[i]=i,sz[i]=1; } } il int find(int x){ return x!=fa[x]?fa[x]=find(fa[x]):x; } il void merge(int u,int v){ u=find(u),v=find(v); if(u==v){ return ; } if(sz[u]>sz[v]){ sz[u]+=sz[v],fa[v]=u; } else{ sz[v]+=sz[u],fa[u]=v; } } il bool check(int u,int v){ return find(u)==find(v); } }D; il void dfs1(int u){ dep[u]=dep[fa[u]]+1; sz[u]=1; int mxs=0; for(pii i:e[u]){ int v=i.fir,w=i.sec; if(v==fa[u]){ continue; } fa[v]=u,dis[v]=w; dfs1(v); sz[u]+=sz[v]; if(mxs<sz[v]){ mxs=sz[v],hes[u]=v; } } } il void dfs2(int u){ dfn[u]=++cnt; idx[cnt]=u; if(!top[u]){ top[u]=u; } if(hes[u]){ top[hes[u]]=top[u]; dfs2(hes[u]); } for(pii i:e[u]){ int v=i.fir; if(v!=fa[u]&&v!=hes[u]){ dfs2(v); } } } il void pushup(int id){ tr[id]=min(tr[lid],tr[rid]); } il void build(int id,int l,int r){ if(l==r){ tr[id]=dis[idx[l]]; return ; } int mid=(l+r)>>1; build(lid,l,mid); build(rid,mid+1,r); pushup(id); } il int query(int id,int L,int R,int l,int r){ if(l>r){ return inf; } if(L>=l&&R<=r){ return tr[id]; } int mid=(L+R)>>1,res=inf; if(l<=mid){ res=min(res,query(lid,L,mid,l,r)); } if(r>mid){ res=min(res,query(rid,mid+1,R,l,r)); } return res; } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m>>q; for(int i=1;i<=m;i++){ cin>>a[i].u>>a[i].v>>a[i].w; } sort(a+1,a+m+1); D.init(); for(int i=1,u,v,w;i<=m;i++){ u=a[i].u,v=a[i].v,w=a[i].w; if(D.find(u)!=D.find(v)){ D.merge(u,v); e[u].pb(mp(v,w)); e[v].pb(mp(u,w)); } } for(int i=1;i<=n;i++){ if(!dep[i]){ dfs1(i),dfs2(i); } } build(1,1,n); while(q--){ int u,v; cin>>u>>v; if(!D.check(u,v)){ cout<<"-1\n"; } else{ int res=inf; while(top[u]!=top[v]){ if(dep[top[u]]<dep[top[v]]){ swap(u,v); } res=min(res,query(1,1,n,dfn[top[u]],dfn[u])); u=fa[top[u]]; } if(dep[u]>dep[v]){ swap(u,v); } res=min(res,query(1,1,n,dfn[u]+1,dfn[v])); cout<<res<<"\n"; } } return 0; } } int main(){return asbt::main();}

E. [THUPC2022 初赛] 最小公倍树

考虑枚举公因子 \(d\)\(d\)\([l,r]\) 之间的倍数形如 \(\{k\times d,(k+1)\times d,\dots,(k+p)\times d\}\),显然所有点都向 \(k\times d\) 连边是最优的。于是边数为 \(O((r-l)\log)\)。然后再跑 kruskal,时间复杂度为 \(O((r-l)\log^2)\)

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
#include<bits/stdc++.h> #define int long long #define il inline #define pb push_back #define gcd __gcd using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=1e6+5; int n,m,fa[maxn],sz[maxn]; il int find(int x){ return x!=fa[x]?fa[x]=find(fa[x]):x; } il void merge(int u,int v){ u=find(u),v=find(v); if(u==v){ return ; } if(sz[u]>sz[v]){ sz[u]+=sz[v],fa[v]=u; } else{ sz[v]+=sz[u],fa[u]=v; } } struct node{ int u,v,w; il bool operator<(const node &x)const{ return w<x.w; } }; vector<node> e; namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } signed main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m; for(int i=1;i<=m;i++){ for(int u=(n+i-1)/i*i,v=u+i;v<=m;v+=i){ e.pb((node){u,v,u/gcd(u,v)*v}); } } sort(e.begin(),e.end()); for(int i=n;i<=m;i++){ fa[i]=i,sz[i]=1; } int ans=0; for(node i:e){ int u=i.u,v=i.v,w=i.w; if(find(u)!=find(v)){ merge(u,v),ans+=w; } } cout<<ans; return 0; } } signed main(){return asbt::main();}

F. [BJWC2010] 严格次小生成树

首先建出最小生成树。
遍历每一条非树边,考虑连上它后形成的环,我们要从这个环上删掉原有的最大的一条边。当然边权和新边不能相等。考虑新边一定 \(\ge\) 原有的边的最大值,因此用倍增维护路径上的最大值和严格次大值即可。同时,新加 \(2\) 条边一定不如新加 \(1\) 条边更优,所以依次枚举非树边是正确的。时间复杂度 \(O(m\log m)\)

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
  • 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
#include<bits/stdc++.h> #define ll long long #define il inline #define pb push_back #define pii pair<int,int> #define mp make_pair #define pb push_back #define fir first #define sec second using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=1e5+5,maxm=3e5+5; int n,m,dep[maxn],anc[maxn][22]; struct node{ int mx1,mx2; node(int mx1=-1,int mx2=-1):mx1(mx1),mx2(mx2){} il node operator+(const node &x)const{ int hp[7]; hp[1]=mx1,hp[2]=mx2,hp[3]=x.mx1,hp[4]=x.mx2; sort(hp+1,hp+5,greater<int>()); hp[unique(hp+1,hp+5)-hp]=-1; return node(hp[1],hp[2]); } }mxd[maxn][22]; struct edge{ int u,v,w; il bool operator<(const edge &x)const{ return w<x.w; } }a[maxm]; bool vis[maxm]; vector<pii> e[maxn]; il void dfs(int u){ dep[u]=dep[anc[u][0]]+1; for(int i=1;i<=20;i++){ anc[u][i]=anc[anc[u][i-1]][i-1]; mxd[u][i]=mxd[u][i-1]+mxd[anc[u][i-1]][i-1]; } for(pii i:e[u]){ int v=i.fir,w=i.sec; if(v!=anc[u][0]){ anc[v][0]=u; mxd[v][0].mx1=w; dfs(v); } } } il node query(int u,int v){ if(dep[u]<dep[v]){ swap(u,v); } int ddep=dep[u]-dep[v],tmp=0; node res; while(ddep){ if(ddep&1){ res=res+mxd[u][tmp]; u=anc[u][tmp]; } ddep>>=1,tmp++; } if(u==v){ return res; } for(int i=20;~i;i--){ if(anc[u][i]!=anc[v][i]){ res=res+mxd[u][i]+mxd[v][i]; u=anc[u][i],v=anc[v][i]; } } return res+mxd[u][0]+mxd[v][0]; } struct{ int fa[maxn],sz[maxn]; il void init(){ for(int i=1;i<=n;i++){ fa[i]=i,sz[i]=1; } } il int find(int x){ return x!=fa[x]?fa[x]=find(fa[x]):x; } il void merge(int u,int v){ u=find(u),v=find(v); if(u==v){ return ; } if(sz[u]>sz[v]){ sz[u]+=sz[v],fa[v]=u; } else{ sz[v]+=sz[u],fa[u]=v; } } il bool check(int u,int v){ return find(u)==find(v); } }D; namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); // cout<<cplx::usdmem()<<"\n"; cin>>n>>m; for(int i=1;i<=m;i++){ cin>>a[i].u>>a[i].v>>a[i].w; } sort(a+1,a+m+1),D.init(); ll ans1=0,ans2=1e18; for(int i=1,u,v,w;i<=m;i++){ u=a[i].u,v=a[i].v,w=a[i].w; if(!D.check(u,v)){ D.merge(u,v); vis[i]=1,ans1+=w; e[u].pb(mp(v,w)); e[v].pb(mp(u,w)); } } dfs(1); for(int i=1,u,v,w;i<=m;i++){ u=a[i].u,v=a[i].v,w=a[i].w; if(vis[i]){ continue; } node tmp=query(u,v); int hp[7]; hp[1]=tmp.mx1,hp[2]=tmp.mx2,hp[3]=w; sort(hp+1,hp+4,greater<int>()); hp[unique(hp+1,hp+4)-hp]=-1; if(~hp[2]){ ans2=min(ans2,ans1-hp[2]+hp[1]); } } cout<<ans2; return 0; } } int main(){return asbt::main();}

G. [APIO2013] 道路费用

\(k\le 20\),考虑 \(O(2^k)\) 暴力枚举加入的边。但是边数很大,时间复杂度很高无法承受。
考虑在一开始强制选这 \(k\) 条边,然后跑最小生成树,此时加入的边就是一定会加入的边。设这个边集为 \(S\)
\(S\) 连接的连通块缩成点,点数为 \(O(k)\)。再在原图上对这些点跑最小生成树,设加入的边集为 \(T\),则 \(T\) 为加入那 \(k\) 条边后有可能在最小生成树中的边。数量也为 \(O(k)\)
然后暴力枚举强制加入的边,用 \(T\) 跑出最小生成树,再用 \(T\) 中的非树边限制强制加入的边即可。具体地,对于一条非树边 \((u,v)\),树上 \(u\)\(v\) 的路径中的所有边都应该 \(\ge\) 这条非树边的边权。暴力跳父亲即可。
考虑统计答案,只需要计算通过每条边的人数,用 dfs 计算子树权值和即可。
时间复杂度 \(O(m\log m+2^kk^2)\)

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
  • 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
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
#include<bits/stdc++.h> #define int long long #define il inline #define pii pair<int,int> #define mp make_pair #define fir first #define sec second #define pb push_back using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=3e5+5; int n,m,k,a[maxn],bel[maxn]; bool vis[maxn]; vector<int> dk; vector<pii> e[maxn]; struct edge{ int u,v,w; il bool operator<(const edge &x)const{ return w<x.w; } }em[maxn],ek[maxn]; vector<edge> et; int fa[maxn],sz[maxn],ans; int mxe[maxn],dep[maxn],tof[maxn]; il void init(){ for(int i=1;i<=n;i++){ fa[i]=i,sz[i]=1; } } il int find(int x){ return x!=fa[x]?fa[x]=find(fa[x]):x; } il void merge(int u,int v){ u=find(u),v=find(v); if(u==v){ return ; } if(sz[u]>sz[v]){ sz[u]+=sz[v],fa[v]=u; } else{ sz[v]+=sz[u],fa[u]=v; } } il void dfs(int u){ dep[u]=dep[fa[u]]+1; sz[u]=a[u]; for(pii i:e[u]){ int v=i.fir,w=i.sec; if(v!=fa[u]){ fa[v]=u; tof[v]=w; dfs(v); sz[u]+=sz[v]; } } } il void solve(int S){ for(int u:dk){ fa[u]=u,sz[u]=1,e[u].clear(); } for(int i=1,u,v;i<=k;i++){ if(S>>(i-1)&1){ u=bel[ek[i].u],v=bel[ek[i].v]; if(find(u)==find(v)){ return ; } merge(u,v); e[u].pb(mp(v,-i)); e[v].pb(mp(u,-i)); } } for(int i=0;i<et.size();i++){ vis[i]=0; } for(int i=0,u,v,w;i<et.size();i++){ u=et[i].u,v=et[i].v,w=et[i].w; if(find(u)!=find(v)){ merge(u,v); vis[i]=1; e[u].pb(mp(v,w)); e[v].pb(mp(u,w)); } } for(int i=1;i<=k;i++){ mxe[i]=INT_MAX; } for(int u:dk){ dep[u]=tof[u]=fa[u]=sz[u]=0; } dfs(bel[1]); // puts("666"); for(int i=0,u,v,w;i<et.size();i++){ // cout<<i<<"\n"; if(vis[i]){ continue; } u=et[i].u,v=et[i].v,w=et[i].w; if(dep[u]<dep[v]){ swap(u,v); } // cout<<dep[u]<<" "<<dep[v]<<"\n"; while(dep[u]>dep[v]){ // puts("666"); if(tof[u]<0){ mxe[-tof[u]]=min(mxe[-tof[u]],w); } u=fa[u]; } while(u!=v){ if(tof[u]<0){ mxe[-tof[u]]=min(mxe[-tof[u]],w); } if(tof[v]<0){ mxe[-tof[v]]=min(mxe[-tof[v]],w); } u=fa[u],v=fa[v]; } } for(int u:dk){ if(tof[u]<0){ tof[u]=-mxe[-tof[u]]; } } int res=0; for(int u:dk){ if(tof[u]<0){ res-=tof[u]*sz[u]; } } ans=max(ans,res); } namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } signed main(){ // cout<<cplx::usdmem(); ios::sync_with_stdio(0),cin.tie(0); // freopen("toll5.in","r",stdin); cin>>n>>m>>k; // cout<<n<<" "<<m<<" "<<k<<"\n"; for(int i=1;i<=m;i++){ cin>>em[i].u>>em[i].v>>em[i].w; } init(),sort(em+1,em+m+1); for(int i=1;i<=k;i++){ cin>>ek[i].u>>ek[i].v; merge(ek[i].u,ek[i].v); } for(int i=1;i<=n;i++){ cin>>a[i]; } for(int i=1,u,v;i<=m;i++){ u=em[i].u,v=em[i].v; if(find(u)!=find(v)){ merge(u,v); vis[i]=1; } } init(); for(int i=1,u,v;i<=m;i++){ if(vis[i]){ u=find(em[i].u); v=find(em[i].v); if(sz[u]>sz[v]){ sz[u]+=sz[v]; a[u]+=a[v]; fa[v]=u; } else{ sz[v]+=sz[u]; a[v]+=a[u]; fa[u]=v; } } } for(int i=1;i<=n;i++){ bel[i]=find(i); if(bel[i]==i){ dk.pb(i); } } init(); for(int i=1,u,v,w;i<=m;i++){ u=bel[em[i].u],v=bel[em[i].v],w=em[i].w; if(find(u)!=find(v)){ et.pb((edge){u,v,w}); merge(u,v); } } for(int S=0;S<1<<k;S++){ // cout<<bitset<15>(S)<<"\n"; solve(S); } cout<<ans; return 0; } } signed main(){return asbt::main();} /* 100000 299989 12 */

H. [蓝桥杯 2015 省 A] 灾后重建

建 kruskal 重构树,问题转化为求 \(\operatorname{lca}\)
根号分治,对于 \(k>\sqrt{n}\) 暴力求,剩下的对于剩余系去建线段树区间查询。时间复杂度 \(O(q\sqrt{n})\)
然而空间十分爆炸。原因是对于每个剩余系都建线段树是无法接受的。因此将查询离线下来,就可以只建一棵线段树,不断重构即可。然而原题数据没有卡这一点。
空间不正确的代码:

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
  • 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
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
#include<bits/stdc++.h> #define ll long long #define il inline #define pb push_back #define lwrb lower_bound #define uprb upper_bound using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=2e5+5,maxb=250; int n,m,q,blen,fa[maxn],zhi[maxn]; vector<int> e[maxn]; il int find(int x){ return x!=fa[x]?fa[x]=find(fa[x]):x; } struct edge{ int u,v,w; il bool operator<(const edge &x)const{ return w<x.w; } }a[maxn]; int dfn[maxn],oula[maxn],idx[maxn],cnt; il void dfs(int u){ dfn[u]=++cnt; oula[cnt]=cnt; idx[cnt]=u; for(int v:e[u]){ if(v!=fa[u]){ dfs(v); oula[++cnt]=dfn[u]; } } } struct{ int Log[maxn],st[maxn][22]; il void init(){ for(int i=2;i<=cnt;i++){ Log[i]=Log[i>>1]+1; } for(int i=1;i<=cnt;i++){ st[i][0]=oula[i]; } for(int j=1;j<=Log[cnt];j++){ for(int i=1;i+(1<<j)-1<=cnt;i++){ st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); } } } il int query(int l,int r){ int tmp=Log[r-l+1]; return min(st[l][tmp],st[r-(1<<tmp)+1][tmp]); } }ST; il int lca(int u,int v){ if(dfn[u]>dfn[v]){ swap(u,v); } return idx[ST.query(dfn[u],dfn[v])]; } vector<int> syx[maxb][maxb]; struct stree{ #define lid id<<1 #define rid id<<1|1 int num; vector<int> tr; il void pushup(int id){ tr[id]=lca(tr[lid],tr[rid]); } il void build(int id,int l,int r,int x,int y){ if(l>r){ return ; } if(l==r){ tr[id]=syx[x][y][l]; return ; } int mid=(l+r)>>1; build(lid,l,mid,x,y); build(rid,mid+1,r,x,y); pushup(id); } stree(int x=0,int y=0):num(syx[x][y].size()),tr(num<<2,0){ // puts("666"); build(1,0,num-1,x,y); } il int query(int id,int L,int R,int l,int r){ if(L>=l&&R<=r){ return tr[id]; } int mid=(L+R)>>1; if(r<=mid){ return query(lid,L,mid,l,r); } if(l>mid){ return query(rid,mid+1,R,l,r); } return lca(query(lid,L,mid,l,r),query(rid,mid+1,R,l,r)); } il int query(int id,int l,int r){ return query(1,0,num-1,l,r); } #undef lid #undef rid }SG[maxb][maxb]; bool vis[maxb][maxb]; namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ // freopen("H.in","r",stdin); // freopen("H.out","w",stdout); // cout<<cplx::usdmem(); ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m>>q; for(int i=1;i<=m;i++){ cin>>a[i].u>>a[i].v>>a[i].w; } sort(a+1,a+m+1); int tot=n; for(int i=1;i<=n<<1;i++){ fa[i]=i; } for(int i=1,u,v,w;i<=m;i++){ // puts("666"); u=a[i].u,v=a[i].v,w=a[i].w; u=find(u),v=find(v); if(u!=v){ fa[u]=fa[v]=++tot; zhi[tot]=w; e[tot].pb(u),e[tot].pb(v); } } dfs(tot),ST.init(); blen=sqrt(n); // cout<<blen<<"\n"; // bool begin; // int num=0; for(int i=1;i<=blen;i++){ for(int j=1;j<=n;j++){ syx[i][j%i].pb(j); // num++; } } // cout<<num<<"\n"; // bool end; // cout<<(&end-&begin)/1048576.0<<"\n"; while(q--){ int l,r,k,c; cin>>l>>r>>k>>c; if(k>blen){ int tmp=l; if(l%k<c){ tmp+=c-l%k; } else if(l%k>c){ tmp-=l%k-c; tmp+=k; } for(int i=tmp+k;i<=r;i+=k){ tmp=lca(tmp,i); } cout<<zhi[tmp]<<"\n"; continue; } if(!vis[k][c]){ SG[k][c]=stree(k,c); vis[k][c]=1; } cout<<zhi[SG[k][c].query(1,lwrb(syx[k][c].begin(),syx[k][c].end(),l)-syx[k][c].begin(),uprb(syx[k][c].begin(),syx[k][c].end(),r)-syx[k][c].begin()-1)]<<"\n"; } return 0; } } int main(){return asbt::main();}

离线卡空间的代码:

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
  • 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
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
#include<bits/stdc++.h> #define ll long long #define il inline #define pb push_back #define lwrb lower_bound #define uprb upper_bound using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=2e5+5,maxb=250; int n,m,q,blen,fa[maxn],zhi[maxn]; vector<int> e[maxn]; il int find(int x){ return x!=fa[x]?fa[x]=find(fa[x]):x; } struct edge{ int u,v,w; il bool operator<(const edge &x)const{ return w<x.w; } }a[maxn]; int dfn[maxn],oula[maxn],idx[maxn],cnt; il void dfs(int u){ dfn[u]=++cnt; oula[cnt]=cnt; idx[cnt]=u; for(int v:e[u]){ if(v!=fa[u]){ dfs(v); oula[++cnt]=dfn[u]; } } } struct{ int Log[maxn],st[maxn][22]; il void init(){ for(int i=2;i<=cnt;i++){ Log[i]=Log[i>>1]+1; } for(int i=1;i<=cnt;i++){ st[i][0]=oula[i]; } for(int j=1;j<=Log[cnt];j++){ for(int i=1;i+(1<<j)-1<=cnt;i++){ st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); } } } il int query(int l,int r){ int tmp=Log[r-l+1]; return min(st[l][tmp],st[r-(1<<tmp)+1][tmp]); } }ST; il int lca(int u,int v){ if(dfn[u]>dfn[v]){ swap(u,v); } return idx[ST.query(dfn[u],dfn[v])]; } vector<int> syx[maxb][maxb]; struct stree{ #define lid id<<1 #define rid id<<1|1 int num,tr[maxn<<2]; il void pushup(int id){ tr[id]=lca(tr[lid],tr[rid]); } il void build(int id,int l,int r,int x,int y){ if(l>r){ return ; } if(l==r){ tr[id]=syx[x][y][l]; return ; } int mid=(l+r)>>1; build(lid,l,mid,x,y); build(rid,mid+1,r,x,y); pushup(id); } stree(int x=0,int y=0):num(syx[x][y].size()){ // puts("666"); build(1,0,num-1,x,y); } il int query(int id,int L,int R,int l,int r){ if(L>=l&&R<=r){ return tr[id]; } int mid=(L+R)>>1; if(r<=mid){ return query(lid,L,mid,l,r); } if(l>mid){ return query(rid,mid+1,R,l,r); } return lca(query(lid,L,mid,l,r),query(rid,mid+1,R,l,r)); } il int query(int id,int l,int r){ return query(1,0,num-1,l,r); } #undef lid #undef rid }SG; struct node{ int l,r,k,c,id; il bool operator<(const node &x)const{ return k<x.k||k==x.k&&c<x.c; } }wt[maxn]; int ans[maxn]; namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ // freopen("H.in","r",stdin); // freopen("H.out","w",stdout); // cout<<cplx::usdmem(); ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m>>q; for(int i=1;i<=m;i++){ cin>>a[i].u>>a[i].v>>a[i].w; } sort(a+1,a+m+1); int tot=n; for(int i=1;i<=n<<1;i++){ fa[i]=i; } for(int i=1,u,v,w;i<=m;i++){ // puts("666"); u=a[i].u,v=a[i].v,w=a[i].w; u=find(u),v=find(v); if(u!=v){ fa[u]=fa[v]=++tot; zhi[tot]=w; e[tot].pb(u),e[tot].pb(v); } } dfs(tot),ST.init(); blen=sqrt(n); // cout<<blen<<"\n"; // bool begin; // int num=0; for(int i=1;i<=blen;i++){ for(int j=1;j<=n;j++){ syx[i][j%i].pb(j); // num++; } } // cout<<num<<"\n"; // bool end; // cout<<(&end-&begin)/1048576.0<<"\n"; for(int i=1;i<=q;i++){ cin>>wt[i].l>>wt[i].r>>wt[i].k>>wt[i].c; wt[i].id=i; } sort(wt+1,wt+q+1); for(int i=1;i<=q;i++){ int l=wt[i].l,r=wt[i].r,k=wt[i].k,c=wt[i].c; if(k>blen){ int tmp=l; if(l%k<c){ tmp+=c-l%k; } else if(l%k>c){ tmp-=l%k-c; tmp+=k; } for(int i=tmp+k;i<=r;i+=k){ tmp=lca(tmp,i); } ans[wt[i].id]=zhi[tmp]; continue; } if(k!=wt[i-1].k||c!=wt[i-1].c){ SG=stree(k,c); } ans[wt[i].id]=zhi[SG.query(1,lwrb(syx[k][c].begin(),syx[k][c].end(),l)-syx[k][c].begin(),uprb(syx[k][c].begin(),syx[k][c].end(),r)-syx[k][c].begin()-1)]; } for(int i=1;i<=q;i++){ cout<<ans[i]<<"\n"; } return 0; } } int main(){return asbt::main();}

I. Mathematics Curriculum

子段最大值个数即为这个点在大根笛卡尔树中的深度。考虑建树过程即可证明。
\(f_{i,j,k}\) 表示 \(i\) 个点的笛卡尔树,其中 \(i\) 是最大的点,第 \(k\) 层有 \(j\) 个点。枚举左右子树即可得到方程:

\[f_{i,j,k}=\sum_{x=0}^{i-1}\sum_{y=0}^{j}{i-1\choose{x}}\times f_{x,y,k-1}\times f_{i-x-1,j-y,k-1} \]

边界条件 \(f_{i,1,1}=i!\)。转移是 \(O(n^5)\) 的,考虑优化。

  • 对于 \(k>i\),只有 \(f_{i,0,k}=i!\),其它值都为 \(0\)
  • 三个乘数都不为 \(0\) 时转移才有效。

于是就可以通过。

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
#include<bits/stdc++.h> #define ll long long #define il inline using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=105; int n,m,num,mod; int fac[maxn],C[maxn][maxn]; int f[maxn][maxn][maxn]; namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ // freopen("I.in","r",stdin); ios::sync_with_stdio(0),cin.tie(0); cin>>n>>m>>num>>mod; fac[0]=C[0][0]=1; for(int i=1;i<=n;i++){ fac[i]=fac[i-1]*1ll*i%mod; 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<=m;i++){ f[0][0][i]=1; } f[1][0][0]=f[1][1][1]=1; for(int i=2;i<=m;i++){ f[1][0][i]=1; } for(int i=2;i<=n;i++){ f[i][1][1]=fac[i]; for(int k=2;k<=m;k++){ if(k>i){ f[i][0][k]=fac[i]; continue; } for(int j=0;j<=i;j++){ for(int x=0;x<i;x++){ if(!C[i-1][x]){ continue; } for(int y=0;y<=j;y++){ if(f[x][y][k-1]&&f[i-1-x][j-y][k-1]){ (f[i][j][k]+=C[i-1][x]*1ll*f[x][y][k-1]%mod*f[i-1-x][j-y][k-1]%mod)%=mod; } } } } } } cout<<f[n][num][m]; return 0; } } int main(){return asbt::main();}

J. 「OICon-02」maxiMINImax

用单调栈可以求出以 \(a_i\) 为最小值和最大值的区间个数 \(qmn_i\)\(qmx_i\)
从小到大枚举第二个区间的最小值,记 \(p_i\) 表示 \(i\) 的位置,则对于 \(i\) 的答案即为:

\[\sum_{j=1}^{p_i-1}\sum_{k=p_i+1}^{n}{qmn_{p_i}qmx_jqmx_k(i-a_j)(i-a_k)} \]

简单推式子后得到:
\( qmn_{p_i}(\sum{qmx_j}\sum{qmx_k}i^2-\sum{qmx_ja_j}\sum{qmx_k}i-\sum{qmx_j}\sum{qmx_ka_k}i+\sum{qmx_ja_j}\sum{qmx_ka_k}) \)
于是开两棵树状数组维护 \(qmx_j\)\(qmx_ja_j\) 就好了。时间复杂度 \(O(n\log n)\)

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
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
#include<bits/stdc++.h> #define ll long long #define il inline using namespace std; namespace asbt{ namespace cplx{bool begin;} const int maxn=1e6+5,mod=9712176; int n,a[maxn],p[maxn],zhan[maxn]; int lmn[maxn],rmn[maxn]; int lmx[maxn],rmx[maxn]; int qmn[maxn],qmx[maxn]; struct{ int tr[maxn]; il int lowbit(int x){ return x&-x; } il void upd(int p,int v){ for(;p<=n;p+=lowbit(p)){ (tr[p]+=v)%=mod; } } il int query(int p){ int res=0; for(;p;p-=lowbit(p)){ (res+=tr[p])%=mod; } return res; } il int query(int l,int r){ return (query(r)-query(l-1)+mod)%mod; } }F1,F2; namespace cplx{ bool end; il double usdmem(){return (&begin-&end)/1048576.0;} } int main(){ ios::sync_with_stdio(0),cin.tie(0); cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; p[a[i]]=i; } for(int i=1,top=0;i<=n;i++){ while(top&&a[zhan[top]]>a[i]){ top--; } lmn[i]=zhan[top]; zhan[++top]=i; } for(int i=1,top=0;i<=n;i++){ while(top&&a[zhan[top]]<a[i]){ top--; } lmx[i]=zhan[top]; zhan[++top]=i; } zhan[0]=n+1; for(int i=n,top=0;i;i--){ while(top&&a[zhan[top]]>a[i]){ top--; } rmn[i]=zhan[top]; zhan[++top]=i; } for(int i=n,top=0;i;i--){ while(top&&a[zhan[top]]<a[i]){ top--; } rmx[i]=zhan[top]; zhan[++top]=i; } for(int i=1;i<=n;i++){ qmn[i]=(i-lmn[i])*1ll*(rmn[i]-i)%mod; qmx[i]=(i-lmx[i])*1ll*(rmx[i]-i)%mod; } int ans=0; for(int i=1,xj,xaj,xk,xak;i<=n;i++){ xj=F1.query(1,p[i]-1); xaj=F2.query(1,p[i]-1); xk=F1.query(p[i]+1,n); xak=F2.query(p[i]+1,n); (ans+=qmn[p[i]]*1ll*(xj*1ll*xk%mod*i%mod*i%mod-xaj*1ll*xk%mod*i%mod-xj*1ll*xak%mod*i%mod+xaj*1ll*xak%mod)%mod)%=mod; (ans+=mod)%=mod; F1.upd(p[i],qmx[p[i]]); F2.upd(p[i],qmx[p[i]]*1ll*i%mod); } cout<<ans; return 0; } } int main(){return asbt::main();}
posted @   zhangxy__hp  阅读(35)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开