https://www.cnblogs.com/zzqc/p/6855913.html
边分治建图模板
边分治最怕菊花图
所以要对原图进行改造
https://blog.csdn.net/make_it_for_good/article/details/52194466
需要用vector存储邻接表
方法妙不可言
0边是虚边,1边是实边
1 | for(int x=1;x<=n;x++) { |
[WC2010]重建计划
给定一棵n个点的树,每条边有权值。
求一条链,这条链包含的边数在L和U之间,且平均边权最大。
类似于分数规划问题,转换为二分答案
将每条边减去二分值,判断是否有权值和大于等于0的链
处理出根到其中一个子树中每种深度的最大权值和
另外记录一下根到已知子树中每种深度的最大权值和
就变成了定区间求最值,用单调队列即可
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#include<iostream>
#include<cstdio>
using namespace std;
int head[100005],vet[200005],nxt[200005],w[200005],num;
bool vis[100005];
int sz[100005],mxs[100005],root,sz1,sz2;
int que[100005];
double mxr,ans,mid,d1[100005],d2[100005];
int L,U,n;
inline int read() {
char ch=0;int sum=0;
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') sum=sum*10+ch-'0',ch=getchar();
return sum;
}
inline void addedge(int x,int y,int c) {
num++;vet[num]=y;nxt[num]=head[x];w[num]=c;head[x]=num;
num++;vet[num]=x;nxt[num]=head[y];w[num]=c;head[y]=num;
}
void getdepth(int x,int ff,int dep,double sum) {
if (dep>sz2) {
sz2=dep;
d2[dep]=-1e11;
}
d2[dep]=max(d2[dep],sum);
for (int e=head[x];e;e=nxt[e]) {
if (vet[e]==ff||vis[vet[e]]) continue;
getdepth(vet[e],x,dep+1,sum+w[e]-mid);
}
}
inline double calc() {
int h=1,t=0;
int i=min(U,sz2),j=0;
double mx=-1e11;
for (;i;i--) {
while (j<=sz1&&i+j<=U) {
while (h<=t&&d1[que[t]]<=d1[j]) t--;
que[++t]=j;
j++;
}
while (h<=t&&que[h]+i<L) h++;
if (h>t) return mx;
mx=max(mx,d1[que[h]]+d2[i]);
if (mx>=0) return mx;
}
return mx;
}
inline bool check(int x) {
sz1=sz2=0;
for (int e=head[x];e;e=nxt[e]) {
int v=vet[e];
if (vis[v]) continue;
getdepth(v,x,1,w[e]-mid);
if (calc()>=0) return true;
for (int i=1;i<=sz2;i++) {
if (i>sz1) d1[i]=-1e11;
d1[i]=max(d1[i],d2[i]);
d2[i]=-1e11;
}
sz1=max(sz1,sz2);
sz2=0;
}
return false;
}
void bs(int x) {
double l=ans,r=mxr;
while (r-l>=0.0001) {
mid=(l+r)/2;
if (check(x)) l=mid;
else r=mid;
}
ans=max(ans,l);
}
void getroot(int x,int ff,int tt) {
sz[x]=1;mxs[x]=0;
for (int e=head[x];e;e=nxt[e]) {
int v=vet[e];
if (vis[v]||v==ff) continue;
getroot(v,x,tt);
sz[x]+=sz[v];
mxs[x]=max(mxs[x],sz[v]);
}
mxs[x]=max(mxs[x],tt-sz[x]);
if (mxs[root]>mxs[x]) root=x;
}
void solve(int x,int tot) {
bs(x);
vis[x]=1;
for (int e=head[x];e;e=nxt[e]) {
if (vis[vet[e]]) continue;
int tt=sz[vet[e]];root=0;
if (tt>sz[x]) tt=tot-sz[x];
getroot(vet[e],x,tt);
solve(root,tt);
}
}
int main() {
n=read();L=read();U=read();
for (int i=1;i<n;i++) {
int x=read(),y=read(),c=read();
addedge(x,y,c);
mxr=max(mxr,(double)c);
}
for (int i=1;i<=n;i++) d1[i]=d2[i]=-1e11;
mxs[0]=1e9;
getroot(1,0,n);
solve(root,n);
printf("%.3lf",ans);
return 0;
}
蒟蒻用dfs算距离
dalao用bfs算距离
http://hzwer.com/5362.html
蒟蒻的常数肯定比dalao大 orz
点分治模板
模板题:https://www.luogu.org/problemnew/show/P3806
给定一棵有n个点的树,m次询问树上距离为k的点对是否存在。k很大,m较小。
1.暴力做法,可能变成n^2 (756ms) 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#include<iostream>
#include<cstdio>
using namespace std;
const int maxK=10000001;
int head[10005],vet[20005],nxt[20005],w[20005],num;
int sz[10005],mxs[10005],root;
int d[10005],top,found[10000005];
bool vis[10005];
int n,m;
void addedge(int x,int y,int c) {
num++;vet[num]=y;nxt[num]=head[x];w[num]=c;head[x]=num;
num++;vet[num]=x;nxt[num]=head[y];w[num]=c;head[y]=num;
}
void getroot(int x,int ff,int tt) {
sz[x]=1;mxs[x]=0;
for (int e=head[x];e;e=nxt[e]) {
int v=vet[e];
if (v==ff || vis[v]) continue;
getroot(v,x,tt);
sz[x]+=sz[v];
mxs[x]=max(mxs[x],sz[v]);
}
mxs[x]=max(mxs[x],tt-sz[x]);
if (mxs[x]<mxs[root]) root=x;
}
void getdepth(int x,int ff,int depth) {
d[++top]=depth;
for (int e=head[x];e;e=nxt[e]) {
if (vet[e]==ff || vis[vet[e]]) continue;
getdepth(vet[e],x,depth+w[e]);
}
}
void cal(int x,int typ,int val) {
top=0;
getdepth(x,0,0);
for (int i=1;i<=top;i++)
for (int j=1;j<=top;j++) {
if (typ && d[i]+d[j]<=maxK) found[d[i]+d[j]]++;
else if (d[i]+d[j]+val<=maxK) found[d[i]+d[j]+val]--;
}
}
void solve(int x,int tot) {
cal(x,1,0);
vis[x]=true;
for (int e=head[x];e;e=nxt[e]) {
if (!vis[vet[e]]) {
cal(vet[e],0,w[e]*2);//刷答案的地方视具体题目而定
root=0;
int tt=sz[vet[e]];
if (tt>sz[x]) tt=tot-sz[x];//主流写法没有这一句。这样写保证不退化,运行速度略微提升。
getroot(vet[e],x,tt);
solve(root,tt);
}
}
}
int main() {
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++) {
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
addedge(x,y,c);
}
mxs[0]=(n<<1);root=0;
getroot(1,0,n);
solve(root,n);
while (m--) {
int x;
scanf("%d",&x);
if (found[x]) printf("AYE\n");
else printf("NAY\n");
}
return 0;
}
其实嘛
我觉得常规写法子树的总节点数tot有问题
如果子树是上一次root的父亲
size[to]是不能直接用的,需要-size[oldroot]
实验证明主流解法的确分得不太均匀
不过因为众多子树中只有一个会出错,问题也不大
另附主流写法核心代码
1 | void getroot(int x,int ff) { |
2.优化,枚举询问 (16ms) 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#include<iostream>
#include<cstdio>
using namespace std;
const int maxK=10000001;
int head[10005],vet[20005],nxt[20005],w[20005],num;
int sz[10005],mxs[10005],root;
int d[10005],q[105],xg[10005];
bool vis[10005],found[10000005],ans[105];
int n,m;
inline int read() {
char ch=0;int sum=0;
while (ch>'9'||ch<'0') ch=getchar();
while (ch>='0'&&ch<='9') sum=sum*10+ch-'0',ch=getchar();
return sum;
}
inline void addedge(int x,int y,int c) {
num++;vet[num]=y;nxt[num]=head[x];w[num]=c;head[x]=num;
num++;vet[num]=x;nxt[num]=head[y];w[num]=c;head[y]=num;
}
void getroot(int x,int ff,int tt) {
sz[x]=1;mxs[x]=0;
for (int e=head[x];e;e=nxt[e]) {
int v=vet[e];
if (v==ff || vis[v]) continue;
getroot(v,x,tt);
sz[x]+=sz[v];
mxs[x]=max(mxs[x],sz[v]);
}
mxs[x]=max(mxs[x],tt-sz[x]);
if (mxs[x]<mxs[root]) root=x;
}
void getdepth(int x,int ff,int depth) {
d[++d[0]]=depth;
for (int e=head[x];e;e=nxt[e]) {
if (vet[e]==ff || vis[vet[e]]) continue;
getdepth(vet[e],x,depth+w[e]);
}
}
inline void calc(int x,int val) {
d[0]=0;
getdepth(x,0,val);
for (int i=1;i<=d[0];i++)
for (int j=1;j<=m;j++)
if (q[j]>=d[i]) ans[j]|=found[q[j]-d[i]];
for (int i=1;i<=d[0];i++) found[d[i]]=1,xg[++xg[0]]=d[i];
}
void solve(int x,int tot) {
vis[x]=true;xg[0]=0;found[0]=1;
for (int e=head[x];e;e=nxt[e]) {
if (!vis[vet[e]]) calc(vet[e],w[e]);
}
for (int i=1;i<=xg[0];i++) found[xg[i]]=0;
for (int e=head[x];e;e=nxt[e]) {
if (!vis[vet[e]]) {
root=0;
int tt=sz[vet[e]];
if (tt>sz[x]) tt=tot-sz[x];
getroot(vet[e],x,tt);
solve(root,tt);
}
}
}
int main() {
n=read();m=read();
for (int i=1;i<n;i++) {
int x=read(),y=read(),c=read();
addedge(x,y,c);
}
for (int i=1;i<=m;i++) q[i]=read();
mxs[0]=(n<<1);root=0;
getroot(1,0,n);
solve(root,n);
for (int i=1;i<=m;i++) {
if (ans[i]) printf("AYE\n");
else printf("NAY\n");
}
return 0;
}
主席树模板
P3834
【模板】可持久化线段树 1(主席树)
查询区间[l,r]内的第k小值
1 | #include<iostream> |
逆元模板
一、扩展欧几里得
二、欧拉定理
三、线性求逆
原理:http://blog.miskcoo.com/2014/09/linear-find-all-invert
四、代码汇总
1 | #include<iostream> |
树链剖分模板
原理:http://www.cnblogs.com/zwfymqz/p/8094500.html
模板题:https://www.luogu.org/problemnew/show/P3384
操作1: 格式: 1 x y z
表示将树从x到y结点最短路径上所有节点的值都加上z
操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和
操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z
操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和
1 | #include<iostream> |
据说n特别大的时候,需要使用bfs防爆栈
FFT模板
从多项式乘法到快速傅里叶变换,Miskcoo
FFT 学习笔记,Menci
1 | #pragma GCC optimize(2) |
回文子串Manacher算法模板
r[i]是处理后每个字符的回文半径
r[i]-1即为处理前回文子串长度
P3805
【模板】manacher算法
1 | #include<iostream> |












splay模板
https://www.luogu.org/problemnew/show/P4146
3种操作:
1.将[L,R]这个区间内的所有数加上V。
2.将[L,R]这个区间翻转,比如1 2 3 4变成4 3 2 1。
3.求[L,R]这个区间中的最大值。
由于交换两个子树必定会改变BST的性质
所以不能用key值做find
只能用size表示数组元素下标
查询下标为k的元素用find_Kth
1 | #include<iostream> |
其他操作
1 | inline void ins(int c) { |
还有合并,拆分等操作未写出