算法竞赛模板
刘皓铭

数据结构

线段树合并

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 maxn 200010
#define maxm 20000010
#define all 100000
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,tot;
int ans[maxn],f[maxn][19],dep[maxn],rt[maxn],mx[maxm],id[maxm],ls[maxm],rs[maxm];
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]={to,head[from]},head[from]=edge_cnt;
}
void dfs_pre(int x,int fa)
{
dep[x]=dep[f[x][0]=fa]+1;
for(int i=1;i<=16;++i) f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fa) continue;
dfs_pre(y,x);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=16;i>=0;--i)
if(f[x][i]&&dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y) return x;
for(int i=16;i>=0;--i)
if(f[x][i]&&f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
void pushup(int cur)
{
if(mx[ls[cur]]>=mx[rs[cur]]) mx[cur]=mx[ls[cur]],id[cur]=id[ls[cur]];
else mx[cur]=mx[rs[cur]],id[cur]=id[rs[cur]];
}
void modify(int l,int r,int pos,int v,int &cur)
{
if(!cur) cur=++tot;
if(l==r)
{
mx[cur]+=v,id[cur]=l;
return;
}
if(pos<=mid) modify(l,mid,pos,v,ls[cur]);
else modify(mid+1,r,pos,v,rs[cur]);
pushup(cur);
}
int merge(int x,int y,int l,int r)
{
if(!x||!y) return x+y;
if(l==r)
{
mx[x]+=mx[y];
return x;
}
ls[x]=merge(ls[x],ls[y],l,mid);
rs[x]=merge(rs[x],rs[y],mid+1,r);
pushup(x);
return x;
}
void dfs_ans(int x)
{
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==f[x][0]) continue;
dfs_ans(y),rt[x]=merge(rt[x],rt[y],1,all);
}
ans[x]=mx[rt[x]]?id[rt[x]]:0;
}
int main()
{
read(n),read(m);
for(int i=1;i<n;++i)
{
int x,y;
read(x),read(y);
add(x,y),add(y,x);
}
dfs_pre(1,0);
for(int i=1;i<=m;++i)
{
int x,y,v,anc;
read(x),read(y),read(v),anc=lca(x,y);
modify(1,all,v,1,rt[x]),modify(1,all,v,1,rt[y]);
modify(1,all,v,-1,rt[anc]),modify(1,all,v,-1,rt[f[anc][0]]);
}
dfs_ans(1);
for(int i=1;i<=n;++i) printf("%d\n",ans[i]);
return 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
#include<bits/stdc++.h>
#define maxn 80000010
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,tot,num=1;
int rt[maxn],ls[maxn],rs[maxn];
ll cnt[maxn];
void pushup(int cur)
{
cnt[cur]=cnt[ls[cur]]+cnt[rs[cur]];
}
void modify(int l,int r,int pos,ll v,int &cur)
{
if(!cur) cur=++tot;
if(l==r)
{
cnt[cur]+=v;
return;
}
if(pos<=mid) modify(l,mid,pos,v,ls[cur]);
else modify(mid+1,r,pos,v,rs[cur]);
pushup(cur);
}
ll query(int L,int R,int l,int r,int cur)
{
if(!cur) return 0;
if(L<=l&&R>=r) return cnt[cur];
ll v=0;
if(L<=mid) v+=query(L,R,l,mid,ls[cur]);
if(R>mid) v+=query(L,R,mid+1,r,rs[cur]);
return v;
}
int kth(int l,int r,ll k,int cur)
{
if(l==r) return l;
if(cnt[ls[cur]]>=k) return kth(l,mid,k,ls[cur]);
else return kth(mid+1,r,k-cnt[ls[cur]],rs[cur]);
}
int merge(int x,int y)
{
if(!x||!y) return x+y;
int p=++tot;
cnt[p]=cnt[x]+cnt[y];
ls[p]=merge(ls[x],ls[y]);
rs[p]=merge(rs[x],rs[y]);
return p;
}
void split(int L,int R,int l,int r,int &x,int &y)
{
if(!x) return;
if(L<=l&&R>=r)
{
y=x,x=0;
return;
}
y=++tot;
if(L<=mid) split(L,R,l,mid,ls[x],ls[y]);
if(R>mid) split(L,R,mid+1,r,rs[x],rs[y]);
pushup(x),pushup(y);
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i)
{
ll x;
read(x),modify(1,n,i,x,rt[1]);
}
while(m--)
{
int opt,p;
ll x,y,k;
read(opt),read(p);
if(opt==1||opt==4) read(k);
else read(x),read(y);
if(opt==0) split(x,y,1,n,rt[p],rt[++num]);
if(opt==1) rt[p]=merge(rt[p],rt[k]);
if(opt==2) modify(1,n,y,x,rt[p]);
if(opt==3) printf("%lld\n",query(x,y,1,n,rt[p]));
if(opt==4)
{
if(cnt[rt[p]]<k) puts("-1");
else printf("%d\n",kth(1,n,k,rt[p]));
}
}
return 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
111
112
113
114
115
116
117
118
119
120
121
#include<bits/stdc++.h>
#define maxn 2000010
#define inf 20000000000
#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,root=1;
ll a[maxn],ma[maxn],hma[maxn],se[maxn],cnt[maxn],sum[maxn];
ll tag[maxn],htag[maxn],add[maxn],hadd[maxn];
void pushup(int cur)
{
sum[cur]=sum[ls]+sum[rs],ma[cur]=max(ma[ls],ma[rs]),hma[cur]=max(hma[ls],hma[rs]);
if(ma[ls]==ma[rs]) se[cur]=max(se[ls],se[rs]),cnt[cur]=cnt[ls]+cnt[rs];
else if(ma[ls]>ma[rs]) se[cur]=max(se[ls],ma[rs]),cnt[cur]=cnt[ls];
else se[cur]=max(ma[ls],se[rs]),cnt[cur]=cnt[rs];
}
void pushtag(int cur,int l,int r,ll v,ll hv,ll w,ll hw)
{
sum[cur]+=v*cnt[cur]+w*(r-l+1-cnt[cur]);
hma[cur]=max(hma[cur],ma[cur]+hv),ma[cur]+=v;
htag[cur]=max(htag[cur],tag[cur]+hv),tag[cur]+=v;
hadd[cur]=max(hadd[cur],add[cur]+hw),add[cur]+=w;
if(se[cur]!=-inf) se[cur]+=w;
}
void pushdown(int cur,int l,int r)
{
ll maxv=max(ma[ls],ma[rs]);
if(ma[ls]==maxv) pushtag(ls,l,mid,tag[cur],htag[cur],add[cur],hadd[cur]);
else pushtag(ls,l,mid,add[cur],hadd[cur],add[cur],hadd[cur]);
if(ma[rs]==maxv) pushtag(rs,mid+1,r,tag[cur],htag[cur],add[cur],hadd[cur]);
else pushtag(rs,mid+1,r,add[cur],hadd[cur],add[cur],hadd[cur]);
tag[cur]=htag[cur]=add[cur]=hadd[cur]=0;
}
void build(int l,int r,int cur)
{
if(l==r)
{
sum[cur]=ma[cur]=hma[cur]=a[l],se[cur]=-inf,cnt[cur]=1;
return;
}
build(l,mid,ls),build(mid+1,r,rs),pushup(cur);
}
void modify_add(int L,int R,int l,int r,ll v,int cur)
{
if(L<=l&&R>=r)
{
pushtag(cur,l,r,v,v,v,v);
return;
}
pushdown(cur,l,r);
if(L<=mid) modify_add(L,R,l,mid,v,ls);
if(R>mid) modify_add(L,R,mid+1,r,v,rs);
pushup(cur);
}
void modify_min(int L,int R,int l,int r,ll v,int cur)
{
if(v>=ma[cur]) return;
if(L<=l&&R>=r&&v>se[cur])
{
pushtag(cur,l,r,v-ma[cur],v-ma[cur],0,0);
return;
}
pushdown(cur,l,r);
if(L<=mid) modify_min(L,R,l,mid,v,ls);
if(R>mid) modify_min(L,R,mid+1,r,v,rs);
pushup(cur);
}
ll query_sum(int L,int R,int l,int r,int cur)
{
if(L<=l&&R>=r) return sum[cur];
pushdown(cur,l,r);
ll ans=0;
if(L<=mid) ans+=query_sum(L,R,l,mid,ls);
if(R>mid) ans+=query_sum(L,R,mid+1,r,rs);
return ans;
}
ll query_max(int L,int R,int l,int r,int cur)
{
if(L<=l&&R>=r) return ma[cur];
pushdown(cur,l,r);
ll ans=-inf;
if(L<=mid) ans=max(ans,query_max(L,R,l,mid,ls));
if(R>mid) ans=max(ans,query_max(L,R,mid+1,r,rs));
return ans;
}
ll query_his(int L,int R,int l,int r,int cur)
{
if(L<=l&&R>=r) return hma[cur];
pushdown(cur,l,r);
ll ans=-inf;
if(L<=mid) ans=max(ans,query_his(L,R,l,mid,ls));
if(R>mid) ans=max(ans,query_his(L,R,mid+1,r,rs));
return ans;
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i) read(a[i]);
build(1,n,root);
while(m--)
{
int opt,l,r,v;
read(opt),read(l),read(r);
if(opt==1||opt==2) read(v);
if(opt==1) modify_add(l,r,1,n,v,root);
if(opt==2) modify_min(l,r,1,n,v,root);
if(opt==3) printf("%lld\n",query_sum(l,r,1,n,root));
if(opt==4) printf("%lld\n",query_max(l,r,1,n,root));
if(opt==5) printf("%lld\n",query_his(l,r,1,n,root));
}
return 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
void build_in(int L,int R,int &cur)
{
cur=++tree_cnt;
if(L==R)
{
in_num[L]=cur;
return;
}
int mid=(L+R)>>1;
build_in(L,mid,ls[cur]);
build_in(mid+1,R,rs[cur]);
add(ls[cur],cur,0),add(rs[cur],cur,0);
}
void build_out(int L,int R,int &cur)
{
cur=++tree_cnt;
if(L==R)
{
out_num[L]=cur;
return;
}
int mid=(L+R)>>1;
build_out(L,mid,ls[cur]);
build_out(mid+1,R,rs[cur]);
add(cur,ls[cur],0),add(cur,rs[cur],0);
}
void modify_in(int L,int R,int l,int r,int pos,int val,int &cur)
{
if(L<=l&&R>=r)
{
add(cur,pos,val);
return;
}
int mid=(l+r)>>1;
if(L<=mid) modify_in(L,R,l,mid,pos,val,ls[cur]);
if(R>mid) modify_in(L,R,mid+1,r,pos,val,rs[cur]);
}
void modify_out(int L,int R,int l,int r,int pos,int val,int &cur)
{
if(L<=l&&R>=r)
{
add(pos,cur,val);
return;
}
int mid=(l+r)>>1;
if(L<=mid) modify_out(L,R,l,mid,pos,val,ls[cur]);
if(R>mid) modify_out(L,R,mid+1,r,pos,val,rs[cur]);
}

线段树分治

删去无向连通图一些边后,询问原图是否连通。将删边转化为边存在的区间,用带权并查集维护连通块大小。

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
#include<bits/stdc++.h>
#define maxn 200010
#define maxm 400010
#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,k,top;
int fa[maxn],siz[maxn],pre[maxn];
bool ans[maxn];
vector<int> ve[maxm];
struct node
{
int x,y;
node(int a=0,int b=0)
{
x=a,y=b;
}
}e[maxn],st[maxn];
int find(int x)
{
return fa[x]==x?x:find(fa[x]);
}
void merge(int x,int y)
{
x=find(x),y=find(y);
if(x==y) return;
if(siz[x]<siz[y]) swap(x,y);
st[++top]=node(x,y),fa[y]=x,siz[x]+=siz[y];
}
void insert(int L,int R,int l,int r,int id,int cur)
{
if(L<=l&&R>=r)
{
ve[cur].push_back(id);
return;
}
if(L<=mid) insert(L,R,l,mid,id,ls);
if(R>mid) insert(L,R,mid+1,r,id,rs);
}
void del(int id)
{
int x=st[id].x,y=st[id].y;
fa[y]=y,siz[x]-=siz[y];
}
void dfs(int l,int r,int cur)
{
int now=top;
for(int i=0;i<ve[cur].size();++i)
merge(e[ve[cur][i]].x,e[ve[cur][i]].y);
if(l==r) ans[l]=siz[find(1)]==n;
else dfs(l,mid,ls),dfs(mid+1,r,rs);
while(top>now) del(top--);
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i) siz[fa[i]=i]=1;
for(int i=1;i<=m;++i) read(e[i].x),read(e[i].y),pre[i]=1;
read(k);
for(int i=1;i<=k;++i)
{
int c;
read(c);
while(c--)
{
int id;
read(id);
if(pre[id]<i) insert(pre[id],i-1,1,k,id,1);
pre[id]=i+1;
}
}
for(int i=1;i<=m;++i)
if(pre[i]<=k)
insert(pre[i],k,1,k,i,1);
dfs(1,k,1);
for(int i=1;i<=k;++i) puts(ans[i]?"Connected":"Disconnected");
return 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
#include<bits/stdc++.h>
#define maxn 100010
#define maxm 4000010
#define all 1000000
#define inf 1000000000000
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,tot,root;
int ls[maxm],rs[maxm],num[maxm];
ll h[maxn],s[maxn],k[maxn],b[maxn],f[maxn];
ll y(int id,ll x)
{
return k[id]*x+b[id];
}
void insert(int l,int r,int id,int &cur)
{
if(!cur)
{
num[cur=++tot]=id;
return;
}
if(y(id,mid)<y(num[cur],mid)) swap(id,num[cur]);
if(y(id,l)>=y(num[cur],l)&&y(id,r)>=y(num[cur],r)) return;
if(y(id,l)<y(num[cur],l)) insert(l,mid,id,ls[cur]);
else insert(mid+1,r,id,rs[cur]);
}
ll query(int l,int r,int x,int cur)
{
if(!cur) return inf;
ll v=y(num[cur],x);
if(x<=mid) v=min(v,query(l,mid,x,ls[cur]));
else v=min(v,query(mid+1,r,x,rs[cur]));
return v;
}
int main()
{
read(n);
for(int i=1;i<=n;++i) read(h[i]);
for(int i=1;i<=n;++i) read(s[i]),s[i]+=s[i-1];
for(int i=1;i<=n;++i)
{
if(i!=1) f[i]=query(0,all,h[i],root)+h[i]*h[i]+s[i-1];
k[i]=-2*h[i],b[i]=f[i]+h[i]*h[i]-s[i],insert(0,all,i,root);
}
printf("%lld",f[n]);
return 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
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
#include<bits/stdc++.h>
#define maxn 400010
#define ls (cur<<1)
#define rs (cur<<1|1)
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,rt,root=1,cnt;
ll p;
int siz[maxn],fa[maxn],dfn[maxn],top[maxn],son[maxn],dep[maxn],rev[maxn];
ll a[maxn],sum[maxn],tag[maxn];
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]={to,head[from]},head[from]=edge_cnt;
}
void pushup(int cur)
{
sum[cur]=(sum[ls]+sum[rs])%p;
}
void pushtag(int cur,int l,int r,ll v)
{
sum[cur]=(sum[cur]+v*(r-l+1)%p)%p,tag[cur]=(tag[cur]+v)%p;
}
void pushdown(int cur,int l,int r)
{
if(!tag[cur]) return;
pushtag(ls,l,mid,tag[cur]),pushtag(rs,mid+1,r,tag[cur]),tag[cur]=0;
}
void build(int l,int r,int cur)
{
if(l==r)
{
sum[cur]=a[rev[l]];
return;
}
build(l,mid,ls),build(mid+1,r,rs),pushup(cur);
}
void modify(int L,int R,int l,int r,int v,int cur)
{
if(L<=l&&R>=r)
{
pushtag(cur,l,r,v);
return;
}
pushdown(cur,l,r);
if(L<=mid) modify(L,R,l,mid,v,ls);
if(R>mid) modify(L,R,mid+1,r,v,rs);
pushup(cur);
}
ll query(int L,int R,int l,int r,int cur)
{
if(L<=l&&R>=r) return sum[cur];
ll v=0;
pushdown(cur,l,r);
if(L<=mid) v=(v+query(L,R,l,mid,ls))%p;
if(R>mid) v=(v+query(L,R,mid+1,r,rs))%p;
return v;
}
void update(int x,int y,int v)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
modify(dfn[top[x]],dfn[x],1,n,v,root),x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
modify(dfn[x],dfn[y],1,n,v,root);
}
ll ask(int x,int y)
{
ll v=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
v=(v+query(dfn[top[x]],dfn[x],1,n,root))%p,x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return (v+query(dfn[x],dfn[y],1,n,root))%p;
}
void dfs_son(int x,int fath)
{
fa[x]=fath,dep[x]=dep[fath]+1,siz[x]=1;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fath) continue;
dfs_son(y,x),siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
void dfs_chain(int x,int tp)
{
dfn[x]=++cnt,rev[cnt]=x,top[x]=tp;
if(son[x]) dfs_chain(son[x],tp);
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(dfn[y]) continue;
dfs_chain(y,y);
}
}
int main()
{
read(n),read(m),read(rt),read(p);
for(int i=1;i<=n;++i) read(a[i]);
for(int i=1;i<n;++i)
{
int x,y;
read(x),read(y);
add(x,y),add(y,x);
}
dfs_son(rt,0),dfs_chain(rt,rt),build(1,n,root);
while(m--)
{
int opt,x,y,v;
read(opt),read(x);
if(opt==1) read(y),read(v),update(x,y,v);
if(opt==2) read(y),printf("%lld\n",ask(x,y));
if(opt==3) read(v),modify(dfn[x],dfn[x]+siz[x]-1,1,n,v,root);
if(opt==4) printf("%lld\n",query(dfn[x],dfn[x]+siz[x]-1,1,n,root));
}
return 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
#include<bits/stdc++.h>
#define maxn 200010
#define maxm 10000010
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,tot,num;
int a[maxn],s[maxn],rt[maxn],cnt[maxm],ls[maxm],rs[maxm];
void modify(int l,int r,int pos,int &cur)
{
int x=++tot;
ls[x]=ls[cur],rs[x]=rs[cur],cnt[x]=cnt[cur]+1,cur=x;
if(l==r) return;
if(pos<=mid) modify(l,mid,pos,ls[cur]);
else modify(mid+1,r,pos,rs[cur]);
}
int query(int l,int r,int k,int x,int y)
{
if(l==r) return l;
if(k>cnt[ls[y]]-cnt[ls[x]]) return query(mid+1,r,k-cnt[ls[y]]+cnt[ls[x]],rs[x],rs[y]);
return query(l,mid,k,ls[x],ls[y]);
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i) read(a[i]),s[i]=a[i];
sort(s+1,s+n+1),num=unique(s+1,s+n+1)-s-1;
for(int i=1;i<=n;++i) rt[i]=rt[i-1],modify(1,num,lower_bound(s+1,s+num+1,a[i])-s,rt[i]);
while(m--)
{
int l,r,k;
read(l),read(r),read(k);
printf("%d\n",s[query(1,num,k,rt[l-1],rt[r])]);
}
return 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
#include<bits/stdc++.h>
#define maxn 10000010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag) x=-x;
}
int n,m,tree_cnt;
int ls[maxn],rs[maxn],root[maxn],a[maxn],fa[maxn],de[maxn];
void build(int L,int R,int &cur)
{
cur=++tree_cnt;
if(L==R)
{
fa[cur]=L;
return;
}
int mid=(L+R)>>1;
build(L,mid,ls[cur]);
build(mid+1,R,rs[cur]);
}
void merge(int L,int R,int pos,int fath,int pre,int &cur)
{
cur=++tree_cnt;
if(L==R)
{
fa[cur]=fath;
de[cur]=de[pre];
return;
}
ls[cur]=ls[pre],rs[cur]=rs[pre];
int mid=(L+R)>>1;
if(pos<=mid) merge(L,mid,pos,fath,ls[pre],ls[cur]);
if(pos>mid) merge(mid+1,R,pos,fath,rs[pre],rs[cur]);
}
int query(int L,int R,int pos,int cur)
{
if(L==R) return cur;
int mid=(L+R)>>1;
if(pos<=mid) return query(L,mid,pos,ls[cur]);
if(pos>mid) return query(mid+1,R,pos,rs[cur]);
}
void add(int L,int R,int pos,int cur)
{
if(L==R)
{
de[cur]++;
return;
}
int mid=(L+R)>>1;
if(pos<=mid) add(L,mid,pos,ls[cur]);
else add(mid+1,R,pos,rs[cur]);
}
int find(int pos,int cur)
{
int fath=query(1,n,pos,cur);
if(pos==fa[fath]) return fath;
return find(fa[fath],cur);
}
int main()
{
read(n),read(m);
build(1,n,root[0]);
for(int i=1;i<=m;i++)
{
int flag,a,b;
read(flag);
if(flag==1)
{
read(a),read(b);
root[i]=root[i-1];
int u=find(a,root[i]),v=find(b,root[i]);
if(fa[u]==fa[v]) continue;
if(de[u]>de[v]) swap(u,v);
merge(1,n,fa[u],fa[v],root[i-1],root[i]);
if(de[u]==de[v]) add(1,n,fa[v],root[i]);
}
if(flag==2)
{
read(a);
root[i]=root[a];
}
if(flag==3)
{
read(a),read(b);
root[i]=root[i-1];
int u=find(a,root[i]),v=find(b,root[i]);
if(fa[u]==fa[v]) puts("1");
else puts("0");
}
}
return 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
#include<bits/stdc++.h>
#define maxn 2000010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,last,ans,tot,root;
int ls[maxn],rs[maxn],siz[maxn],key[maxn],val[maxn];
int add(int v)
{
val[++tot]=v,key[tot]=rand(),siz[tot]=1;
return tot;
}
void pushup(int x)
{
siz[x]=siz[ls[x]]+siz[rs[x]]+1;
}
void merge(int &p,int x,int y)
{
if(!x||!y)
{
p=x+y;
return;
}
if(key[x]<key[y]) p=x,merge(rs[x],rs[x],y);
else p=y,merge(ls[y],x,ls[y]);
pushup(p);
}
void split(int p,int k,int &x,int &y)
{
if(!p)
{
x=y=0;
return;
}
if(val[p]<=k) x=p,split(rs[p],k,rs[x],y);
else y=p,split(ls[p],k,x,ls[y]);
pushup(p);
}
void insert(int v)
{
int x,y;
split(root,v,x,y),merge(x,x,add(v)),merge(root,x,y);
}
void del(int v)
{
int x,y,z;
split(root,v,x,y),split(x,v-1,x,z);
merge(z,ls[z],rs[z]),merge(x,x,z),merge(root,x,y);
}
int kth(int v)
{
int x,y;
split(root,v-1,x,y);
int ans=siz[x]+1;
merge(root,x,y);
return ans;
}
int get(int p,int k)
{
if(k==siz[ls[p]]+1) return val[p];
if(k<=siz[ls[p]]) return get(ls[p],k);
else return get(rs[p],k-siz[ls[p]]-1);
}
int pre(int v)
{
int x,y;
split(root,v-1,x,y);
int ans=get(x,siz[x]);
merge(root,x,y);
return ans;
}
int nxt(int v)
{
int x,y;
split(root,v,x,y);
int ans=get(y,1);
merge(root,x,y);
return ans;
}
int main()
{
read(n),read(m);
for(int i=1,a;i<=n;++i) read(a),insert(a);
while(m--)
{
int opt,a;
read(opt),read(a),a^=last;
if(opt==1) insert(a);
if(opt==2) del(a);
if(opt==3) last=kth(a);
if(opt==4) last=get(root,a);
if(opt==5) last=pre(a);
if(opt==6) last=nxt(a);
if(opt!=1&&opt!=2) ans^=last;
}
printf("%d",ans);
return 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
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
#include<bits/stdc++.h>
#define maxn 1000010
#define inf 500000000
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,tot,root,top;
int fa[maxn],siz[maxn],ch[maxn][2],tag[maxn],rev[maxn],st[maxn];
int val[maxn],sum[maxn],ma[maxn],a[maxn],lm[maxn],rm[maxn],c[maxn];
string opt;
bool check(int x)
{
return ch[fa[x]][1]==x;
}
void pushup(int x)
{
int ls=ch[x][0],rs=ch[x][1];
siz[x]=siz[ls]+siz[rs]+1;
sum[x]=sum[ls]+sum[rs]+val[x];
lm[x]=max(lm[ls],sum[ls]+val[x]+lm[rs]);
rm[x]=max(rm[rs],sum[rs]+val[x]+rm[ls]);
ma[x]=max(val[x]+lm[rs]+rm[ls],max(ma[ls],ma[rs]));
}
void pushr(int x)
{
rev[x]^=1,swap(ch[x][0],ch[x][1]),swap(lm[x],rm[x]);
}
void pushv(int x,int v)
{
if(!x) return;
tag[x]=1,val[x]=v,sum[x]=v*siz[x];
lm[x]=rm[x]=max(sum[x],0),ma[x]=max(sum[x],val[x]);
}
void pushdown(int x)
{
int ls=ch[x][0],rs=ch[x][1];
if(tag[x]) pushv(ls,val[x]),pushv(rs,val[x]);
if(rev[x]) pushr(ls),pushr(rs);
tag[x]=rev[x]=0;
}
int add()
{
int x=top?st[top--]:++tot;
fa[x]=ch[x][0]=ch[x][1]=rev[x]=siz[x]=tag[x]=0;
return x;
}
void build(int l,int r,int &x,int *a)
{
x=add();
int mid=(l+r)>>1;
lm[x]=rm[x]=max(a[mid],0);
val[x]=ma[x]=sum[x]=a[mid];
if(l<mid) build(l,mid-1,ch[x][0],a);
if(r>mid) build(mid+1,r,ch[x][1],a);
fa[ch[x][0]]=fa[ch[x][1]]=x;
pushup(x);
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=check(x),w=ch[x][k^1];
ch[z][check(y)]=x,fa[x]=z;
ch[y][k]=w,fa[w]=y;
ch[x][k^1]=y,fa[y]=x;
pushup(y),pushup(x);
}
void splay(int x,int goal)
{
for(int y;fa[x]!=goal;rotate(x))
if(fa[y=fa[x]]!=goal)
rotate(check(x)^check(y)?x:y);
if(!goal) root=x;
}
int kth(int x)
{
int p=root;
while(1)
{
pushdown(p);
int ls=ch[p][0],rs=ch[p][1];
if(ls&&x<=siz[ls]) p=ls;
else
{
x-=siz[ls]+1;
if(!x) return p;
p=rs;
}
}
}
void split(int l,int r)
{
l=kth(l-1),r=kth(r+1),splay(l,0),splay(r,l);
}
void insert(int x,int num)
{
int t,p;
build(1,num,t,c);
split(x+1,x);
p=ch[root][1];
ch[p][0]=t,fa[t]=p;
pushup(p),pushup(root);
}
void del(int x)
{
if(!x) return;
st[++top]=x;
del(ch[x][0]),del(ch[x][1]);
}
void erase(int l,int r)
{
int p;
split(l,r);
p=ch[root][1];
del(ch[p][0]),ch[p][0]=0;
pushup(p),pushup(root);
}
void cover(int l,int r,int v)
{
int p;
split(l,r);
p=ch[root][1];
pushv(ch[p][0],v);
pushup(p),pushup(root);
}
void reverse(int l,int r)
{
int p;
split(l,r);
p=ch[root][1];
pushr(ch[p][0]);
pushup(p),pushup(root);
}
int query(int l,int r)
{
int p;
split(l,r);
p=ch[root][1];
return sum[ch[p][0]];
}
int main()
{
read(n),read(m);
ma[0]=a[1]=a[n+2]=-inf;
for(int i=2;i<=n+1;++i) read(a[i]);
build(1,n+2,root,a);
while(m--)
{
cin>>opt;
int x,num,v;
if(opt=="MAX-SUM") printf("%d\n",ma[root]);
else read(x),read(num),x++;
if(opt=="INSERT")
{
for(int i=1;i<=num;++i) read(c[i]);
insert(x,num);
}
if(opt=="DELETE") erase(x,x+num-1);
if(opt=="MAKE-SAME") read(v),cover(x,x+num-1,v);
if(opt=="REVERSE") reverse(x,x+num-1);
if(opt=="GET-SUM") printf("%d\n",query(x,x+num-1));
}
return 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
111
112
113
114
115
116
117
#include<bits/stdc++.h>
#define maxn 25000010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,flag,tot,v,x,y,z;
int ls[maxn],rs[maxn],siz[maxn],key[maxn],val[maxn],root[maxn];
int build(int x)
{
val[++tot]=x;
siz[tot]=1;
key[tot]=rand();
return tot;
}
void pushup(int x)
{
siz[x]=siz[ls[x]]+siz[rs[x]]+1;
}
int cpy(int x)
{
tot++;
val[tot]=val[x];
siz[tot]=siz[x];
key[tot]=key[x];
ls[tot]=ls[x];
rs[tot]=rs[x];
return tot;
}
void merge(int &p,int x,int y)
{
if(!x||!y)
{
p=x+y;
return;
}
if(key[x]<key[y]) p=cpy(x),merge(rs[p],rs[p],y);
else p=cpy(y),merge(ls[p],x,ls[p]);
pushup(p);
}
void split(int p,int k,int &x,int &y)
{
if(!p)
{
x=y=0;
return;
}
if(val[p]<=k)
{
x=cpy(p);
split(rs[x],k,rs[x],y);
pushup(x);
}
else
{
y=cpy(p);
split(ls[y],k,x,ls[y]);
pushup(y);
}
}
int query(int p,int k)
{
if(k==siz[ls[p]]+1) return val[p];
if(k<=siz[ls[p]]) return query(ls[p],k);
else return query(rs[p],k-siz[ls[p]]-1);
}
int main()
{
read(n);
for(int i=1;i<=n;++i)
{
int a;
read(v),read(flag),read(a);
root[i]=root[v];
if(flag==1)
{
split(root[i],a,x,y);
merge(x,x,build(a));
merge(root[i],x,y);
}
if(flag==2)
{
split(root[i],a,x,y);
split(x,a-1,x,z);
merge(z,ls[z],rs[z]);
merge(x,x,z);
merge(root[i],x,y);
}
if(flag==3)
{
split(root[i],a-1,x,y);
printf("%d\n",siz[x]+1);
merge(root[i],x,y);
}
if(flag==4)
printf("%d\n",query(root[i],a));
if(flag==5)
{
split(root[i],a-1,x,y);
if(!siz[x]) puts("-2147483647");
printf("%d\n",query(x,siz[x]));
merge(root[i],x,y);
}
if(flag==6)
{
split(root[i],a,x,y);
if(!siz[y]) puts("2147483647");
else printf("%d\n",query(y,1));
merge(root[i],x,y);
}
}
return 0;
}

LCT

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
#include<bits/stdc++.h>
#define maxn 300010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m;
int fa[maxn],ch[maxn][2],rev[maxn],val[maxn],sum[maxn];
bool check(int x)
{
return ch[fa[x]][1]==x;
}
bool notroot(int x)
{
return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
}
void pushup(int x)
{
sum[x]=sum[ch[x][0]]^sum[ch[x][1]]^val[x];
}
void pushrev(int x)
{
swap(ch[x][0],ch[x][1]),rev[x]^=1;
}
void pushdown(int x)
{
if(rev[x]) pushrev(ch[x][0]),pushrev(ch[x][1]),rev[x]=0;
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=check(x),w=ch[x][k^1];
if(notroot(y)) ch[z][check(y)]=x;
ch[x][k^1]=y,ch[y][k]=w;
if(w) fa[w]=y;
fa[x]=z,fa[y]=x;
pushup(y),pushup(x);
}
void all(int x)
{
if(notroot(x)) all(fa[x]);
pushdown(x);
}
void splay(int x)
{
all(x);
for(int y;notroot(x);rotate(x))
if(notroot(y=fa[x]))
rotate(check(x)^check(y)?x:y);
pushup(x);
}
void access(int x)
{
for(int y=0;x;y=x,x=fa[x])
splay(x),ch[x][1]=y,pushup(x);
}
void makeroot(int x)
{
access(x),splay(x),pushrev(x);
}
void split(int x,int y)
{
makeroot(x),access(y),splay(y);
}
int findroot(int x)
{
access(x),splay(x);
while(ch[x][0]) pushdown(x),x=ch[x][0];
splay(x);
return x;
}
void link(int x,int y)
{
makeroot(x);
if(findroot(y)!=x) fa[x]=y;
}
void cut(int x,int y)
{
split(x,y);
if(ch[y][0]==x&&!ch[x][1]) fa[x]=ch[y][0]=0;
}
int query(int x,int y)
{
split(x,y);
return sum[y];
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i) read(val[i]);
while(m--)
{
int opt,x,y;
read(opt),read(x),read(y);
if(opt==0) printf("%d\n",query(x,y));
if(opt==1) link(x,y);
if(opt==2) cut(x,y);
if(opt==3) splay(x),val[x]=y;
}
return 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
#include<bits/stdc++.h>
#define maxn 200010
#define maxm 10000010
#define lowbit(x) (x&(-x))
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,cnt,tot;
int ans[maxn],rt[maxn],sum[maxm],ls[maxm],rs[maxm];
struct node
{
int a,b,c,val;
}p[maxn];
bool cmp(const node &x,const node &y)
{
if(x.a==y.a)
{
if(x.b==y.b) return x.c<y.c;
return x.b<y.b;
}
return x.a<y.a;
}
void modify(int l,int r,int pos,int v,int &cur)
{
if(!cur) cur=++tot;
sum[cur]+=v;
if(l==r) return;
if(pos<=mid) modify(l,mid,pos,v,ls[cur]);
else modify(mid+1,r,pos,v,rs[cur]);
}
int query(int L,int R,int l,int r,int cur)
{
if(!cur) return 0;
if(L<=l&&R>=r) return sum[cur];
int v=0;
if(L<=mid) v+=query(L,R,l,mid,ls[cur]);
if(R>mid) v+=query(L,R,mid+1,r,rs[cur]);
return v;
}
void update(int x,int c,int v)
{
while(x<=m) modify(1,m,c,v,rt[x]),x+=lowbit(x);
}
int ask(int x,int c)
{
int v=0;
while(x) v+=query(1,c,1,m,rt[x]),x-=lowbit(x);
return v;
}
void init()
{
int pos;
sort(p+1,p+n+1,cmp);
for(int i=1;i<=n;i=pos+1)
{
pos=i;
while(pos<n&&p[i].a==p[pos+1].a&&p[i].b==p[pos+1].b&&p[i].c==p[pos+1].c) pos++;
p[++cnt]=p[i],p[cnt].val=pos-i+1;
}
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i) read(p[i].a),read(p[i].b),read(p[i].c);
init();
for(int i=1;i<=cnt;++i)
{
ans[ask(p[i].b,p[i].c)+p[i].val-1]+=p[i].val;
update(p[i].b,p[i].c,p[i].val);
}
for(int i=0;i<n;++i) printf("%d\n",ans[i]);
return 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
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
#include<bits/stdc++.h>
#define maxn 20000010
#define inf 2147483647
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
template<typename T> inline void write(T x)
{
short st[30],tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
}
int n,m,tot;
int a[maxn],val[maxn],key[maxn],siz[maxn],ls[maxn],rs[maxn];
struct FHQ_Treap
{
int root,x,y,z;
int add(int x)
{
val[++tot]=x;
siz[tot]=1;
key[tot]=rand();
return tot;
}
void pushup(int x)
{
siz[x]=siz[ls[x]]+siz[rs[x]]+1;
}
void merge(int &p,int x,int y)
{
if(!x||!y)
{
p=x+y;
return;
}
if(key[x]<key[y]) p=x,merge(rs[p],rs[p],y);
else p=y,merge(ls[p],x,ls[p]);
pushup(p);
}
void split(int p,int k,int &x,int &y)
{
if(!p)
{
x=y=0;
return;
}
if(val[p]<=k) x=p,split(rs[p],k,rs[p],y);
else y=p,split(ls[p],k,x,ls[p]);
pushup(p);
}
void insert(int v)
{
split(root,v,x,y);
merge(x,x,add(v));
merge(root,x,y);
}
void del(int v)
{
split(root,v,x,y);
split(x,v-1,x,z);
merge(z,ls[z],rs[z]);
merge(x,x,z);
merge(root,x,y);
}
void build(int l,int r)
{
for(int i=l;i<=r;++i) insert(a[i]);
}
int kth(int v)
{
split(root,v-1,x,y);
int ans=siz[x]+1;
merge(root,x,y);
return ans;
}
int get(int p,int k)
{
if(k==siz[ls[p]]+1) return val[p];
if(k<=siz[ls[p]]) return get(ls[p],k);
else return get(rs[p],k-siz[ls[p]]-1);
}
int pre(int v)
{
split(root,v-1,x,y);
int ans;
if(siz[x]) ans=get(x,siz[x]);
else ans=-inf;
merge(root,x,y);
return ans;
}
int nxt(int v)
{
split(root,v,x,y);
int ans;
if(siz[y]) ans=get(y,1);
else ans=inf;
merge(root,x,y);
return ans;
}
}treap[maxn];
int tree_cnt,root;
int lc[maxn],rc[maxn];
struct Segment_Tree
{
void build(int l,int r,int &cur)
{
cur=++tree_cnt;
treap[cur].build(l,r);
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,lc[cur]),build(mid+1,r,rc[cur]);
}
int q_rnk(int L,int R,int l,int r,int k,int cur)
{
if(L<=l&&R>=r) return treap[cur].kth(k)-1;
int mid=(l+r)>>1,ans=0;
if(L<=mid) ans+=q_rnk(L,R,l,mid,k,lc[cur]);
if(R>mid) ans+=q_rnk(L,R,mid+1,r,k,rc[cur]);
return ans;
}
int q_val(int L,int R,int rnk)
{
int l=0,r=1e8,ans;
while(l<=r)
{
int mid=(l+r)>>1;
if(q_rnk(L,R,1,n,mid,root)+1<=rnk) ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
void modify(int l,int r,int pos,int k,int cur)
{
treap[cur].del(a[pos]),treap[cur].insert(k);
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) modify(l,mid,pos,k,lc[cur]);
if(pos>mid) modify(mid+1,r,pos,k,rc[cur]);
}
int lower(int L,int R,int l,int r,int k,int cur)
{
if(L>r||R<l) return -inf;
if(L<=l&&R>=r) return treap[cur].pre(k);
int mid=(l+r)>>1;
return max(lower(L,R,l,mid,k,lc[cur]),lower(L,R,mid+1,r,k,rc[cur]));
}
int upper(int L,int R,int l,int r,int k,int cur)
{
if(L>r||R<l) return inf;
if(L<=l&&R>=r) return treap[cur].nxt(k);
int mid=(l+r)>>1;
return min(upper(L,R,l,mid,k,lc[cur]),upper(L,R,mid+1,r,k,rc[cur]));
}
}tree;
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i) read(a[i]);
tree.build(1,n,root);
for(int i=1;i<=m;++i)
{
int opt,l,r,p,k;
read(opt);
if(opt==1)
{
read(l),read(r),read(k);
write(tree.q_rnk(l,r,1,n,k,root)+1),puts("");
}
if(opt==2)
{
read(l),read(r),read(k);
write(tree.q_val(l,r,k)),puts("");
}
if(opt==3)
{
read(p),read(k);
tree.modify(1,n,p,k,root),a[p]=k;
}
if(opt==4)
{
read(l),read(r),read(k);
write(tree.lower(l,r,1,n,k,root)),puts("");
}
if(opt==5)
{
read(l),read(r),read(k);
write(tree.upper(l,r,1,n,k,root)),puts("");
}
}
return 0;
}

CDQ 分治

三维偏序。

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
#include<bits/stdc++.h>
#define maxn 200010
#define lowbit(x) (x&(-x))
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,k,cnt,tot;
int num[maxn],tree[maxn];
struct node
{
int a,b,c,val,ans;
}p[maxn],q[maxn];
bool cmp1(node x,node y)
{
if(x.a==y.a)
{
if(x.b==y.b) return x.c<y.c;
return x.b<y.b;
}
return x.a<y.a;
}
bool cmp2(node x,node y)
{
if(x.b==y.b) return x.c<y.c;
return x.b<y.b;
}
void update(int x,int v)
{
while(x<=k)
{
tree[x]+=v;
x+=lowbit(x);
}
}
int query(int x)
{
int sum=0;
while(x)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
void cdq(int l,int r)
{
if(l==r) return;
int mid=(l+r)>>1;
cdq(l,mid),cdq(mid+1,r);
sort(q+l,q+mid+1,cmp2);
sort(q+mid+1,q+r+1,cmp2);
int j=l;
for(int i=mid+1;i<=r;++i)
{
while(q[j].b<=q[i].b&&j<=mid)
{
update(q[j].c,q[j].val);
j++;
}
q[i].ans+=query(q[i].c);
}
for(int i=l;i<j;++i) update(q[i].c,-q[i].val);
}
int main()
{
read(n),read(k);
for(int i=1;i<=n;++i)
read(p[i].a),read(p[i].b),read(p[i].c);
sort(p+1,p+n+1,cmp1);
for(int i=1;i<=n;++i)
{
cnt++;
if(p[i].a!=p[i+1].a||p[i].b!=p[i+1].b||p[i].c!=p[i+1].c)
{
q[++tot]=p[i];
q[tot].val=cnt;
cnt=0;
}
}
cdq(1,tot);
for(int i=1;i<=tot;++i)
num[q[i].ans+q[i].val-1]+=q[i].val;
for(int i=0;i<n;++i) printf("%d\n",num[i]);
return 0;
}

分块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void modify(int l,int r,ll v)
{
for(int i=l;i<=min(S*bel[l],r);++i) a[i]+=v,sum[bel[l]]+=v;
if(bel[l]!=bel[r])
for(int i=S*(bel[r]-1)+1;i<=r;++i)
a[i]+=v,sum[bel[r]]+=v;
for(int i=bel[l]+1;i<=bel[r]-1;++i) add[i]+=v;
}
ll query(int l,int r)
{
ll ans=0;
for(int i=l;i<=min(S*bel[l],r);++i) ans+=a[i]+add[bel[l]];
if(bel[l]!=bel[r])
for(int i=S*(bel[r]-1)+1;i<=r;++i)
ans+=a[i]+add[bel[r]];
for(int i=bel[l]+1;i<=bel[r]-1;++i) ans+=sum[i]+S*add[i];
return ans;
}

......

for(int i=1;i<=n;++i)
read(a[i]),bel[i]=(i-1)/S+1,sum[bel[i]]+=a[i];

点分治

求出树上两点距离小于等于 的点对数量。

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
#include<bits/stdc++.h>
#define maxn 80010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,k,root,tot,cnt;
ll ans;
int siz[maxn],mx[maxn],len[maxn];
bool vis[maxn];
struct edge
{
int to,nxt,v;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to,int val)
{
e[++edge_cnt]={to,head[from],val},head[from]=edge_cnt;
}
void dfs_root(int x,int fa)
{
siz[x]=1,mx[x]=0;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fa||vis[y]) continue;
dfs_root(y,x),siz[x]+=siz[y];
mx[x]=max(mx[x],siz[y]);
}
mx[x]=max(mx[x],tot-siz[x]);
if(mx[x]<mx[root]) root=x;
}
void dfs_get(int x,int fa,int dis)
{
len[++cnt]=dis;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fa||vis[y]) continue;
dfs_get(y,x,dis+e[i].v);
}
}
ll calc(int x,int dis)
{
cnt=0,dfs_get(x,0,dis),sort(len+1,len+cnt+1);
ll v=0,pos=cnt;
for(int i=1;i<=cnt;++i)
{
while(pos&&len[i]+len[pos]>k) pos--;
v+=pos-(i<=pos);
}
return v/2;
}
void solve(int x)
{
int now=tot;
vis[x]=true,ans+=calc(x,0);
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(vis[y]) continue;
ans-=calc(y,e[i].v);
root=0,tot=siz[y]>siz[x]?now-siz[x]:siz[y];
dfs_root(y,x),solve(root);
}
}
int main()
{
read(n);
for(int i=1;i<n;++i)
{
int x,y,v;
read(x),read(y),read(v);
add(x,y,v),add(y,x,v);
}
read(k),tot=mx[0]=n,dfs_root(1,0),solve(root),printf("%lld",ans);
return 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
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
#include<bits/stdc++.h>
#define maxn 200010
#define maxm 10000010
#define mid ((l+r)>>1)
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,dfn_cnt,tot,root,ans;
int v[maxn],de[maxn],fa[maxn],ma[maxn],siz[maxn],si[maxn];
int pos[maxn],f[maxn][20],lg[maxn];
bool vis[maxn];
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=(edge){to,head[from]};
head[from]=edge_cnt;
}
struct node
{
int tree_cnt;
int ls[maxm],rs[maxm],rt[maxn],sum[maxm];
void modify(int l,int r,int pos,int v,int &cur)
{
if(!cur) cur=++tree_cnt;
sum[cur]+=v;
if(l==r) return;
if(pos<=mid) modify(l,mid,pos,v,ls[cur]);
else modify(mid+1,r,pos,v,rs[cur]);
}
int query(int L,int R,int l,int r,int cur)
{
if(L>R||!cur) return 0;
if(L<=l&&R>=r) return sum[cur];
int v=0;
if(L<=mid) v+=query(L,R,l,mid,ls[cur]);
if(R>mid) v+=query(L,R,mid+1,r,rs[cur]);
return v;
}
}T1,T2;
void dfs_pre(int x,int fa)
{
de[x]=de[fa]+1,f[++dfn_cnt][0]=de[x],pos[x]=dfn_cnt;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fa) continue;
dfs_pre(y,x),f[++dfn_cnt][0]=de[x];
}
}
void st()
{
lg[0]=-1;
for(int i=1;i<=dfn_cnt;++i) lg[i]=lg[i>>1]+1;
for(int j=1;j<=18;++j)
for(int i=1;i+(1<<j)-1<=dfn_cnt;++i)
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
int ask(int x,int y)
{
int l=pos[x],r=pos[y],len;
if(l>r) swap(l,r);
len=lg[r-l+1];
return min(f[l][len],f[r-(1<<len)+1][len]);
}
int dis(int x,int y)
{
return de[x]+de[y]-2*ask(x,y);
}
void dfs_root(int x,int fath)
{
siz[x]=1,ma[x]=0;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(vis[y]||y==fath) continue;
dfs_root(y,x),siz[x]+=siz[y],ma[x]=max(ma[x],siz[y]);
}
ma[x]=max(ma[x],tot-siz[x]);
if(ma[x]<ma[root]) root=x;
}
void solve(int x)
{
int now=tot;
vis[x]=true,si[x]=now;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(vis[y]) continue;
root=0,tot=siz[y];
if(siz[y]>siz[x]) tot=now-siz[x];
dfs_root(y,x),fa[root]=x,solve(root);
}
}
void update(int x,int val)
{
T1.modify(0,si[x],0,val,T1.rt[x]);
for(int i=x;fa[i];i=fa[i])
{
T1.modify(0,si[fa[i]],dis(x,fa[i]),val,T1.rt[fa[i]]);
T2.modify(0,si[fa[i]],dis(x,fa[i]),val,T2.rt[i]);
}
}
int query(int x,int k)
{
int v=T1.query(0,k,0,si[x],T1.rt[x]);
for(int i=x;fa[i];i=fa[i])
{
v+=T1.query(0,k-dis(x,fa[i]),0,si[fa[i]],T1.rt[fa[i]]);
v-=T2.query(0,k-dis(x,fa[i]),0,si[fa[i]],T2.rt[i]);
}
return v;
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i) read(v[i]);
for(int i=1;i<n;++i)
{
int x,y;
read(x),read(y);
add(x,y),add(y,x);
}
dfs_pre(1,0),st(),tot=ma[0]=n,dfs_root(1,0),solve(root);
for(int i=1;i<=n;++i) update(i,v[i]);
while(m--)
{
int opt,x,k;
read(opt),read(x),read(k),x^=ans,k^=ans;
if(!opt) printf("%d\n",ans=query(x,k));
else update(x,k-v[x]),v[x]=k;
}
return 0;
}

笛卡尔树

1
2
3
4
5
6
7
8
9
void build()
{
for(int i=1;i<=n;++i)
{
while(top&&a[st[top]]>a[i]) ls[i]=st[top--];
if(top) rs[st[top]]=i;
st[++top]=i;
}
}

左偏树

维护 个小根堆,支持堆的合并,查询最小值。

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
#include<bits/stdc++.h>
#define maxn 100010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m;
int fa[maxn],ls[maxn],rs[maxn],dis[maxn],val[maxn];
int find(int x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int merge(int x,int y)
{
if(!x||!y) return x+y;
if(val[x]>val[y]) swap(x,y);
rs[x]=merge(rs[x],y),fa[rs[x]]=x;
if(dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);
dis[x]=dis[rs[x]]+1;
return x;
}
void del(int x)
{
val[x]=-1,fa[ls[x]]=ls[x],fa[rs[x]]=rs[x];
fa[x]=merge(ls[x],rs[x]);
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i) read(val[i]),fa[i]=i;
while(m--)
{
int opt,x,y;
read(opt);
if(opt==1)
{
read(x),read(y);
if(val[x]==-1||val[y]==-1) continue;
x=find(x),y=find(y);
if(x!=y) merge(x,y);
}
else
{
read(x);
if(val[x]==-1) puts("-1");
else x=find(x),printf("%d\n",val[x]),del(x);
}
}
return 0;
}

K-D Tree

查询二维平面上欧氏距离下的第 远点对。

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
#include<bits/stdc++.h>
#define maxn 1000010
#define inf 200000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,k,root,type;
struct KD_tree
{
ll d[2];
int mi[2],ma[2],ls,rs,id;
}t[maxn];
bool cmp(const KD_tree &a,const KD_tree &b)
{
return a.d[type]<b.d[type];
}
void pushup(int cur)
{
int ls=t[cur].ls,rs=t[cur].rs;
for(int i=0;i<=1;++i)
{
t[cur].ma[i]=t[cur].mi[i]=t[cur].d[i];
if(ls)
{
t[cur].ma[i]=max(t[cur].ma[i],t[ls].ma[i]);
t[cur].mi[i]=min(t[cur].mi[i],t[ls].mi[i]);
}
if(rs)
{
t[cur].ma[i]=max(t[cur].ma[i],t[rs].ma[i]);
t[cur].mi[i]=min(t[cur].mi[i],t[rs].mi[i]);
}
}
}
void build(int l,int r,int k,int &cur)
{
int mid=(l+r)>>1;
cur=mid,type=k;
nth_element(t+l+1,t+mid+1,t+r+1,cmp);
if(l<mid) build(l,mid-1,k^1,t[mid].ls);
if(r>mid) build(mid+1,r,k^1,t[mid].rs);
pushup(cur);
}
ll calc(ll x)
{
return x*x;
}
ll dis(int cur,ll x,ll y)
{
return calc(t[cur].d[0]-x)+calc(t[cur].d[1]-y);
}
ll dist(int cur,ll x,ll y)
{
return max(calc(t[cur].ma[0]-x),calc(t[cur].mi[0]-x))+max(calc(t[cur].ma[1]-y),calc(t[cur].mi[1]-y));
}
priority_queue<ll,vector<ll>,greater<ll> > q;
void query(int cur,ll x,ll y)
{
ll d,dl,dr;
int ls=t[cur].ls,rs=t[cur].rs;
d=dis(cur,x,y);
if(d>q.top()) q.pop(),q.push(d);
if(ls) dl=dist(ls,x,y);
else dl=-inf;
if(rs) dr=dist(rs,x,y);
else dr=-inf;
if(dl>q.top()) query(ls,x,y);
if(dr>q.top()) query(rs,x,y);
}
int main()
{
read(n),read(k);
for(int i=1;i<=n;++i)
read(t[i].d[0]),read(t[i].d[1]);
build(1,n,0,root);
for(int i=1;i<=2*k;++i) q.push(0);
for(int i=1;i<=n;++i)
query(root,t[i].d[0],t[i].d[1]);
printf("%lld",q.top());
return 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
struct KD_tree
{
int d[2],mi[2],ma[2],ls,rs,id;
ll val,sum;
}t[maxn],dat[maxn];
bool cmp(const KD_tree &a,const KD_tree &b)
{
return a.d[type]<b.d[type];
}
void pushup(int cur)
{
int ls=t[cur].ls,rs=t[cur].rs;
for(int i=0;i<=1;++i)
{
t[cur].ma[i]=t[cur].mi[i]=t[cur].d[i];
if(ls)
{
t[cur].ma[i]=max(t[cur].ma[i],t[ls].ma[i]);
t[cur].mi[i]=min(t[cur].mi[i],t[ls].mi[i]);
}
if(rs)
{
t[cur].ma[i]=max(t[cur].ma[i],t[rs].ma[i]);
t[cur].mi[i]=min(t[cur].mi[i],t[rs].mi[i]);
}
}
t[cur].sum=t[ls].sum+t[rs].sum+t[cur].val;
}
void build(int l,int r,int k,int &cur)
{
cur=++tot,type=k;
int mid=(l+r)>>1;
nth_element(dat+l,dat+mid,dat+r+1,cmp);
t[cur]=dat[mid];
if(l<mid) build(l,mid-1,k^1,t[cur].ls);
if(r>mid) build(mid+1,r,k^1,t[cur].rs);
pushup(cur);
}
bool check(ll x,ll y)
{
return a*x+b*y<c;
}
ll query(int cur)
{
ll ans=0;
int ls=t[cur].ls,rs=t[cur].rs,cnt=0;
cnt+=check(t[cur].ma[0],t[cur].ma[1]);
cnt+=check(t[cur].ma[0],t[cur].mi[1]);
cnt+=check(t[cur].mi[0],t[cur].mi[1]);
cnt+=check(t[cur].mi[0],t[cur].ma[1]);
if(cnt==4) return t[cur].sum;
if(!cnt) return 0;
if(check(t[cur].d[0],t[cur].d[1])) ans+=t[cur].val;
if(ls) ans+=query(ls);
if(rs) ans+=query(rs);
return ans;
}

支持插入点和查询二维平面上矩形内所有点的权值和,强制在线,用 实现,当不平衡时,像替罪羊树一样重构。

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
struct KD_tree
{
int d[2],mi[2],ma[2],ls,rs,val,sum,siz;
}t[maxn],dat[maxn],p;
bool cmp(const KD_tree &a,const KD_tree &b)
{
return a.d[type]<b.d[type];
}
int add()
{
if(top) return st[top--];
return ++tot;
}
void pushup(int cur)
{
int ls=t[cur].ls,rs=t[cur].rs;
for(int i=0;i<=1;++i)
{
t[cur].ma[i]=t[cur].mi[i]=t[cur].d[i];
if(ls)
{
t[cur].ma[i]=max(t[cur].ma[i],t[ls].ma[i]);
t[cur].mi[i]=min(t[cur].mi[i],t[ls].mi[i]);
}
if(rs)
{
t[cur].ma[i]=max(t[cur].ma[i],t[rs].ma[i]);
t[cur].mi[i]=min(t[cur].mi[i],t[rs].mi[i]);
}
}
t[cur].sum=t[ls].sum+t[rs].sum+t[cur].val;
t[cur].siz=t[ls].siz+t[rs].siz+1;
}
void build(int l,int r,int k,int &cur)
{
cur=add(),type=k;
int mid=(l+r)>>1;
nth_element(dat+l,dat+mid,dat+r+1,cmp);
t[cur]=dat[mid];
t[cur].ls=t[cur].rs=0;
if(l<mid) build(l,mid-1,k^1,t[cur].ls);
if(r>mid) build(mid+1,r,k^1,t[cur].rs);
pushup(cur);
}
void del(int cur)
{
if(!cur) return;
dat[++now]=t[cur];
st[++top]=cur;
del(t[cur].ls),del(t[cur].rs);
}
void check(int &cur,int k)
{
int ls=t[cur].ls,rs=t[cur].rs;
if(t[cur].siz*alpha<max(t[ls].siz,t[rs].siz))
now=0,del(cur),build(1,t[cur].siz,k,cur);
}
void insert(KD_tree p,int k,int &cur)
{
if(!cur)
{
cur=add();
t[cur]=p;
t[cur].ls=t[cur].rs=0;
pushup(cur);
return;
}
if(p.d[k]<=t[cur].d[k]) insert(p,k^1,t[cur].ls);
else insert(p,k^1,t[cur].rs);
pushup(cur);
check(cur,k);
}
bool check_p(KD_tree p)
{
return p.d[0]<=bx&&p.d[0]>=ax&&p.d[1]<=by&&p.d[1]>=ay;
}
bool check_in(KD_tree p)
{
return p.mi[0]>=ax&&p.ma[0]<=bx&&p.mi[1]>=ay&&p.ma[1]<=by;
}
bool check_out(KD_tree p)
{
return p.mi[0]>bx||p.ma[0]<ax||p.mi[1]>by||p.ma[1]<ay;
}
int query(int cur)
{
int ls=t[cur].ls,rs=t[cur].rs,val=t[cur].val,sum=t[cur].sum,ans=0;
if(check_in(t[cur])) return sum;
if(check_out(t[cur])) return 0;
if(check_p(t[cur])) ans+=val;
if(ls) ans+=query(ls);
if(rs) ans+=query(rs);
return ans;
}

图论

强连通分量

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
#include<bits/stdc++.h>
#define maxn 200010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,cnt,top,tot,ans;
int dfn[maxn],low[maxn],col[maxn],st[maxn],d[maxn];
int v[maxn],val[maxn],f[maxn];
bool vis[maxn];
struct Edge
{
int x,y;
}ed[maxn];
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=(edge){to,head[from]},head[from]=edge_cnt;
}
void tarjan(int x)
{
dfn[x]=low[x]=++cnt,vis[st[++top]=x]=true;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
else if(vis[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
tot++;
int now;
do
{
vis[now=st[top--]]=false;
val[col[now]=tot]+=v[now];
}while(now!=x);
}
}
void clear()
{
for(int i=1;i<=n;++i) head[i]=0;
for(int i=1;i<=edge_cnt;++i) e[i].nxt=e[i].to=0;
edge_cnt=0;
}
void dp()
{
queue<int> q;
for(int i=1;i<=tot;++i)
{
f[i]=val[i];
if(!d[i]) q.push(i);
}
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
d[y]--,f[y]=max(f[y],f[x]+val[y]);
if(!d[y]) q.push(y);
}
}
for(int i=1;i<=tot;++i) ans=max(ans,f[i]);
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i) read(v[i]);
for(int i=1;i<=m;++i)
{
read(ed[i].x),read(ed[i].y);
add(ed[i].x,ed[i].y);
}
for(int i=1;i<=n;++i)
if(!dfn[i])
tarjan(i);
clear();
for(int i=1;i<=m;++i)
{
int x=col[ed[i].x],y=col[ed[i].y];
if(x==y) continue;
add(x,y),d[y]++;
}
dp(),printf("%d",ans);
return 0;
}

2-SAT

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
void tarjan(int x)
{
dfn[x]=low[x]=++dfn_cnt;
st[++top]=x;
vis[x]=true;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(vis[y])
low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x])
{
co_cnt++;
int now;
do
{
now=st[top--];
vis[now]=false;
co[now]=co_cnt;
}while(now!=x);
}
}
bool check()
{
for(int i=1;i<=2*n;++i)
if(!dfn[i])
tarjan(i);
for(int i=1;i<=n;++i)
if(co[i]==co[i+n])
return false;
return true;
}

......

add(x+(a^1)*n,y+b*n),add(y+(b^1)*n,x+a*n);

点双连通分量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void tarjan(int x,int root)
{
int son=0;
dfn[x]=low[x]=++dfn_cnt;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(!dfn[y])
{
tarjan(y,root),low[x]=min(low[x],low[y]);
if(x!=root&&dfn[x]<=low[y]) cut[x]=true;
if(x==root) son++;
}
else low[x]=min(low[x],dfn[y]);
}
if(son>1) cut[x]=true;
}

......

for(int i=1;i<=n;++i)
if(!dfn[i])
tarjan(i,i);

求割点时维护一个栈即可求出每个点双连通分量。当 满足为割点时,就依次弹栈,直到弹出 就停止, 还需留在栈中,因为割点 可能属于多个点双连通分量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void tarjan(int x)
{
dfn[x]=low[x]=++dfn_cnt,st[++top]=x;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(!dfn[y])
{
tarjan(y),low[x]=min(low[x],low[y]);
if(dfn[x]<=low[y])
{
col_cnt++;
int now;
do now=st[top--],col[now]=col_cnt; while(now!=y);
}
}
else low[x]=min(low[x],dfn[y]);
}
}

边双连通分量

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
void tarjan(int x,int link)
{
dfn[x]=low[x]=++dfn_cnt;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(!dfn[y])
{
tarjan(y,i),low[x]=min(low[x],low[y]);
if(dfn[x]<low[y]) bri[i]=bri[i^1]=true;
}
else if(i!=(link^1)) low[x]=min(low[x],dfn[y]);
}
}
void dfs_col(int x)
{
col[x]=co_cnt;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(col[y]||bri[i]) continue;
dfs_col(y);
}
}

......

for(int i=1;i<=n;++i)
if(!dfn[i])
tarjan(i,0);
for(int i=1;i<=n;++i)
if(!col[i])
col_cnt++,dfs_col(i);

最短路

spfa

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
#include<bits/stdc++.h>
#define maxn 15010
#define inf 1000000000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,s,t;
ll dis[maxn];
bool vis[maxn];
struct edge
{
int to,nxt,v;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to,int val)
{
e[++edge_cnt]={to,head[from],val},head[from]=edge_cnt;
}
void spfa()
{
queue<int> q;
for(int i=1;i<=n;++i) dis[i]=inf;
q.push(s),dis[s]=0,vis[s]=true;
while(!q.empty())
{
int x=q.front();
q.pop(),vis[x]=false;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to,v=e[i].v;
if(dis[y]>dis[x]+v)
{
dis[y]=dis[x]+v;
if(!vis[y]) q.push(y),vis[y]=true;
}
}
}
}
int main()
{
read(n),read(m),read(s),read(t);
for(int i=1;i<=m;++i)
{
int x,y,v;
read(x),read(y),read(v);
add(x,y,v),add(y,x,v);
}
spfa(),printf("%lld",dis[t]);
return 0;
}

dijkstra

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
#include<bits/stdc++.h>
#define maxn 15010
#define inf 1000000000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,s,t;
ll dis[maxn];
bool vis[maxn];
struct edge
{
int to,nxt,v;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to,int val)
{
e[++edge_cnt]={to,head[from],val},head[from]=edge_cnt;
}
void dij()
{
priority_queue<pair<ll,int> > q;
for(int i=1;i<=n;++i) dis[i]=inf;
q.push({0,s}),dis[s]=0;
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].nxt)
{
int y=e[i].to,v=e[i].v;
if(dis[y]>dis[x]+v)
dis[y]=dis[x]+v,q.push({-dis[y],y});
}
}
}
int main()
{
read(n),read(m),read(s),read(t);
for(int i=1;i<=m;++i)
{
int x,y,v;
read(x),read(y),read(v);
add(x,y,v),add(y,x,v);
}
dij(),printf("%lld",dis[t]);
return 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
#include<bits/stdc++.h>
#define maxn 10010
#define inf 1000000000
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int T,n,m;
int cnt[maxn],dis[maxn];
bool vis[maxn];
struct edge
{
int to,nxt,v;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to,int val)
{
e[++edge_cnt]={to,head[from],val},head[from]=edge_cnt;
if(val>=0) e[++edge_cnt]={from,head[to],val},head[to]=edge_cnt;
}
bool spfa()
{
for(int i=1;i<=n;++i) cnt[i]=vis[i]=0,dis[i]=inf;
queue<int> q;
q.push(1),vis[1]=true,dis[1]=0;
while(!q.empty())
{
int x=q.front();
q.pop(),vis[x]=false;
if(++cnt[x]>n) return true;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to,v=e[i].v;
if(dis[y]>dis[x]+v)
{
dis[y]=dis[x]+v;
if(!vis[y]) q.push(y),vis[y]=true;
}
}
}
return false;
}
int main()
{
read(T);
while(T--)
{
read(n),read(m);
edge_cnt=0,memset(head,0,sizeof(head));
for(int i=1;i<=m;++i)
{
int x,y,v;
read(x),read(y),read(v);
add(x,y,v);
}
puts(spfa()?"YES":"NO");
}
return 0;
}

LCA

重链剖分

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
#include<bits/stdc++.h>
#define maxn 2000010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag) x=-x;
}
int n,m,s;
struct edge
{
int to;
int nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=(edge){to,head[from]};
head[from]=edge_cnt;
}
int fa[maxn],siz[maxn],de[maxn],son[maxn];
void dfs_son(int x,int fath)
{
fa[x]=fath;
siz[x]=1;
de[x]=de[fath]+1;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fath) continue;
dfs_son(y,x);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
int top[maxn],dfn[maxn];
int dfn_cnt;
void dfs_chain(int x,int tp)
{
top[x]=tp;
dfn[x]=++dfn_cnt;
if(son[x]) dfs_chain(son[x],tp);
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(dfn[y]) continue;
dfs_chain(y,y);
}
}
int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(de[top[x]]<de[top[y]]) swap(x,y);
x=fa[top[x]];
}
if(de[x]>de[y]) swap(x,y);
return x;
}
int main()
{
read(n),read(m),read(s);
for(int i=1;i<n;++i)
{
int a,b;
read(a),read(b);
add(a,b),add(b,a);
}
dfs_son(s,0);
dfs_chain(s,s);
for(int i=1;i<=m;++i)
{
int a,b;
read(a),read(b);
printf("%d\n",lca(a,b));
}
return 0;
}

ST 表

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
#include<bits/stdc++.h>
#define maxn 1000010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,rt,cnt;
int pos[maxn],dep[maxn],lg[maxn];
pair<int,int> f[22][maxn];
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]={to,head[from]},head[from]=edge_cnt;
}
void dfs(int x,int fa)
{
dep[x]=dep[fa]+1,f[0][pos[x]=++cnt]={dep[x],x};
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fa) continue;
dfs(y,x),f[0][++cnt]={dep[x],x};
}
}
void ST()
{
lg[0]=-1;
for(int i=1;i<=cnt;++i) lg[i]=lg[i>>1]+1;
for(int j=1;j<=19;++j)
for(int i=1;i+(1<<j)-1<=cnt;++i)
f[j][i]=min(f[j-1][i],f[j-1][i+(1<<(j-1))]);
}
int ask(int l,int r)
{
l=pos[l],r=pos[r];
if(l>r) swap(l,r);
int len=lg[r-l+1];
return min(f[len][l],f[len][r-(1<<len)+1]).second;
}
int main()
{
read(n),read(m),read(rt);
for(int i=1;i<n;++i)
{
int x,y;
read(x),read(y);
add(x,y),add(y,x);
}
dfs(rt,0),ST();
while(m--)
{
int x,y;
read(x),read(y),printf("%d\n",ask(x,y));
}
return 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
#include<bits/stdc++.h>
#define maxn 500010
#define inf 1000000000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,s,t;
int d[maxn],cur[maxn];
struct edge
{
int to,nxt;
ll v;
edge(int a=0,int b=0,ll c=0)
{
to=a,nxt=b,v=c;
}
}e[maxn];
int head[maxn],edge_cnt=1;
void add(int from,int to,int val)
{
e[++edge_cnt]=edge(to,head[from],val),head[from]=edge_cnt;
e[++edge_cnt]=edge(from,head[to],0),head[to]=edge_cnt;
}
bool bfs()
{
for(int i=1;i<=n;++i) cur[i]=head[i],d[i]=0;
queue<int> q;
q.push(s),d[s]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(d[y]||!e[i].v) continue;
d[y]=d[x]+1,q.push(y);
}
}
return d[t];
}
ll dfs(int x,ll lim)
{
if(x==t) return lim;
ll res=lim,flow;
for(int &i=cur[x];i;i=e[i].nxt)
{
int y=e[i].to;
ll v=e[i].v;
if(d[y]!=d[x]+1||!v) continue;
if(flow=dfs(y,min(res,v)))
{
res-=flow,e[i].v-=flow,e[i^1].v+=flow;
if(!res) break;
}
}
return lim-res;
}
ll dinic()
{
ll v=0,flow;
while(bfs())
while(flow=dfs(s,inf))
v+=flow;
return v;
}
int main()
{
read(n),read(m),read(s),read(t);
for(int i=1;i<=m;++i)
{
int x,y,v;
read(x),read(y),read(v),add(x,y,v);
}
printf("%lld",dinic());
return 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
#include<bits/stdc++.h>
#define maxn 500010
#define inf 1000000000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,s,t;
ll ans,sum;
ll dis[maxn];
bool vis[maxn];
struct edge
{
int to,nxt;
ll v,c;
edge(int x=0,int y=0,ll z=0,ll w=0)
{
to=x,nxt=y,v=z,c=w;
}
}e[maxn];
int head[maxn],edge_cnt=1;
void add(int from,int to,int val,int cost)
{
e[++edge_cnt]=edge(to,head[from],val,cost),head[from]=edge_cnt;
e[++edge_cnt]=edge(from,head[to],0,-cost),head[to]=edge_cnt;
}
bool bfs()
{
for(int i=1;i<=n;++i) dis[i]=inf,vis[i]=false;
queue<int> q;
q.push(s),dis[s]=0,vis[s]=true;
while(!q.empty())
{
int x=q.front();
q.pop(),vis[x]=false;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
ll c=e[i].c;
if(dis[y]<=dis[x]+c||!e[i].v) continue;
dis[y]=dis[x]+c;
if(!vis[y]) vis[y]=true,q.push(y);
}
}
return dis[t]!=inf;
}
ll dfs(int x,ll lim)
{
if(x==t) return lim;
vis[x]=true;
ll res=lim,flow;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
ll v=e[i].v;
if(dis[y]!=dis[x]+e[i].c||!v||vis[y]) continue;
if(flow=dfs(y,min(res,v)))
{
res-=flow,e[i].v-=flow,e[i^1].v+=flow;
if(!res) break;
}
}
return lim-res;
}
void dinic()
{
ll flow;
while(bfs())
while(flow=dfs(s,inf))
ans+=flow,sum+=flow*dis[t];
}
int main()
{
read(n),read(m),s=1,t=n;
for(int i=1;i<=m;++i)
{
int x,y,v,w;
read(x),read(y),read(v),read(w);
add(x,y,v,w);
}
dinic(),printf("%lld %lld",ans,sum);
return 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
#include<bits/stdc++.h>
#define maxn 100010
#define maxm 1002010
#define inf 2000000000
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag) x=-x;
}
int n,m,s,t;
struct edge
{
int to,nxt,v;
}e[maxm];
int head[maxn],edge_cnt=1;
void add(int from,int to,int val)
{
e[++edge_cnt]=(edge){to,head[from],val};
head[from]=edge_cnt;
e[++edge_cnt]=(edge){from,head[to],0};
head[to]=edge_cnt;
}
int d[maxn],cur[maxn],in[maxn],low[maxn];
bool bfs()
{
for(int i=s;i<=t;++i) cur[i]=head[i];
memset(d,0,sizeof(d));
queue<int> q;
q.push(s);
d[s]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to,v=e[i].v;
if(d[y]||!v) continue;
q.push(y);
d[y]=d[x]+1;
}
}
return d[t];
}
int dfs(int x,int lim)
{
if(x==t) return lim;
int res=lim,k;
for(int &i=cur[x];i;i=e[i].nxt)
{
int y=e[i].to,v=e[i].v;
if(d[y]!=d[x]+1||!v) continue;
if(k=dfs(y,min(res,v)))
{
res-=k;
e[i].v-=k;
e[i^1].v+=k;
if(res<=0) break;
}
}
return lim-res;
}
int dinic()
{
int k,ans=0;
while(bfs())
{
while(k=dfs(s,inf))
{
ans+=k;
}
}
return ans;
}
bool check()
{
for(int i=head[s];i;i=e[i].nxt)
if(e[i].v)
return false;
return true;
}
int main()
{
read(n),read(m),t=n+1;
for(int i=1;i<=m;++i)
{
int a,b,high;
read(a),read(b),read(low[i]),read(high);
in[a]-=low[i],in[b]+=low[i];
add(a,b,high-low[i]);
}
for(int i=1;i<=n;i++)
{
if(in[i]>0) add(s,i,in[i]);
else add(i,t,-in[i]);
}
dinic();
if(check())
{
puts("YES");
for(int i=1;i<=m;i++) printf("%d\n",e[(i<<1)^1].v+low[i]);
}
else puts("NO");
return 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
111
#include<bits/stdc++.h>
#define maxn 100010
#define maxm 1002010
#define inf 2000000000
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag) x=-x;
}
int n,m,s,t,S,T,ans;
struct edge
{
int to,nxt,v;
}e[maxm];
int head[maxn],edge_cnt=1;
void add(int from,int to,int val)
{
e[++edge_cnt]=(edge){to,head[from],val};
head[from]=edge_cnt;
e[++edge_cnt]=(edge){from,head[to],0};
head[to]=edge_cnt;
}
int d[maxn],cur[maxn],in[maxn],low[maxn];
bool bfs()
{
memcpy(cur,head,sizeof(head));
memset(d,0,sizeof(d));
queue<int> q;
q.push(s);
d[s]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to,v=e[i].v;
if(d[y]||!v) continue;
q.push(y);
d[y]=d[x]+1;
}
}
return d[t];
}
int dfs(int x,int lim)
{
if(x==t) return lim;
int res=lim,k;
for(int &i=cur[x];i;i=e[i].nxt)
{
int y=e[i].to,v=e[i].v;
if(d[y]!=d[x]+1||!v) continue;
if(k=dfs(y,min(res,v)))
{
res-=k;
e[i].v-=k;
e[i^1].v+=k;
if(res<=0) break;
}
}
return lim-res;
}
int dinic()
{
int k,flow=0;
while(bfs())
{
while(k=dfs(s,inf))
{
flow+=k;
}
}
return flow;
}
bool check()
{
for(int i=head[s];i;i=e[i].nxt)
if(e[i].v)
return false;
return true;
}
int main()
{
read(n),read(m),read(S),read(T),t=n+1;
for(int i=1;i<=m;++i)
{
int a,b,up;
read(a),read(b),read(low[i]),read(up);
in[a]-=low[i],in[b]+=low[i];
add(a,b,up-low[i]);
}
for(int i=1;i<=n;i++)
{
if(in[i]>0) add(s,i,in[i]);
else add(i,t,-in[i]);
}
add(T,S,inf);
dinic();
ans=e[edge_cnt].v;
e[edge_cnt].v=e[edge_cnt^1].v=0;
if(check())
{
s=S,t=T;
printf("%d",ans+dinic());
}
else puts("please go home to sleep");
return 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<bits/stdc++.h>
#define maxn 500010
#define maxm 5002010
#define inf 2000000000
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag) x=-x;
}
int n,m,s,t,S,T;
struct edge
{
int to,nxt,v;
}e[maxm];
int head[maxn],edge_cnt=1;
void add(int from,int to,int val)
{
e[++edge_cnt]=(edge){to,head[from],val};
head[from]=edge_cnt;
e[++edge_cnt]=(edge){from,head[to],0};
head[to]=edge_cnt;
}
int d[maxn],cur[maxn],in[maxn];
bool bfs()
{
for(int i=0;i<=n+1;++i) cur[i]=head[i];
memset(d,0,sizeof(d));
queue<int> q;
q.push(s);
d[s]=1;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to,v=e[i].v;
if(d[y]||!v) continue;
q.push(y);
d[y]=d[x]+1;
}
}
return d[t];
}
int dfs(int x,int lim)
{
if(x==t) return lim;
int res=lim,k;
for(int &i=cur[x];i;i=e[i].nxt)
{
int y=e[i].to,v=e[i].v;
if(d[y]!=d[x]+1||!v) continue;
if(k=dfs(y,min(res,v)))
{
res-=k;
e[i].v-=k;
e[i^1].v+=k;
if(!res) break;
}
}
return lim-res;
}
int dinic()
{
int k,flow=0;
while(bfs())
{
while(k=dfs(s,inf))
{
flow+=k;
}
}
return flow;
}
bool check()
{
for(int i=head[s];i;i=e[i].nxt)
if(e[i].v)
return false;
return true;
}
int main()
{
read(n),read(m),read(S),read(T),t=n+1;
for(int i=1;i<=m;++i)
{
int a,b,up,low;
read(a),read(b),read(low),read(up);
in[a]-=low,in[b]+=low;
add(a,b,up-low);
}
for(int i=1;i<=n;i++)
{
if(in[i]>0) add(s,i,in[i]);
else add(i,t,-in[i]);
}
dinic();
add(T,S,inf);
dinic();
if(!check())
{
puts("please go home to sleep");
return 0;
}
printf("%d",e[edge_cnt].v);
return 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
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
#include<bits/stdc++.h>
#define maxn 500010
#define maxm 5000010
#define inf 1000000000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int T,n,m,s,t;
ll ans;
int in[maxn],de[maxn];
ll dis[maxn];
bool vis[maxn];
struct edge
{
int to,nxt,v;
ll c;
}e[maxm];
int head[maxn],edge_cnt;
void add(int from,int to,int val,int cost)
{
e[++edge_cnt]=(edge){to,head[from],val,cost};
head[from]=edge_cnt;
e[++edge_cnt]=(edge){from,head[to],0,-cost};
head[to]=edge_cnt;
}
void Add(int from,int to,int val,ll cost)
{
in[from]+=val,in[to]-=val,ans+=cost,add(from,to,val,cost);
}
struct Edge
{
int to,nxt,v;
}ed[maxn];
int hd[maxn],e_cnt;
void link(int from,int to,int val)
{
ed[++e_cnt]=(Edge){to,hd[from],val};
hd[from]=e_cnt;
}
void dfs_pre(int x,int fa)
{
de[x]=de[fa]+1;
for(int i=hd[x];i;i=ed[i].nxt)
{
int y=ed[i].to;
if(y==fa) continue;
Add(x,y,ed[i].v,0),dfs_pre(y,x);
}
}
bool spfa()
{
for(int i=s;i<=t;++i) vis[i]=0,dis[i]=inf;
queue<int> q;
q.push(s),dis[s]=0,vis[s]=true;
while(!q.empty())
{
int x=q.front();
q.pop(),vis[x]=false;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
ll v=e[i].v,c=e[i].c;
if(dis[y]>dis[x]+c&&v)
{
dis[y]=dis[x]+c;
if(!vis[y])
{
vis[y]=true;
q.push(y);
}
}
}
}
return dis[t]!=inf;
}
ll dfs(int x,ll lim)
{
if(x==t) return lim;
vis[x]=true;
ll res=lim,flow;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
ll v=e[i].v,c=e[i].c;
if(dis[y]!=dis[x]+c||!v||vis[y]) continue;
if(flow=dfs(y,min(res,v)))
{
res-=flow;
e[i].v-=flow;
e[i^1].v+=flow;
if(!res) break;
}
}
return lim-res;
}
ll dinic()
{
ll flow,sum=0;
while(spfa())
while(flow=dfs(s,inf))
sum+=flow*dis[t];
return sum;
}
void clear()
{
e_cnt=ans=0,edge_cnt=1;
memset(in,0,sizeof(in));
memset(hd,0,sizeof(hd));
memset(head,0,sizeof(head));
}
int main()
{
read(T);
while(T--)
{
clear(),read(n),read(m),t=n+1;
for(int i=1;i<n;++i)
{
int x,y,v;
read(x),read(y),read(v);
link(x,y,v),link(y,x,v);
}
dfs_pre(1,0);
for(int i=1;i<=m;++i)
{
int x,y,v;
read(x),read(y),read(v);
if(de[x]<de[y]) swap(x,y);
Add(x,y,1,v);
}
for(int i=1;i<=n;++i)
{
if(in[i]>0) add(s,i,in[i],0);
else add(i,t,-in[i],0);
}
printf("%lld\n",ans-dinic());
}
return 0;
}

最小割树

通过建最小割树来快速求解无向图中两点间的最小割。最小割树中的一条边的权值,为其两端点的最小割,那么任意两点之间的最小割即为路径上的最小值。通过递归建树即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void build(int l,int r)
{
if(l==r) return;
s=p[l],t=p[l+1];
int v=F.dinic(),cnt1=0,cnt2=0;
add(s,t,v),add(t,s,v);
for(int i=l;i<=r;++i)
{
int x=p[i];
if(F.d[x]) p1[++cnt1]=x;
else p2[++cnt2]=x;
}
for(int i=1;i<=cnt1;++i) p[l+i-1]=p1[i];
for(int i=1;i<=cnt2;++i) p[l+cnt1+i-1]=p2[i];
build(l,l+cnt1-1),build(l+cnt1,r);
}

树的直径

可以通过两次 求出树的直径,从任意结点出发,搜索得出直径的一个端点,然后从这个端点继续搜索,得出另一个端点,但无法处理负边权。也可以用树形求直径,考虑每个结点,求出经过结点的最长链长度,则树的直径长度就是这些最长链长度的最大值,但无法求出直径的端点。

1
2
3
4
5
6
7
8
9
10
11
void dp(int x,int fa)
{
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to,v=e[i].v;
if(y==fa) continue;
dp(y,x);
maxd=max(maxd,dis[x]+dis[y]+v);
dis[x]=max(dis[x],dis[y]+v);
}
}

树上 k 级祖先

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
#include<bits/stdc++.h>
#define maxn 500010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,q,root,cnt;
ll ans,last;
int son[maxn],top[maxn],de[maxn],dep[maxn];
int lg[maxn],f[maxn][22],u[maxn],d[maxn],dfn[maxn];
unsigned int s;
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=(edge){to,head[from]};
head[from]=edge_cnt;
}
unsigned int get()
{
s^=s<<13,s^=s>>17,s^=s<<5;
return s;
}
void dfs_son(int x)
{
de[x]=dep[x]=de[f[x][0]]+1;
for(int i=1;i<=19;++i) f[x][i]=f[f[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
dfs_son(y),dep[x]=max(dep[x],dep[y]);
if(dep[y]>dep[son[x]]) son[x]=y;
}
}
void dfs_dfn(int x,int tp,int anc)
{
top[x]=tp,dfn[x]=++cnt,u[cnt]=anc,d[cnt]=x;
if(son[x]) dfs_dfn(son[x],tp,f[anc][0]);
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==son[x]) continue;
dfs_dfn(y,y,y);
}
}
int ask(int x,int k)
{
if(!k) return x;
x=f[x][lg[k]],k-=(1<<lg[k])+de[x]-de[top[x]],x=top[x];
if(k>=0) return u[dfn[x]+k];
else return d[dfn[x]-k];
}
int main()
{
read(n),read(q),read(s),lg[0]=-1;
for(int i=1;i<=n;++i) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;++i)
{
read(f[i][0]);
if(!f[i][0]) root=i;
else add(f[i][0],i);
}
dfs_son(root),dfs_dfn(root,root,root);
for(int i=1;i<=q;++i)
{
int x=(get()^last)%n+1,k=(get()^last)%de[x];
last=ask(x,k),ans^=last*i;
}
printf("%lld",ans);
return 0;
}

无向图三元环计数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for(int i=1;i<=m;++i)
{
int x=ed[i].x,y=ed[i].y;
if(deg[x]>deg[y]||(deg[x]==deg[y]&&x>y)) swap(x,y);
add(x,y);
}
for(int x=1;x<=n;++x)
{
for(int i=head[x];i;i=e[i].nxt) vis[e[i].to]=x;
for(int i=head[x];i;i=e[i].nxt)
for(int j=head[e[i].to];j;j=e[j].nxt)
if(vis[e[j].to]==x)
ans++;
}

欧拉回路

为无向图, 为有向图。

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
#include<bits/stdc++.h>
#define maxn 400010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,s,t,cnt;
int deg[maxn],p[maxn];
bool vis[maxn];
struct edge
{
int to,nxt;
edge(int a=0,int b=0)
{
to=a,nxt=b;
}
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=edge(to,head[from]),head[from]=edge_cnt;
}
void dfs1(int x)
{
for(int &i=head[x];i;i=e[i].nxt)
{
if(vis[i]) continue;
int l=i;
vis[i]=vis[i^1]=true,dfs1(e[i].to),p[++cnt]=l&1?-(l>>1):(l>>1);
}
}
void dfs2(int x)
{
for(int &i=head[x];i;i=e[i].nxt)
{
if(vis[i]) continue;
int l=i;
vis[i]=true,dfs2(e[i].to),p[++cnt]=l;
}
}
bool check()
{
for(int i=1;i<=n;++i)
if(deg[i])
return false;
return true;
}
int main()
{
read(t),read(n),read(m),edge_cnt=t==1;
for(int i=1;i<=m;++i)
{
int x,y;
read(y),read(x),add(x,y),s=x;
if(t==1) add(y,x),deg[x]^=1,deg[y]^=1;
else deg[x]++,deg[y]--;
}
if(!check())
{
puts("NO");
return 0;
}
if(t==1) dfs1(s);
else dfs2(s);
if(cnt!=m)
{
puts("NO");
return 0;
}
puts("YES");
for(int i=1;i<=m;++i) printf("%d ",p[i]);
return 0;
}

Kruskal 重构树

构建生成树的顺序来构建 重构树。两个连通块合并时,新建一个节点,点权为联通这两个连通块边的边权,新节点向两个连通块的根连边,新节点为合并后的连通块的根。得到的树为有 个叶子节点的二叉树,其满足堆的性质。求一个点 在只经过边权不大于 的边所能到达的点。边权从小到大排序,建 重构树,得到的是一个大根堆,从 向上倍增,到达满足点权不大于 深度最浅的节点,该节点子树中的所有叶子节点 都可到达。

1
2
3
4
5
6
7
8
for(int i=1;i<=m;++i)
{
int x=find(ed[i].x),y=find(ed[i].y);
if(x==y) continue;
val[++tot]=ed[i].v,add(tot,x),add(tot,y);
fa[x]=fa[y]=f[x][0]=f[y][0]=tot;
if(tot==2*n-1) break;
}

静态仙人掌

询问两点最短路。

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
#include<bits/stdc++.h>
#define maxn 40010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,q,tot,dfn_cnt;
int dfn[maxn],low[maxn],fa[maxn],pre[maxn],sum[maxn],dis[maxn],de[maxn],f[maxn][18];
struct node
{
struct edge
{
int to,nxt,v;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to,int val)
{
e[++edge_cnt]=(edge){to,head[from],val};
head[from]=edge_cnt;
}
}T1,T2;
void build(int x,int y,int v)
{
int s=v;
for(int i=y;i!=fa[x];i=fa[i]) sum[i]=s,s+=pre[i];
sum[++tot]=sum[x],sum[x]=0;
for(int i=y;i!=fa[x];i=fa[i])
T2.add(tot,i,min(sum[i],sum[tot]-sum[i])),T2.add(i,tot,min(sum[i],sum[tot]-sum[i]));
}
void tarjan(int x,int fath)
{
dfn[x]=low[x]=++dfn_cnt;
for(int i=T1.head[x];i;i=T1.e[i].nxt)
{
int y=T1.e[i].to,v=T1.e[i].v;
if(y==fath) continue;
if(!dfn[y])
{
fa[y]=x,pre[y]=v,tarjan(y,x);
low[x]=min(low[x],low[y]);
}
else low[x]=min(low[x],dfn[y]);
if(dfn[x]<low[y]) T2.add(x,y,v),T2.add(y,x,v);
}
for(int i=T1.head[x];i;i=T1.e[i].nxt)
{
int y=T1.e[i].to;
if(fa[y]!=x&&dfn[x]<dfn[y])
build(x,y,T1.e[i].v);
}
}
void dfs(int x,int fath)
{
f[x][0]=fath,de[x]=de[fath]+1;
for(int i=1;i<=15;++i) f[x][i]=f[f[x][i-1]][i-1];
for(int i=T2.head[x];i;i=T2.e[i].nxt)
{
int y=T2.e[i].to;
if(y==fath) continue;
dis[y]=dis[x]+T2.e[i].v,dfs(y,x);
}
}
int lca(int x,int y)
{
if(de[x]<de[y]) swap(x,y);
for(int i=15;i>=0;--i)
if(de[f[x][i]]>=de[y])
x=f[x][i];
if(x==y) return x;
for(int i=15;i>=0;--i)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int find(int x,int k)
{
for(int i=0;i<=15;++i)
if(k&(1<<i))
x=f[x][i];
return x;
}
int main()
{
read(n),read(m),read(q),tot=n;
for(int i=1;i<=m;++i)
{
int x,y,v;
read(x),read(y),read(v);
T1.add(x,y,v),T1.add(y,x,v);
}
tarjan(1,0),dfs(1,0);
while(q--)
{
int x,y,anc;
read(x),read(y),anc=lca(x,y);
if(anc<=n) printf("%d\n",dis[x]+dis[y]-dis[anc]*2);
else
{
int sx=find(x,de[x]-de[anc]-1),sy=find(y,de[y]-de[anc]-1);
if(sum[sx]<sum[sy]) swap(sx,sy);
printf("%d\n",dis[x]-dis[sx]+dis[y]-dis[sy]+min(sum[sx]-sum[sy],sum[anc]+sum[sy]-sum[sx]));
}
}
return 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
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
#include<bits/stdc++.h>
#define maxn 2000010
#define inf 200000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag) x=-x;
}
int n,q;
ll ans,tot,maxd,mind;
ll dma[maxn],dmi[maxn];
bool flag;
int query[maxn];
struct edge
{
int to,nxt;
ll v;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to,ll val=0)
{
e[++edge_cnt]=(edge){to,head[from],val};
head[from]=edge_cnt;
}
int dfn_cnt;
int dfn[maxn],top_fa[maxn],fa[maxn],son[maxn];
ll de[maxn],siz[maxn];
void dfs_son(int x,int fath)
{
siz[x]=1;
fa[x]=fath;
de[x]=de[fath]+1;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fath) continue;
dfs_son(y,x);
siz[x]+=siz[y];
if(siz[son[x]]<siz[y]) son[x]=y;
}
}
void dfs_chain(int x,int tp)
{
dfn[x]=++dfn_cnt,top_fa[x]=tp;
if(son[x]) dfs_chain(son[x],tp);
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(dfn[y]) continue;
dfs_chain(y,y);
}
}
int lca(int x,int y)
{
while(top_fa[x]!=top_fa[y])
{
if(de[top_fa[x]]<de[top_fa[y]]) swap(x,y);
x=fa[top_fa[x]];
}
if(dfn[x]>dfn[y]) swap(x,y);
return x;
}
ll dis(int x,int y)
{
return de[x]+de[y]-de[lca(x,y)]*2;
}
bool cmp(const int &a,const int &b)
{
return dfn[a]<dfn[b];
}
int st[maxn],top;
void insert(int x)
{
if(x==1) return;
if(top==1)
{
st[++top]=x;
return;
}
int anc=lca(x,st[top]);
if(anc==st[top])
{
st[++top]=x;
return;
}
while(top>1&&dfn[anc]<=dfn[st[top-1]])
add(st[top-1],st[top],dis(st[top-1],st[top])),top--;
if(anc!=st[top]) add(anc,st[top],dis(anc,st[top])),st[top]=anc;
st[++top]=x;
}
bool vis[maxn];
void dp(int x)
{
dma[x]=-inf,dmi[x]=inf;
if(vis[x]) dma[x]=dmi[x]=0;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
ll v=e[i].v;
dp(y);
maxd=max(maxd,dma[x]+dma[y]+v);
dma[x]=max(dma[x],dma[y]+v);
mind=min(mind,dmi[x]+dmi[y]+v);
dmi[x]=min(dmi[x],dmi[y]+v);
}
}
void dfs_ans(int x)
{
if(vis[x]) siz[x]=1;
else siz[x]=0;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
ll v=e[i].v;
dfs_ans(y);
siz[x]+=siz[y];
ans+=siz[y]*(tot-siz[y])*v;
}
head[x]=0;
}
void clear()
{
edge_cnt=0;
memset(siz,0,sizeof(siz));
memset(head,0,sizeof(head));
}
int main()
{
read(n);
for(int i=1;i<n;++i)
{
int a,b;
read(a),read(b);
add(a,b),add(b,a);
}
dfs_son(1,0),dfs_chain(1,1),clear();
read(q);
while(q--)
{
int k;
read(k);
tot=k;
edge_cnt=ans=0;
mind=inf,maxd=-inf;
for(int i=1;i<=k;++i)
{
read(query[i]);
vis[query[i]]=true;
}
sort(query+1,query+k+1,cmp);
st[top=1]=1;
for(int i=1;i<=k;++i) insert(query[i]);
while(top) add(st[top-1],st[top],dis(st[top-1],st[top])),top--;
dp(1),dfs_ans(1);
for(int i=1;i<=k;++i) vis[query[i]]=false;
printf("%lld %lld %lld\n",ans,mind,maxd);
}
return 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
#include<bits/stdc++.h>
#define maxn 20010
#define inf 200000000
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag) x=-x;
}
int n,m,root;
struct edge
{
int x,y,v;
}e[maxn];
int id[maxn],pre[maxn],ine[maxn],vis[maxn];
int zhuliu()
{
int ans=0,cnt;
while(1)
{
cnt=0;
for(int i=1;i<=n;++i) ine[i]=inf,id[i]=vis[i]=0;
for(int i=1;i<=m;++i)
{
int x=e[i].x,y=e[i].y,v=e[i].v;
if(x!=y&&v<ine[y]) ine[y]=v,pre[y]=x;
}
for(int i=1;i<=n;++i)
if(i!=root&&ine[i]==inf)
return -1;
for(int i=1;i<=n;++i)
{
if(i==root) continue;
ans+=ine[i];
int y=i;
while(vis[y]!=i&&!id[y]&&y!=root)
{
vis[y]=i;
y=pre[y];
}
if(!id[y]&&y!=root)
{
id[y]=++cnt;
for(int x=pre[y];x!=y;x=pre[x]) id[x]=cnt;
}
}
if(!cnt) break;
for(int i=1;i<=n;++i)
if(!id[i])
id[i]=++cnt;
for(int i=1;i<=m;++i)
{
int x=e[i].x,y=e[i].y;
e[i].x=id[x],e[i].y=id[y];
if(id[x]!=id[y]) e[i].v-=ine[y];
}
root=id[root];
n=cnt;
}
return ans;
}
int main()
{
read(n),read(m),read(root);
for(int i=1;i<=m;++i)
read(e[i].x),read(e[i].y),read(e[i].v);
printf("%d",zhuliu());
return 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
#include<bits/stdc++.h>
#define maxn 200010
#define maxm 600010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m;
struct node
{
struct edge
{
int to,nxt;
}e[maxm];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=(edge){to,head[from]};
head[from]=edge_cnt;
}
}a,b,c,d;
int dfn_cnt;
int dfn[maxn],id[maxn],fa[maxn];
int sdom[maxn],idom[maxn],fath[maxn],val[maxn],ans[maxn];
void dfs_dfn(int x)
{
dfn[x]=++dfn_cnt;
id[dfn_cnt]=x;
for(int i=a.head[x];i;i=a.e[i].nxt)
{
int y=a.e[i].to;
if(dfn[y]) continue;
fa[y]=x;
dfs_dfn(y);
}
}
int find(int x)
{
if(x==fath[x]) return x;
int tmp=find(fath[x]);
if(dfn[sdom[val[fath[x]]]]<dfn[sdom[val[x]]])
val[x]=val[fath[x]];
return fath[x]=tmp;
}
void tarjan()
{
for(int i=1;i<=n;++i) sdom[i]=fath[i]=val[i]=i;
for(int i=dfn_cnt;i>1;--i)
{
int x=id[i];
for(int i=b.head[x];i;i=b.e[i].nxt)
{
int y=b.e[i].to;
if(!dfn[y]) continue;
find(y);
if(dfn[sdom[val[y]]]<dfn[sdom[x]])
sdom[x]=sdom[val[y]];
}
c.add(sdom[x],x);
fath[x]=fa[x];
x=fa[x];
for(int i=c.head[x];i;i=c.e[i].nxt)
{
int y=c.e[i].to;
find(y);
if(sdom[val[y]]==x) idom[y]=x;
else idom[y]=val[y];
}
c.head[x]=0;
}
for(int i=2;i<=dfn_cnt;++i)
{
int x=id[i];
if(idom[x]!=sdom[x])
idom[x]=idom[idom[x]];
}
for(int i=2;i<=n;++i) d.add(idom[i],i);
}
void dfs_ans(int x)
{
ans[x]=1;
for(int i=d.head[x];i;i=d.e[i].nxt)
{
int y=d.e[i].to;
dfs_ans(y);
ans[x]+=ans[y];
}
}
int main()
{
read(n),read(m);
for(int i=1;i<=m;++i)
{
int x,y;
read(x),read(y);
a.add(x,y),b.add(y,x);
}
dfs_dfn(1);
tarjan();
dfs_ans(1);
for(int i=1;i<=n;++i) printf("%d ",ans[i]);
return 0;
}

动态规划

单调队列优化 DP

优化形如 方程。

1
2
3
4
5
while(h<=t&&f[x][y]>q[t].val+dis(x,y,q[t].x,q[t].y)) t--;
q[++t]=(que){f[x][y],x,y};
while(h<=t&&(abs(x-q[h].x)>len||abs(y-q[h].y)>len)) h++;
f[x][y]=max(f[x][y],q[h].val+dis(x,y,q[h].x,q[h].y));
ans=max(ans,f[x][y]);

数位 DP

不含前导零且相邻两个数字之差至少为 的正整数被称为 windy 数。windy 想知道,在 之间,包括 ,总共有多少个 windy 数?

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
#include<bits/stdc++.h>
#define maxn 15
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll a,b,cnt;
ll num[maxn],f[maxn][maxn][2][2][2];
ll dp(int pos,int las,bool lim,bool lead,bool flag)
{
if(!pos) return flag;
if(f[pos][las][lim][lead][flag]!=-1) return f[pos][las][lim][lead][flag];
ll v=0,ma=9;
if(lim) ma=num[pos];
for(int i=0;i<=ma;++i)
{
if(!i&&lead) v+=dp(pos-1,12,0,1,1);
else v+=dp(pos-1,i,lim&&i==ma,0,flag&&(abs(las-i)>=2));
}
return f[pos][las][lim][lead][flag]=v;
}
ll solve(ll x)
{
cnt=0,memset(f,-1,sizeof(f));
while(x) num[++cnt]=x%10,x/=10;
return dp(cnt,12,1,1,1);
}
int main()
{
read(a),read(b),printf("%lld",solve(b)-solve(a-1));
return 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
#include<bits/stdc++.h>
#define maxn 1000010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll n,h,t;
ll f[maxn],p[maxn],s[maxn],c[maxn],dis[maxn],q[maxn];
double x(int i)
{
return p[i];
}
double y(int i)
{
return f[i]+s[i];
}
double slope(int j,int k)
{
return (y(j)-y(k))/(x(j)-x(k));
}
int main()
{
read(n);
for(int i=1;i<=n;++i)
{
read(dis[i]),read(p[i]),read(c[i]);
s[i]=s[i-1]+dis[i]*p[i],p[i]+=p[i-1];
}
for(int i=1;i<=n;++i)
{
while(h<t&&slope(q[h],q[h+1])<dis[i]) h++;
f[i]=f[q[h]]+dis[i]*(p[i]-p[q[h]])-(s[i]-s[q[h]])+c[i];
while(h<t&&slope(q[t],q[t-1])>slope(q[t],i)) t--;
q[++t]=i;
}
printf("%lld",f[n]);
return 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
#include<bits/stdc++.h>
#define maxn 1000010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll n,h,t,a,b,c;
ll f[maxn],s[maxn],q[maxn];
ll calc(ll x)
{
return x*x;
}
double x(int i)
{
return s[i];
}
double y(int i)
{
return f[i]+a*calc(s[i])-b*s[i];
}
double slope(int j,int k)
{
return (y(j)-y(k))/(x(j)-x(k));
}
int main()
{
read(n),read(a),read(b),read(c);
for(int i=1;i<=n;++i) read(s[i]),s[i]+=s[i-1];
for(int i=1;i<=n;++i)
{
while(h<t&&slope(q[h],q[h+1])>2*a*s[i]) h++;
f[i]=f[q[h]]+a*calc(s[i]-s[q[h]])+b*(s[i]-s[q[h]])+c;
while(h<t&&slope(q[t],q[t-1])<slope(q[t],i)) t--;
q[++t]=i;
}
printf("%lld",f[n]);
return 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
double calc(int i,int j)
{
return a[j]-a[i]+sqrt(i-j);
}
int find(node t,int x)
{
int l=t.l,r=t.r,ans=t.r+1;
while(l<=r)
{
int mid=(l+r)>>1;
if(calc(mid,x)>=calc(mid,t.pos)) r=mid-1,ans=mid;
else l=mid+1;
}
return ans;
}
void dp(double *f)
{
q[h=t=1]=node(1,n,0);
for(int i=1;i<=n;++i)
{
while(h<=t&&q[h].r<i) h++;
f[i]=calc(i,q[h].pos);
if(calc(n,i)>=calc(n,q[t].pos))
{
while(h<=t&&calc(q[t].l,i)>=calc(q[t].l,q[t].pos)) t--;
if(h>t) q[++t]=node(i,n,i);
else
{
int x=find(q[t],i);
q[t].r=x-1,q[++t]=node(x,n,i);
}
}
}
}

分治:

对于区间 ,已知其最优决策范围为 。取 后扫一遍区间 来得出 值和最优决策点 ,那么左区间的最优决策范围为 ,右区间的最优决策范围为 。应用分治时要求保证区间 的信息都已存在,因此应用分治大部分时候都是分层的两维

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
double calc(int i,int j)
{
return a[j]-a[i]+sqrt(i-j);
}
void solve(int l,int r,int L,int R,double *f)
{
if(l>r) return;
int pos,mid=(l+r)>>1;
for(int i=L;i<=min(mid,R);++i)
{
double v=calc(mid,i);
if(v>f[mid]) f[mid]=v,pos=i;
}
solve(l,mid-1,L,pos,f),solve(mid+1,r,pos,R,f);
}

可以处理无法快速 计算 ,其中 为区间 相同元素的对数。

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
void add(int c)
{
sum+=cnt[c],cnt[c]++;
}
void del(int c)
{
sum-=cnt[c]-1,cnt[c]--;
}
ll get(int ql,int qr)
{
while(l<ql) del(a[l++]);
while(r>qr) del(a[r--]);
while(l>ql) add(a[--l]);
while(r<qr) add(a[++r]);
return sum;
}
void solve(int id,int l,int r,int L,int R)
{
if(l>r) return;
int pos,mid=(l+r)>>1;
for(int i=L;i<=min(mid,R);++i)
{
ll v=f[i][id-1]+get(i+1,mid);
if(v<f[mid][id]) f[mid][id]=v,pos=i;
}
solve(id,l,mid-1,L,pos),solve(id,mid+1,r,pos,R);
}

指针的移动均摊是 的。

长链剖分

给定一棵以 为根, 个节点的树。设 子树中到 距离为 的节点数。 对于每个点,求一个最小的 ,使得 最大。

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
#include<bits/stdc++.h>
#define maxn 2000010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n;
int len[maxn],son[maxn],ans[maxn];
vector<int> f[maxn];
struct edge
{
int to,nxt;
edge(int a=0,int b=0)
{
to=a,nxt=b;
}
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=edge(to,head[from]),head[from]=edge_cnt;
}
void dfs_son(int x,int fa)
{
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fa) continue;
dfs_son(y,x);
if(len[y]>=len[son[x]]) son[x]=y,len[x]=len[y]+1;
}
}
void dfs(int x,int fa)
{
if(!son[x])
{
f[x].push_back(1);
return;
}
dfs(son[x],x),swap(f[x],f[son[x]]),f[x].push_back(1),ans[x]=ans[son[x]]+1;
if(f[x][len[x]-ans[x]]==1) ans[x]=0;
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fa||y==son[x]) continue;
dfs(y,x);
for(int j=0;j<=len[y];++j)
{
f[x][len[x]-j-1]+=f[y][len[y]-j];
if(f[x][len[x]-j-1]>f[x][len[x]-ans[x]]||(f[x][len[x]-j-1]==f[x][len[x]-ans[x]]&&j+1<ans[x]))
ans[x]=j+1;
}
}
}
int main()
{
read(n);
for(int i=1;i<n;++i)
{
int x,y;
read(x),read(y);
add(x,y),add(y,x);
}
dfs_son(1,0),dfs(1,0);
for(int i=1;i<=n;++i) printf("%d\n",ans[i]);
return 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
#include<bits/stdc++.h>
#define maxn 600010
#define inf 1000000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,k;
ll l,r,ans;
struct edge
{
int to,nxt;
ll v;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to,int val)
{
e[++edge_cnt]=(edge){to,head[from],val};
head[from]=edge_cnt;
}
struct node
{
ll val,cnt;
}f[maxn][3],g[3];
bool operator <(const node &a,const node &b)
{
if(a.val==b.val) return a.cnt<b.cnt;
return a.val<b.val;
}
node operator +(const node &a,const node &b)
{
return (node){a.val+b.val,a.cnt+b.cnt};
}
void dfs(int x,int fa,ll delta)
{
f[x][0]=(node){0,0},f[x][1]=(node){-inf,-inf},f[x][2]=(node){delta,1};
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
ll v=e[i].v;
if(y==fa) continue;
dfs(y,x,delta),g[0]=g[1]=g[2]=(node){-inf,-inf};
for(int j=0;j<3;++j)
for(int k=0;k<3;++k)
g[j]=max(g[j],f[x][j]+f[y][k]);
g[1]=max(g[1],max(f[x][0]+f[y][0]+(node){v+delta,1},f[x][0]+f[y][1]+(node){v,0}));
g[2]=max(g[2],max(f[x][1]+f[y][1]+(node){v-delta,-1},f[x][1]+f[y][0]+(node){v,0}));
f[x][0]=g[0],f[x][1]=g[1],f[x][2]=g[2];
}
}
bool check(ll x)
{
dfs(1,0,x);
return max(f[1][0],max(f[1][1],f[1][2])).cnt>=k;
}
int main()
{
read(n),read(k),k++;
for(int i=1;i<n;++i)
{
int x,y,v;
read(x),read(y),read(v);
add(x,y,v),add(y,x,v),r+=abs(v);
}
l=-r;
while(l<=r)
{
ll mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
check(ans),printf("%lld",max(f[1][0],max(f[1][1],f[1][2])).val-ans*k);
return 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
#include<bits/stdc++.h>
#define maxn 500010
#define inf 1000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,k,pos,l=-inf,r=0;
ll ans,sum,cnt;
struct node
{
ll v,p;
}a[maxn],b[maxn];
bool cmp(const node &a,const node &b)
{
if(a.p==b.p) return a.v<b.v;
return a.p<b.p;
}
bool check(int x)
{
priority_queue<int> q;
sum=cnt=0,pos=1;
for(int i=1;i<=n;++i)
{
while(pos<=m&&a[i].p>=b[pos].p) q.push(b[pos++].v);
if(q.empty()) continue;
if(q.top()-a[i].v+x>=0)
sum+=q.top()-a[i].v+x,cnt++,q.pop();
}
return cnt<=k;
}
int main()
{
read(n),read(m),read(k);
for(int i=1;i<=n;++i) read(a[i].v),read(a[i].p);
for(int i=1;i<=m;++i) read(b[i].v),read(b[i].p);
sort(a+1,a+n+1,cmp),sort(b+1,b+m+1,cmp);
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
check(ans),printf("%lld\n",sum-ans*cnt);
return 0;
}

整体 DP

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
#include<bits/stdc++.h>
#define maxn 100010
#define maxm 15000010
#define mid ((l+r)>>1)
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,k,tot;
int d[maxn],w[maxn],rt[maxn],ls[maxm],rs[maxm];
ll mx[maxm],tag[maxm];
struct edge
{
int to,nxt;
edge(int a=0,int b=0)
{
to=a,nxt=b;
}
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=edge(to,head[from]),head[from]=edge_cnt;
}
void pushtag(int cur,ll v)
{
if(cur) mx[cur]+=v,tag[cur]+=v;
}
void pushdown(int cur)
{
if(tag[cur]) pushtag(ls[cur],tag[cur]),pushtag(rs[cur],tag[cur]),tag[cur]=0;
}
void modify(int l,int r,int p,ll v,int &cur)
{
if(!cur) cur=++tot;
if(l==r)
{
mx[cur]=v;
return;
}
pushdown(cur);
if(p<=mid) modify(l,mid,p,v,ls[cur]);
else modify(mid+1,r,p,v,rs[cur]);
mx[cur]=max(mx[ls[cur]],mx[rs[cur]]);
}
ll query(int L,int R,int l,int r,int cur)
{
if(!cur) return 0;
if(L<=l&&R>=r) return mx[cur];
ll v=0;
pushdown(cur);
if(L<=mid) v=max(v,query(L,R,l,mid,ls[cur]));
if(R>mid) v=max(v,query(L,R,mid+1,r,rs[cur]));
return v;
}
int get(int x,int y,int l,int r,ll v1,ll v2)
{
if(!x&&!y) return 0;
if(x&&!y)
{
pushtag(x,v2);
return x;
}
if(!x&&y)
{
pushtag(y,v1);
return y;
}
if(l==r)
{
mx[x]=max(mx[x],v1)+max(mx[y],v2);
return x;
}
pushdown(x),pushdown(y);
rs[x]=get(rs[x],rs[y],mid+1,r,max(v1,mx[ls[x]]),max(v2,mx[ls[y]]));
ls[x]=get(ls[x],ls[y],l,mid,v1,v2);
mx[x]=max(mx[ls[x]],mx[rs[x]]);
return x;
}
void dfs(int x)
{
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
dfs(y),rt[x]=get(rt[x],rt[y],1,k,0,0);
}
if(d[x]) modify(1,k,d[x],w[x]+query(1,d[x],1,k,rt[x]),rt[x]);
}
int main()
{
read(n),read(m),read(k);
for(int i=2,p;i<=n;++i) read(p),add(p,i);
for(int i=1,x;i<=m;++i) read(x),read(d[x]),read(w[x]);
dfs(1),printf("%lld\n",mx[rt[1]]);
return 0;
}

动态 DP

给定一棵 个点的树,点带点权。有 次操作,每次操作给定 ,表示修改点 的权值为 。你需要在每次操作之后求出这棵树的最大权独立集的权值大小。

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
#include<bits/stdc++.h>
#define maxn 200010
#define inf 200000000
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m;
int f[maxn][2],a[maxn],ch[maxn][2],fa[maxn];
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=(edge){to,head[from]};
head[from]=edge_cnt;
}
struct matrix
{
int a[2][2];
matrix()
{
a[0][0]=a[0][1]=a[1][0]=a[1][1]=-inf;
}
int get()
{
return max(a[0][0],a[1][0]);
}
}val[maxn],mul[maxn];
matrix operator *(const matrix &x,const matrix &y)
{
matrix z;
for(int k=0;k<=1;++k)
for(int i=0;i<=1;++i)
for(int j=0;j<=1;++j)
z.a[i][j]=max(z.a[i][j],x.a[i][k]+y.a[k][j]);
return z;
}
void pushup(int x)
{
mul[x]=val[x];
if(ch[x][0]) mul[x]=mul[ch[x][0]]*mul[x];
if(ch[x][1]) mul[x]=mul[x]*mul[ch[x][1]];
}
bool check(int x)
{
return ch[fa[x]][1]==x;
}
bool notroot(int x)
{
return ch[fa[x]][0]==x||ch[fa[x]][1]==x;
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=check(x),w=ch[x][k^1];
if(notroot(y)) ch[z][check(y)]=x;
ch[x][k^1]=y,ch[y][k]=w;
if(w) fa[w]=y;
fa[x]=z,fa[y]=x;
pushup(y),pushup(x);
}
void splay(int x)
{
for(int y;notroot(x);rotate(x))
if(notroot(y=fa[x]))
rotate(check(x)^check(y)?x:y);
}
void access(int x)
{
for(int y=0;x;y=x,x=fa[x])
{
splay(x);
if(ch[x][1]) val[x].a[0][0]+=mul[ch[x][1]].get(),val[x].a[1][0]+=mul[ch[x][1]].a[0][0];
if(y) val[x].a[0][0]-=mul[y].get(),val[x].a[1][0]-=mul[y].a[0][0];
val[x].a[0][1]=val[x].a[0][0],ch[x][1]=y,pushup(x);
}
}
void modify(int x,int v)
{
access(x),splay(x);
val[x].a[1][0]-=a[x]-v;
pushup(x),a[x]=v;
}
void dfs(int x,int fath)
{
fa[x]=fath,f[x][1]=a[x];
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fath) continue;
dfs(y,x),f[x][0]+=max(f[y][0],f[y][1]),f[x][1]+=f[y][0];
}
val[x].a[0][0]=val[x].a[0][1]=f[x][0],val[x].a[1][0]=f[x][1],mul[x]=val[x];
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i) read(a[i]);
for(int i=1;i<n;++i)
{
int x,y;
read(x),read(y);
add(x,y),add(y,x);
}
dfs(1,0);
while(m--)
{
int x,y;
read(x),read(y),modify(x,y),splay(1);
printf("%d\n",mul[1].get());
}
return 0;
}

字符串

KMP

1
2
3
4
5
6
7
8
9
10
11
12
13
for(int i=2;i<=m;++i)
{
while(pos&&t[i]!=t[pos+1]) pos=nxt[pos];
if(t[i]==t[pos+1]) pos++;
nxt[i]=pos;
}
pos=0;
for(int i=1;i<=n;++i)
{
while(pos&&s[i]!=t[pos+1]) pos=nxt[pos];
if(s[i]==t[pos+1]) pos++;
if(pos==m) printf("%d\n",i-m+1);
}

Manacher

为以 为中心的长度为奇数的回文串个数,也就是最长回文串的回文半径。

1
2
3
4
5
6
7
8
9
10
11
12
void manacher()
{
s[0]='/',s[1]='#';
for(int i=1;i<=n;++i) s[i<<1]=t[i],s[i<<1|1]='#';
n=n<<1|1,s[n+1]='!';
for(int i=1,mr=1,mid=0;i<=n;++i)
{
f[i]=i<mr?min(f[mid*2-i],mr-i):1;
while(s[i+f[i]]==s[i-f[i]]) f[i]++;
if(i+f[i]>mr) mr=i+f[i],mid=i;
}
}

AC 自动机

给你一个文本串 个模式串 ,请你分别求出每个模式串 中出现的次数。

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
#include<bits/stdc++.h>
#define maxn 2000010
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,tot,root;
int ch[maxn][28],fail[maxn],siz[maxn],bel[maxn];
char s[maxn];
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]={to,head[from]},head[from]=edge_cnt;
}
int insert(char *s)
{
int len=strlen(s+1),p=root;
for(int i=1;i<=len;++i)
{
int c=s[i]-'a';
if(!ch[p][c]) ch[p][c]=++tot;
p=ch[p][c];
}
return p;
}
void build()
{
queue<int> q;
for(int i=0;i<26;++i)
if(ch[root][i])
q.push(ch[root][i]);
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=0;i<26;++i)
{
int y=ch[x][i];
if(y) fail[y]=ch[fail[x]][i],q.push(y);
else ch[x][i]=ch[fail[x]][i];
}
}
for(int i=1;i<=tot;++i) add(fail[i],i);
}
void update()
{
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=1,p=root;i<=len;++i) p=ch[p][s[i]-'a'],siz[p]++;
}
void dfs(int x)
{
for(int i=head[x];i;i=e[i].nxt) dfs(e[i].to),siz[x]+=siz[e[i].to];
}
int main()
{
read(n);
for(int i=1;i<=n;++i) scanf("%s",s+1),bel[i]=insert(s);
build(),update(),dfs(root);
for(int i=1;i<=n;++i) printf("%d\n",siz[bel[i]]);
return 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
void rsort()
{
for(int i=0;i<=m;++i) b[i]=0;
for(int i=1;i<=n;++i) b[rk[i]]++;
for(int i=1;i<=m;++i) b[i]+=b[i-1];
for(int i=n;i;--i) sa[b[rk[tp[i]]]--]=tp[i];
}
void SA()
{
for(int i=1;i<=n;++i) rk[i]=s[i],tp[i]=i;
rsort();
for(int k=1;k<=n;k<<=1)
{
int num=0;
for(int i=n-k+1;i<=n;++i) tp[++num]=i;
for(int i=1;i<=n;++i)
if(sa[i]>k)
tp[++num]=sa[i]-k;
rsort();
memcpy(tp,rk,sizeof(rk));
rk[sa[1]]=num=1;
for(int i=2;i<=n;++i)
rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+k]==tp[sa[i-1]+k])?num:++num;
if(num==n) break;
m=num;
}
}
void height()
{
int k=0;
for(int i=1;i<=n;++i) rk[sa[i]]=i;
for(int i=1;i<=n;++i)
{
if(rk[i]==1) continue;
if(k) k--;
int j=sa[rk[i]-1];
while(s[i+k]==s[j+k]) k++;
ht[rk[i]]=k;
}
}
void ST()
{
lg[0]=-1;
for(int i=1;i<=n;++i) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;++i) f[i][0]=ht[i];
for(int j=1;j<=20;++j)
for(int i=1;i+(1<<j)-1<=n;++i)
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
int lcp(int l,int r)
{
l=rk[l],r=rk[r];
if(l>r) swap(l,r);
l++;
int len=lg[r-l+1];
return min(f[l][len],f[r-(1<<len)+1][len]);
}

后缀自动机

后缀自动机可以理解为是将字符串所有后缀所建出的 进行压缩后得出的 。对于一个子串 ,它结束位置的集合称为 ,如 为原串的两个子串,设 ,则 的后缀当且仅当 不是 的后缀当且仅当 。由 的包含关系可以得出一个树形结构,称为 树。

后缀自动机的节点就是 树的节点,每个节点表示一个 的节点数不超过 ,边数不超过 ,数组大小应开成两倍。为一个 所对应的子串中最长子串的长度。为转移函数。为后缀连接。设 为一个 所对应的子串中最短子串的长度,得 。后缀自动机是一张有向无环图,其中顶点是状态,而边代表了状态之间的转移。每一个状态包含了它包含的最长子串的一些连续长度的后缀,不是所有后缀,再短的其他后缀在 连接的状态,也就是该串的所有后缀在 树的链上。一个字符串的 树,是其反串的后缀树。从初始状态经由任意路径走到某一终止状态,得到的字符串为原串的某一后缀。从初始状态经由任意路径走到某一状态,得到的字符串为原串的某一子串。所有终止状态包含了原串的所有后缀,整串状态是终止状态,整串状态在 树上的祖先也都是终止状态。一个状态的 集合大小等于该状态转移到终止状态的方案数。

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
#include<bits/stdc++.h>
#define maxn 2000010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,tot=1,las=1;
ll ans;
int len[maxn],fa[maxn],ch[maxn][28],siz[maxn];
char s[maxn];
struct edge
{
int to,nxt;
edge(int a=0,int b=0)
{
to=a,nxt=b;
}
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=edge(to,head[from]),head[from]=edge_cnt;
}
void insert(int c)
{
int p=las,np=las=++tot;
len[np]=len[p]+1,siz[np]=1;
while(p&&!ch[p][c]) ch[p][c]=np,p=fa[p];
if(!p) fa[np]=1;
else
{
int q=ch[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tot;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
len[nq]=len[p]+1,fa[nq]=fa[q],fa[q]=fa[np]=nq;
while(ch[p][c]==q) ch[p][c]=nq,p=fa[p];
}
}
}
void dfs(int x)
{
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
dfs(y),siz[x]+=siz[y];
}
if(siz[x]>1) ans=max(ans,(ll)len[x]*siz[x]);
}
int main()
{
scanf("%s",s+1),n=strlen(s+1);
for(int i=1;i<=n;++i) insert(s[i]-'a');
for(int i=2;i<=tot;++i) add(fa[i],i);
dfs(1),printf("%lld",ans);
return 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
#include<bits/stdc++.h>
#define maxn 2000010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,las,tot=1,root=1;
ll ans;
int fa[maxn],len[maxn],ch[maxn][26];
char s[maxn];
int insert(int c)
{
if(ch[las][c]&&len[las]+1==len[ch[las][c]]) return ch[las][c];
int p=las,np=++tot;
len[np]=len[p]+1;
while(p&&!ch[p][c]) ch[p][c]=np,p=fa[p];
if(!p) fa[np]=root;
else
{
int q=ch[p][c];
if(len[q]==len[p]+1) fa[np]=q;
else
{
int nq=++tot;
bool flag=las==p;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
len[nq]=len[p]+1,fa[nq]=fa[q],fa[q]=fa[np]=nq;
while(ch[p][c]==q) ch[p][c]=nq,p=fa[p];
return flag?nq:np;
}
}
return np;
}
int main()
{
read(n);
for(int i=1;i<=n;++i)
{
int lenth;
scanf("%s",s+1),lenth=strlen(s+1),las=1;
for(int j=1;j<=lenth;++j) las=insert(s[j]-'a');
}
for(int i=2;i<=tot;++i) ans+=len[i]-len[fa[i]];
printf("%lld",ans);
return 0;
}

回文自动机

回文树可以用来处理一个字符串中所有的回文子串。一个串的本质不同回文子串个数最多为 个。一个字符串的回文树由两棵树组成,一个维护所有长度为奇数的回文子串,一个维护所有长度为偶数的回文子串。树上除根节点外的每个节点都表示串中的一个回文子串。

节点对应的回文子串长度。

指向该节点所对应的回文子串的最长回文后缀所对应的节点。

转移到该树的另一个节点,转移为向当前回文子串两端加上一个字符。

以一个位置结尾对应节点的回文子串个数

为方便处理,偶树的根的 设为 设为奇树的根,奇树的根的 设为 设为其本身。构造时用增量法即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void init()
{
len[1]=-1,fail[0]=fail[1]=tot=1;
}
void insert(int i)
{
int p=las,c=s[i]-'a';
while(s[i-1-len[p]]!=s[i]) p=fail[p];
if(ch[p][c])
{
las=ch[p][c];
return;
}
int x=fail[p],y=++tot;
while(s[i-1-len[x]]!=s[i]) x=fail[x];
fail[y]=ch[x][c],len[y]=len[p]+2,ch[p][c]=las=y,cnt[y]=cnt[fail[y]]+1;
}

树:

1
2
3
for(int i=0;i<=tot;++i)
if(i!=1)
add(fail[i],i);

一个字符串的本质不同回文子串个数即为其回文树除了两个根的节点个数。字符串中一个位置的回文后缀个数即为该位置对应的节点的 链长度。在维护每个本质不同回文子串的出现次数时,还需在 树上用儿子来更新父亲。

1
for(int i=tot;i;--i) cnt[fail[i]]+=cnt[i];

有时还需用到 ,指向长度小于等于其回文子串长度一半的最长回文后缀的节点,建树时维护即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void insert(int i)
{
int p=las,c=s[i]-'a';
while(s[i-1-len[p]]!=s[i]) p=fail[p];
if(ch[p][c])
{
las=ch[p][c];
return;
}
int x=fail[p],y=++tot;
while(s[i-1-len[x]]!=s[i]) x=fail[x];
fail[y]=ch[x][c],len[y]=len[p]+2,ch[p][c]=las=y;
if(len[y]<=2) trans[y]=fail[y];
else
{
int q=trans[p];
while(s[i-1-len[q]]!=s[i]||(len[q]+2)*2>len[y]) q=fail[q];
trans[y]=ch[q][c];
}
}

扩展 KMP

扩展 可以 得出一个字符串的 函数, 为以 开头的后缀和整个串的

1
2
3
4
5
6
7
z[1]=n;
for(int i=2;i<=n;++i)
{
if(i<=r) z[i]=min(z[i-l+1],r-i+1);
while(i+z[i]<=n&&s[i+z[i]]==s[z[i]+1]) z[i]++;
if(i+z[i]-1>r) l=i,r=i+z[i]-1;
}

计算几何

二维凸包

求凸包周长。

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
#include<bits/stdc++.h>
#define maxn 100010
#define eps 1e-8
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,top;
double ans;
int sgn(double x)
{
if(fabs(x)<eps) return 0;
return x>0?1:-1;
}
struct Vec
{
double x,y;
Vec(const double a=0,const double b=0)
{
x=a,y=b;
}
}p[maxn],st[maxn];
Vec operator + (const Vec &a,const Vec &b)
{
return Vec(a.x+b.x,a.y+b.y);
}
Vec operator - (const Vec &a,const Vec &b)
{
return Vec(a.x-b.x,a.y-b.y);
}
Vec operator * (const Vec &a,const double &b)
{
return Vec(a.x*b,a.y*b);
}
Vec operator / (const Vec &a,const double &b)
{
return Vec(a.x/b,a.y/b);
}
double operator * (const Vec &a,const Vec &b)
{
return a.x*b.x+a.y*b.y;
}
double operator & (const Vec &a,const Vec &b)
{
return a.x*b.y-a.y*b.x;
}
double dis(const Vec &a,const Vec &b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool cmp(const Vec &a,const Vec &b)
{
double A=atan2(a.y-p[1].y,a.x-p[1].x),B=atan2(b.y-p[1].y,b.x-p[1].x);
return sgn(A-B)?A<B:a.x<b.x;
}
void graham()
{
int k=1;
for(int i=2;i<=n;++i)
if(p[i].x<p[k].x||(p[i].x==p[k].x&&p[i].y<p[k].y))
k=i;
swap(p[1],p[k]),sort(p+2,p+n+1,cmp),st[top=1]=p[1];
for(int i=2;i<=n;++i)
{
while(top>1&&sgn((p[i]-st[top-1])&(st[top]-st[top-1]))>=0) top--;
st[++top]=p[i];
}
}
int main()
{
read(n);
for(int i=1;i<=n;++i) scanf("%lf%lf",&p[i].x,&p[i].y);
graham();
for(int i=1;i<top;++i) ans+=dis(st[i],st[i+1]);
printf("%.2lf",ans+dis(st[top],st[1]));
return 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
#include<bits/stdc++.h>
#define maxn 50010
#define eps 1e-8
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,top;
int sgn(double x)
{
if(fabs(x)<eps) return 0;
return x>0?1:-1;
}
struct Vec
{
double x,y;
Vec(const double a=0,const double b=0)
{
x=a,y=b;
}
}p[maxn],st[maxn];
Vec operator + (const Vec &a,const Vec &b)
{
return Vec(a.x+b.x,a.y+b.y);
}
Vec operator - (const Vec &a,const Vec &b)
{
return Vec(a.x-b.x,a.y-b.y);
}
Vec operator * (const Vec &a,const double &b)
{
return Vec(a.x*b,a.y*b);
}
Vec operator / (const Vec &a,const double &b)
{
return Vec(a.x/b,a.y/b);
}
double operator * (const Vec &a,const Vec &b)
{
return a.x*b.y-a.y*b.x;
}
double operator % (const Vec &a,const Vec &b)
{
return a.x*b.x+a.y*b.y;
}
int dis(const Vec &a,const Vec &b)
{
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
bool cmp(const Vec &a,const Vec &b)
{
double A=atan2(a.y-p[1].y,a.x-p[1].x),B=atan2(b.y-p[1].y,b.x-p[1].x);
return sgn(A-B)?A<B:a.x<b.x;
}
void graham()
{
int k=1;
for(int i=2;i<=n;++i)
if(p[i].x<p[k].x||(p[i].x==p[k].x&&p[i].y<p[k].y))
k=i;
swap(p[1],p[k]),sort(p+2,p+n+1,cmp),st[top=1]=p[1];
for(int i=2;i<=n;++i)
{
while(top>1&&sgn((p[i]-st[top-1])*(st[top]-st[top-1]))>=0) top--;
st[++top]=p[i];
}
}
int calc()
{
if(top==2) return dis(st[1],st[2]);
int j=3,v=0;;
st[top+1]=st[1];
for(int i=1;i<=top;++i)
{
while((st[i+1]-st[i])*(st[j]-st[i])<=(st[i+1]-st[i])*(st[j+1]-st[i])) j=j%top+1;
v=max(v,max(dis(st[j],st[i]),dis(st[j],st[i+1])));
}
return v;
}
int main()
{
read(n);
for(int i=1;i<=n;++i) scanf("%lf%lf",&p[i].x,&p[i].y);
graham(),printf("%d",calc());
return 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
111
112
113
114
115
116
#include<bits/stdc++.h>
#define maxn 510
#define eps 1e-8
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,cnt,tot,h,t;
int q[maxn];
int sgn(double x)
{
if(fabs(x)<eps) return 0;
return x>0?1:-1;
}
struct Vec
{
double x,y;
Vec(const double a=0,const double b=0)
{
x=a,y=b;
}
void in()
{
scanf("%lf%lf",&x,&y);
}
double len()
{
return sqrt(x*x+y*y);
}
}p[maxn];
Vec operator + (const Vec &a,const Vec &b)
{
return Vec(a.x+b.x,a.y+b.y);
}
Vec operator - (const Vec &a,const Vec &b)
{
return Vec(a.x-b.x,a.y-b.y);
}
Vec operator * (const Vec &a,const double &b)
{
return Vec(a.x*b,a.y*b);
}
Vec operator / (const Vec &a,const double &b)
{
return Vec(a.x/b,a.y/b);
}
double operator * (const Vec &a,const Vec &b)
{
return a.x*b.y-a.y*b.x;
}
double operator % (const Vec &a,const Vec &b)
{
return a.x*b.x+a.y*b.y;
}
struct Line
{
Vec a,b,c;
double k;
void init()
{
c=b-a,k=atan2(c.y,c.x);
}
}l[maxn];
bool cmp(const Line &u,const Line &v)
{
return sgn(u.k-v.k)?u.k<v.k:sgn(u.c*(v.b-u.a))==-1;
}
Vec cross(Line u,Line v)
{
return v.a+v.c*((v.a-u.a)*u.c)/(u.c*v.c);
}
bool check(Line u,Line v,Line w)
{
return sgn(w.c*(cross(u,v)-w.a))==-1;
}
double get()
{
double v=0;
sort(l+1,l+cnt+1,cmp);
for(int i=1;i<=cnt;++i)
if(sgn(l[i].k-l[i-1].k))
l[++tot]=l[i];
q[h=t=1]=1,q[++t]=2;
for(int i=3;i<=tot;++i)
{
while(h<t&&check(l[q[t-1]],l[q[t]],l[i])) t--;
while(h<t&&check(l[q[h]],l[q[h+1]],l[i])) h++;
q[++t]=i;
}
while(h<t&&check(l[q[t-1]],l[q[t]],l[q[h]])) t--;
if(t-h+1<=2) return 0;
q[t+1]=q[h],cnt=0;
for(int i=h;i<=t;++i) p[++cnt]=cross(l[q[i]],l[q[i+1]]);
p[cnt+1]=p[1];
for(int i=1;i<=cnt;++i) v+=p[i]*p[i+1];
return v/2;
}
int main()
{
read(n);
for(int i=1;i<=n;++i)
{
int k;
read(k);
for(int j=1;j<=k;++j)
l[++cnt].b.in(),l[cnt].a=l[cnt-1].b;
l[cnt-k+1].a=l[cnt].b;
}
for(int i=1;i<=cnt;++i) l[i].init();
printf("%.3lf",get());
return 0;
}

数学

FFT

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<bits/stdc++.h>
#define maxn 8000010
#define Pi acos(-1.0)
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m;
int rev[maxn];
struct Complex
{
double x,y;
Complex(double a=0,double b=0)
{
x=a,y=b;
}
}f[maxn],g[maxn];
Complex operator +(const Complex &a,const Complex &b)
{
return Complex(a.x+b.x,a.y+b.y);
}
Complex operator -(const Complex &a,const Complex &b)
{
return Complex(a.x-b.x,a.y-b.y);
}
Complex operator *(const Complex &a,const Complex &b)
{
return Complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}
int calc(int n)
{
int lim=1;
while(lim<=n) lim<<=1;
for(int i=0;i<lim;++i)
rev[i]=(rev[i>>1]>>1)|((i&1)?lim>>1:0);
return lim;
}
void FFT(Complex *a,int lim,int type)
{
for(int i=0;i<lim;++i)
if(i<rev[i])
swap(a[i],a[rev[i]]);
for(int len=1;len<lim;len<<=1)
{
Complex T(cos(Pi/len),type*sin(Pi/len));
for(int i=0;i<lim;i+=len<<1)
{
Complex t(1,0);
for(int j=i;j<i+len;++j,t=t*T)
{
Complex x=a[j],y=t*a[j+len];
a[j]=x+y,a[j+len]=x-y;
}
}
}
if(type==1) return;
for(int i=0;i<lim;++i) a[i].x=a[i].x/lim+0.5;
}
void mul(Complex *f,Complex *g)
{
int lim=calc(n+m);
FFT(f,lim,1),FFT(g,lim,1);
for(int i=0;i<lim;++i) f[i]=f[i]*g[i];
FFT(f,lim,-1);
}
int main()
{
read(n),read(m);
for(int i=0,x;i<=n;++i) read(x),f[i].x=x;
for(int i=0,x;i<=m;++i) read(x),g[i].x=x;
mul(f,g);
for(int i=0;i<=n+m;++i) printf("%d ",(int)f[i].x);
return 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
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
#include<bits/stdc++.h>
#define maxn 400010
#define P 998244353
#define G 3
#define Gi (P+1)/G
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll n,k,T,lenth;
ll rev[maxn],f[maxn],g[maxn];
char s[maxn];
ll qp(ll x,ll y)
{
ll v=1;
while(y)
{
if(y&1) v=v*x%P;
x=x*x%P,y>>=1;
}
return v;
}
int calc(int n)
{
int lim=1;
while(lim<=n) lim<<=1;
for(int i=0;i<lim;++i)
rev[i]=(rev[i>>1]>>1)|((i&1)?lim>>1:0);
return lim;
}
void NTT(ll *a,int lim,int type)
{
for(int i=0;i<lim;++i)
if(i<rev[i])
swap(a[i],a[rev[i]]);
for(int len=1;len<lim;len<<=1)
{
ll wn=qp(type==1?G:Gi,(P-1)/(len<<1));
for(int i=0;i<lim;i+=len<<1)
{
ll w=1;
for(int j=i;j<i+len;++j,w=w*wn%P)
{
ll x=a[j],y=w*a[j+len]%P;
a[j]=(x+y)%P,a[j+len]=(x-y+P)%P;
}
}
}
if(type==1) return;
ll inv=qp(lim,P-2);
for(int i=0;i<lim;++i) a[i]=a[i]*inv%P;
}
void Inv(int deg,ll *a,ll *b)
{
static ll t[maxn];
if(deg==1)
{
b[0]=qp(a[0],P-2);
return;
}
Inv((deg+1)>>1,a,b);
int lim=calc(deg<<1);
for(int i=0;i<deg;++i) t[i]=a[i];
for(int i=deg;i<lim;++i) t[i]=b[i]=0;
NTT(t,lim,1),NTT(b,lim,1);
for(int i=0;i<lim;++i)
b[i]=b[i]*(2-t[i]*b[i]%P+P)%P;
NTT(b,lim,-1);
for(int i=deg;i<lim;++i) b[i]=0;
}
void Ln(int deg,ll *a,ll *b)
{
static ll inva[maxn],dera[maxn];
Inv(deg,a,inva);
for(int i=0;i<deg-1;++i) dera[i]=a[i+1]*(i+1)%P;
dera[deg-1]=0;
int lim=calc(deg<<1);
for(int i=deg;i<lim;++i) dera[i]=inva[i]=0;
NTT(dera,lim,1),NTT(inva,lim,1);
for(int i=0;i<lim;++i) b[i]=dera[i]*inva[i]%P;
NTT(b,lim,-1);
for(int i=deg-1;i>=1;--i) b[i]=b[i-1]*qp(i,P-2)%P;
b[0]=0;
for(int i=deg;i<lim;++i) b[i]=0;
}
void Exp(int deg,ll *a,ll *b)
{
static ll t[maxn],lnb[maxn];
if(deg==1)
{
b[0]=1;
return;
}
Exp((deg+1)>>1,a,b),Ln(deg,b,lnb);
int lim=calc(deg<<1);
for(int i=0;i<deg;++i) t[i]=a[i];
for(int i=deg;i<lim;++i) t[i]=b[i]=0;
NTT(t,lim,1),NTT(b,lim,1),NTT(lnb,lim,1);
for(int i=0;i<lim;++i)
b[i]=b[i]*(1-lnb[i]+t[i]+P)%P;
NTT(b,lim,-1);
for(int i=deg;i<lim;++i) b[i]=0;
}
void Pow(int deg,ll *a,ll *b,ll k)
{
static ll lna[maxn];
Ln(deg,a,lna);
for(int i=0;i<deg;++i) lna[i]=lna[i]*k%P;
Exp(deg,lna,b);
}
void Sqrt(int deg,ll *a,ll *b)
{
static ll t[maxn],invb[maxn];
if(deg==1)
{
b[0]=1;
return;
}
Sqrt((deg+1)>>1,a,b);
int lim=calc(deg<<1);
for(int i=0;i<deg;++i) t[i]=2*b[i]%P,invb[i]=0;
for(int i=deg;i<lim;++i) t[i]=invb[i]=0;
Inv(deg,t,invb);
for(int i=0;i<deg;++i) t[i]=a[i];
for(int i=deg;i<lim;++i) t[i]=b[i]=0;
NTT(t,lim,1),NTT(b,lim,1),NTT(invb,lim,1);
for(int i=0;i<lim;++i)
b[i]=(b[i]*b[i]%P+t[i])%P*invb[i]%P;
NTT(b,lim,-1);
for(int i=deg;i<lim;++i) b[i]=0;
}
int main()
{
read(n);
for(int i=0;i<n;++i) read(f[i]);
Sqrt(n,f,g);
for(int i=0;i<n;++i) printf("%lld ",g[i]);
return 0;
}

分治 FFT

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
#include<bits/stdc++.h>
#define maxn 400010
#define p 998244353
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n;
int rev[maxn];
ll f[maxn],g[maxn];
ll qp(ll x,ll y)
{
ll v=1;
while(y)
{
if(y&1) v=v*x%p;
x=x*x%p,y>>=1;
}
return v;
}
int calc(int n)
{
int lim=1;
while(lim<=n) lim<<=1;
for(int i=0;i<lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?lim>>1:0);
return lim;
}
void NTT(ll *a,int lim,int type)
{
for(int i=0;i<lim;++i)
if(i<rev[i])
swap(a[i],a[rev[i]]);
for(int len=1;len<lim;len<<=1)
{
ll wn=qp(3,(p-1)/(len<<1));
for(int i=0;i<lim;i+=len<<1)
{
ll w=1;
for(int j=i;j<i+len;++j,w=w*wn%p)
{
ll x=a[j],y=w*a[j+len]%p;
a[j]=(x+y)%p,a[j+len]=(x-y+p)%p;
}
}
}
if(type==1) return;
ll inv=qp(lim,p-2);
for(int i=0;i<lim;++i) a[i]=a[i]*inv%p;
reverse(a+1,a+lim);
}
void Inv(int deg,ll *f,ll *g)
{
if(deg==1)
{
g[0]=qp(f[0],p-2);
return;
}
static ll t[maxn];
Inv((deg+1)>>1,f,g);
int lim=calc(deg<<1);
for(int i=0;i<deg;++i) t[i]=f[i];
for(int i=deg;i<lim;++i) t[i]=0;
NTT(t,lim,1),NTT(g,lim,1);
for(int i=0;i<lim;++i) g[i]=(2*g[i]-t[i]*g[i]%p*g[i]%p+p)%p;
NTT(g,lim,-1);
for(int i=deg;i<lim;++i) g[i]=0;
}
int main()
{
read(n),f[0]=1;
for(int i=1;i<n;++i) read(f[i]),f[i]=p-f[i];
Inv(n,f,g);
for(int i=0;i<n;++i) printf("%lld ",g[i]);
return 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
#include<bits/stdc++.h>
#define maxn 800010
#define all 262144
#define M 32767
#define Pi acos(-1.0)
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,p;
int rev[maxn],f[maxn],g[maxn],h[maxn];
struct Complex
{
long double x,y;
Complex(long double a=0,long double b=0)
{
x=a,y=b;
}
}w[maxn];
Complex operator +(const Complex &a,const Complex &b)
{
return Complex(a.x+b.x,a.y+b.y);
}
Complex operator -(const Complex &a,const Complex &b)
{
return Complex(a.x-b.x,a.y-b.y);
}
Complex operator *(const Complex &a,const Complex &b)
{
return Complex(a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x);
}
Complex operator ~(const Complex &a)
{
return Complex(a.x,-a.y);
}
int calc(int n)
{
int lim=1;
while(lim<=n) lim<<=1;
for(int i=0;i<lim;++i)
rev[i]=(rev[i>>1]>>1)|((i&1)?lim>>1:0);
return lim;
}
void init(int n)
{
int lim=calc(n);
for(int len=1;len<lim;len<<=1)
for(int i=0;i<len;++i)
w[i+len]=Complex(cos(Pi*i/len),sin(Pi*i/len));
}
void FFT(Complex *a,int lim,int type)
{
for(int i=0;i<lim;++i)
if(i<rev[i])
swap(a[i],a[rev[i]]);
for(int len=1;len<lim;len<<=1)
{
for(int i=0;i<lim;i+=len<<1)
{
for(int j=0;j<len;++j)
{
Complex x=a[i+j],y=w[j+len]*a[i+j+len];
a[i+j]=x+y,a[i+j+len]=x-y;
}
}
}
if(type==1) return;
reverse(a+1,a+lim);
for(int i=0;i<lim;++i)
a[i].x=a[i].x/lim+0.5,a[i].y=a[i].y/lim+0.5;
}
void MTT(int *f,int *g,int *h)
{
static Complex a[maxn],b[maxn],a0[maxn],b0[maxn];
int lim=calc(n+m);
for(int i=0;i<lim;++i) a[i]=Complex(f[i]>>15,f[i]&M);
for(int i=0;i<lim;++i) b[i]=Complex(g[i]>>15,g[i]&M);
FFT(a,lim,1),FFT(b,lim,1),a0[0]=~a[0],b0[0]=~b[0];
for(int i=1;i<lim;++i) a0[i]=~a[lim-i],b0[i]=~b[lim-i];
for(int i=0;i<lim;++i)
{
Complex v1=a[i],v2=a0[i],v3=b[i],v4=b0[i];
a[i]=(v1+v2)*Complex(0.5,0),a0[i]=(v1-v2)*Complex(0,-0.5);
b[i]=(v3+v4)*Complex(0.5,0),b0[i]=(v3-v4)*Complex(0,-0.5);
}
for(int i=0;i<lim;++i)
a[i]=a[i]*b[i]+(a[i]*b0[i]+a0[i]*b[i])*Complex(0,1),b[i]=a0[i]*b0[i];
FFT(a,lim,-1),FFT(b,lim,-1);
for(int i=0;i<lim;++i)
h[i]=((((ll)a[i].x%p<<30)%p+((ll)a[i].y%p<<15)%p)%p+(ll)b[i].x)%p;
}
int main()
{
read(n),read(m),read(p),n++,m++,init(n+m);
for(int i=0;i<n;++i) read(f[i]),f[i]%=p;
for(int i=0;i<m;++i) read(g[i]),g[i]%=p;
MTT(f,g,h);
for(int i=0;i<n+m-1;++i) printf("%d ",h[i]);
return 0;
}

FWT

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
#include<bits/stdc++.h>
#define maxn 131082
#define p 998244353
#define inv 499122177
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,all;
ll va[maxn],vb[maxn],a[maxn],b[maxn];
void FWT_or(ll *a,int type)
{
for(int len=1;len<all;len<<=1)
for(int i=0;i<all;i+=len<<1)
for(int j=i;j<i+len;++j)
a[j+len]=(a[j+len]+a[j]*type+p)%p;
}
void FWT_and(ll *a,int type)
{
for(int len=1;len<all;len<<=1)
for(int i=0;i<all;i+=len<<1)
for(int j=i;j<i+len;++j)
a[j]=(a[j]+a[j+len]*type+p)%p;
}
void FWT_xor(ll *a,int type)
{
for(int len=1;len<all;len<<=1)
{
for(int i=0;i<all;i+=len<<1)
{
for(int j=i;j<i+len;++j)
{
ll x=a[j],y=a[j+len];
a[j]=(x+y)%p,a[j+len]=(x-y+p)%p;
if(type==-1) a[j]=a[j]*inv%p,a[j+len]=a[j+len]*inv%p;
}
}
}
}
void copy()
{
memcpy(a,va,sizeof(va)),memcpy(b,vb,sizeof(vb));
}
void Or()
{
copy(),FWT_or(a,1),FWT_or(b,1);
for(int i=0;i<all;++i) a[i]=a[i]*b[i]%p;
FWT_or(a,-1);
for(int i=0;i<all;++i) printf("%lld ",a[i]);
putchar('\n');
}
void And()
{
copy(),FWT_and(a,1),FWT_and(b,1);
for(int i=0;i<all;++i) a[i]=a[i]*b[i]%p;
FWT_and(a,-1);
for(int i=0;i<all;++i) printf("%lld ",a[i]);
putchar('\n');
}
void Xor()
{
copy(),FWT_xor(a,1),FWT_xor(b,1);
for(int i=0;i<all;++i) a[i]=a[i]*b[i]%p;
FWT_xor(a,-1);
for(int i=0;i<all;++i) printf("%lld ",a[i]);
putchar('\n');
}
int main()
{
read(n),all=1<<n;
for(int i=0;i<all;++i) read(va[i]);
for(int i=0;i<all;++i) read(vb[i]);
Or(),And(),Xor();
return 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
#include<bits/stdc++.h>
#define maxn 21
#define maxs 1200010
#define p 1000000009
#define lowbit(x) (x&(-x))
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,all;
int cnt[maxs],h[maxn][maxs],f[maxn][maxs],g[maxn][maxs];
void FWT(int *a,int type)
{
for(int len=1;len<all;len<<=1)
for(int i=0;i<all;i+=len<<1)
for(int j=i;j<i+len;++j)
a[j+len]=((a[j+len]+a[j]*type)%p+p)%p;
}
int main()
{
read(n),all=1<<n;
for(int i=1;i<all;++i) cnt[i]=cnt[i-lowbit(i)]+1;
for(int i=0;i<all;++i) read(f[cnt[i]][i]);
for(int i=0;i<all;++i) read(g[cnt[i]][i]);
for(int i=0;i<=n;++i) FWT(f[i],1),FWT(g[i],1);
for(int i=0;i<=n;++i)
for(int j=0;j<=i;++j)
for(int s=0;s<all;++s)
h[i][s]=(h[i][s]+(ll)f[i-j][s]*g[j][s]%p)%p;
for(int i=0;i<=n;++i) FWT(h[i],-1);
for(int i=0;i<all;++i) printf("%lld ",h[cnt[i]][i]);
return 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
#include<bits/stdc++.h>
#define maxn 2010
#define mod 998244353
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll n,k,ans;
ll x[maxn],y[maxn];
ll qp(ll x,ll y)
{
ll ans=1;
while(y)
{
if(y&1) ans=(ans*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return ans;
}
int main()
{
read(n),read(k);
for(int i=1;i<=n;++i) read(x[i]),read(y[i]);
for(int i=1;i<=n;++i)
{
ll p=1;
for(int j=1;j<=n;++j)
if(i!=j)
p=p*(((x[i]-x[j])%mod+mod)%mod)%mod;
p=qp(p,mod-2);
for(int j=1;j<=n;++j)
if(i!=j)
p=p*(((k-x[j])%mod+mod)%mod)%mod;
p=p*y[i]%mod;
ans=(ans+p)%mod;
}
printf("%lld\n",ans);
return 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
#include<bits/stdc++.h>
#define maxn 310
#define p 1000000007
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll n,m,type;
ll a[maxn][maxn];
void add1(int x,int y,int v)
{
a[x][x]=(a[x][x]+v)%p;
a[y][y]=(a[y][y]+v)%p;
a[x][y]=(a[x][y]-v)%p;
a[y][x]=(a[y][x]-v)%p;
}
void add2(int x,int y,int v)
{
a[y][y]=(a[y][y]+v)%p;
a[x][y]=(a[x][y]-v)%p;
}
ll qp(ll x,ll y)
{
ll v=1;
while(y)
{
if(y&1) v=v*x%p;
x=x*x%p,y>>=1;
}
return v;
}
ll det()
{
ll ans=1;
for(int i=2;i<=n;++i)
{
int ma=i;
for(int j=i+1;j<=n;++j)
if(a[j][i]>a[ma][i])
ma=j;
if(ma!=i) swap(a[i],a[ma]),ans*=-1;
if(!a[i][i]) return 0;
ll inv=qp(a[i][i],p-2);
for(int j=i+1;j<=n;++j)
{
ll d=a[j][i]*inv%p;
for(int k=i;k<=n;++k) a[j][k]=((a[j][k]-a[i][k]*d%p)%p+p)%p;
}
ans=ans*a[i][i]%p;
}
return (ans%p+p)%p;
}
int main()
{
read(n),read(m),read(type);
for(int i=1;i<=m;++i)
{
int a,b,v;
read(a),read(b),read(v);
if(!type) add1(a,b,v);
else add2(a,b,v);
}
printf("%lld",det());
return 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
#include<bits/stdc++.h>
#define maxn 110
#define eps 1e-8
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag) x=-x;
}
int n;
double a[maxn][maxn];
bool gauss()
{
for(int i=1;i<=n;++i)
{
int ma=i;
for(int j=i+1;j<=n;++j)
if(fabs(a[j][i])>fabs(a[ma][i]))
ma=j;
swap(a[ma],a[i]);
double d=a[i][i];
if(fabs(d)<eps) return false;
for(int j=i;j<=n+1;++j) a[i][j]/=d;
for(int j=i+1;j<=n;++j)
{
d=a[j][i];
for(int k=i;k<=n+1;++k)
a[j][k]-=a[i][k]*d;
}
}
for(int i=n;i;--i)
for(int j=i-1;j;--j)
a[j][n+1]-=a[j][i]*a[i][n+1];
return true;
}
int main()
{
read(n);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n+1;++j)
{
int val;
read(val);
a[i][j]=val;
}
}
if(!gauss())
{
puts("No Solution");
return 0;
}
for(int i=1;i<=n;++i)
printf("%.2lf\n",a[i][n+1]);
return 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
#include<bits/stdc++.h>
#define maxn 810
#define mod 1000000007
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll n,m;
ll a[maxn][maxn];
ll inv(ll x)
{
ll y=mod-2,ans=1;
while(y)
{
if(y&1) ans=ans*x%mod;
x=x*x%mod;
y>>=1;
}
return ans;
}
bool gauss()
{
for(int i=1;i<=n;++i)
{
for(int j=i;j<=n;++j)
{
if(a[j][i])
{
swap(a[j],a[i]);
break;
}
}
if(!a[i][i]) return false;
ll d=inv(a[i][i]);
for(int j=i;j<=m;++j) a[i][j]=a[i][j]*d%mod;
for(int j=1;j<=n;++j)
{
if(i==j) continue;
d=a[j][i]%mod;
for(int k=i;k<=m;++k)
a[j][k]=((a[j][k]-a[i][k]*d%mod)%mod+mod)%mod;
}
}
return true;
}
int main()
{
read(n),m=2*n;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j) read(a[i][j]);
a[i][i+n]=1;
}
if(gauss())
{
for(int i=1;i<=n;++i)
{
for(int j=n+1;j<=m;++j)
printf("%lld ",a[i][j]);
puts("");
}
}
else puts("No Solution");
return 0;
}

欧拉筛

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void init()
{
mu[1]=1;
for(int i=2;i<=all;++i)
{
if(!tag[i]) pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot;++j)
{
int k=i*pri[j];
if(k>all) break;
tag[k]=true;
if(i%pri[j]) mu[k]=mu[i]*mu[pri[j]];
else
{
mu[k]=0;
break;
}
}
}
}

Pollard's rho

求最大质因子。

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
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag) x=-x;
}
ll T,n,fac_max;
ll pri[14]={2,3,5,7,11,13,17,19,23,29};
ll mul(ll x,ll y,ll mod)
{
ll c=(long double)x*y/mod+0.5;
c=x*y-c*mod;
return c<0?c+mod:c;
}
ll qp(ll x,ll y,ll mod)
{
ll ans=1;
while(y)
{
if(y&1) ans=mul(ans,x,mod);
x=mul(x,x,mod);
y>>=1;
}
return ans%mod;
}
bool check(ll x,ll p,ll mod)
{
ll t=qp(x,p,mod);
if(t==mod-1) return true;
if(t==1) return p&1?true:check(x,p/2,mod);
return false;
}
bool Miller_Rabin(ll n)
{
if(n==1) return false;
if(n<=3) return true;
if(!(n&1)) false;
for(int i=0;i<10;++i)
{
if(n==pri[i]) return true;
if(!check(pri[i],n-1,n)) return false;
}
return true;
}
ll gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
ll f(ll x,ll y,ll mod)
{
return (mul(x,x,mod)+y)%mod;
}
ll Pollard_Rho(ll x)
{
ll s=0,t=0,c=(ll)1*rand()%(x-1)+1,val=1;
for(ll goal=1;;goal<<=1,s=t,val=1)
{
for(ll step=1;step<=goal;++step)
{
t=f(t,c,x);
val=mul(val,abs(t-s),x);
if(step%127==0)
{
ll d=gcd(val,x);
if(d>1) return d;
}
}
ll d=gcd(val,x);
if(d>1) return d;
}
}
void fac(ll x)
{
if(x<=fac_max||x<2) return;
if(Miller_Rabin(x))
{
fac_max=max(fac_max,x);
return;
}
ll p=x;
while(p>=x) p=Pollard_Rho(x);
while(x%p==0) x/=p;
fac(x),fac(p);
}
int main()
{
read(T);
while(T--)
{
srand(114514);
read(n);
fac_max=0;
fac(n);
if(fac_max==n) puts("Prime");
else printf("%lld\n",fac_max);
}
return 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
#include<bits/stdc++.h>
#define maxn 5000010
#define all 5000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll T,n,tot;
ll p[maxn],phi[maxn],mu[maxn];
bool tag[maxn];
unordered_map<int,ll> s_phi,s_mu;
void init()
{
mu[1]=phi[1]=1;
for(int i=2;i<=all;++i)
{
if(!tag[i]) p[++tot]=i,phi[i]=i-1,mu[i]=-1;
for(int j=1;j<=tot;++j)
{
int k=i*p[j];
if(k>all) break;
tag[k]=true;
if(i%p[j]) phi[k]=phi[i]*phi[p[j]],mu[k]=mu[i]*mu[p[j]];
else
{
mu[k]=0,phi[k]=phi[i]*p[j];
break;
}
}
}
for(int i=1;i<=all;++i) phi[i]+=phi[i-1],mu[i]+=mu[i-1];
}
ll getphi(ll n)
{
if(n<=all) return phi[n];
if(s_phi[n]) return s_phi[n];
ll v=n*(n+1)/2;
for(int l=2,r;l<=n;l=r+1)
r=n/(n/l),v-=(r-l+1)*getphi(n/l);
return s_phi[n]=v;
}
ll getmu(ll n)
{
if(n<=all) return mu[n];
if(s_mu[n]) return s_mu[n];
ll v=1;
for(int l=2,r;l<=n;l=r+1)
r=n/(n/l),v-=(r-l+1)*getmu(n/l);
return s_mu[n]=v;
}
int main()
{
init(),read(T);
while(T--) read(n),printf("%lld %lld\n",getphi(n),getmu(n));
return 0;
}

扩展 BSGS

给定 ,求满足 的最小自然数

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
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag) x=-x;
}
ll a,b,p,x,y,ans;
unordered_map<ll,int> h;
ll qp(ll x,ll y,ll p)
{
ll v=1;
while(y)
{
if(y&1) v=v*x%p;
x=x*x%p,y>>=1;
}
return v;
}
ll exgcd(ll a,ll b)
{
if(!b)
{
x=1,y=0;
return a;
}
ll g=exgcd(b,a%b),tmp=x;
x=y,y=tmp-a/b*y;
return g;
}
ll inv(ll a,ll p)
{
exgcd(a,p);
return (x%p+p)%p;
}
ll bsgs(ll a,ll b,ll p)
{
ll s=ceil(sqrt(p)),v=b,val;
for(int i=0;i<=s;++i,v=v*a%p) h[v]=i;
v=val=qp(a,s,p);
for(int i=1;i<=s;++i,v=v*val%p)
if(h.count(v))
return i*s-h[v];
return -1;
}
ll exbsgs(ll a,ll b,ll p)
{
a%=p,b%=p;
if(b==1||p==1) return 0;
ll g,cnt=0,mul=1,v=a;
for(int i=1;i<=30;++i,v=v*a%p)
if(v==b)
return i;
while((g=exgcd(a,p))!=1)
{
if(b%g!=0) return -1;
cnt++,b/=g,p/=g,mul=mul*(a/g)%p;
}
ll ans=bsgs(a,b*inv(mul,p)%p,p);
if(~ans) return ans+cnt;
return -1;
}
int main()
{
while(1)
{
read(a),read(p),read(b);
if(!a&&!b&&!p) break;
h.clear(),ans=exbsgs(a,b,p);
if(~ans) printf("%lld\n",ans);
else puts("No Solution");
}
return 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
#include<bits/stdc++.h>
#define maxn 100010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag) x=-x;
}
ll n,x,y;
ll a[maxn],m[maxn];
ll mul(ll x,ll y,ll mod)
{
ll ans=0;
while(y)
{
if(y&1) ans=(ans+x)%mod;
x=(x+x)%mod;
y>>=1;
}
return ans%mod;
}
ll exgcd(ll a,ll b)
{
if(!b)
{
x=1,y=0;
return a;
}
ll ans=exgcd(b,a%b);
ll tmp=x;
x=y,y=tmp-a/b*y;
return ans;
}
ll excrt()
{
ll ans=a[1],M=m[1];
for(int i=2;i<=n;++i)
{
ll g=exgcd(M,m[i]),tmp=((a[i]-ans)%m[i]+m[i])%m[i];
if(tmp%g!=0) return -1;
ans+=mul(x,tmp/g,m[i])*M,M*=m[i]/g,ans=(ans%M+M)%M;
}
return ans;
}
int main()
{
read(n);
for(int i=1;i<=n;++i) read(m[i]),read(a[i]);
printf("%lld",excrt());
return 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
#include<bits/stdc++.h>
#define maxn 20000010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll a,m,b,phi,len;
char s[maxn];
bool flag;
ll qp(ll x,ll y)
{
ll v=1;
while(y)
{
if(y&1) v=v*x%m;
x=x*x%m,y>>=1;
}
return v%m;
}
ll get_phi(ll x)
{
ll v=x,t=sqrt(x);
for(int i=2;i<=t;++i)
{
if(x%i) continue;
v=v*(i-1)/i;
while(x%i==0) x/=i;
}
if(x>1) v=v*(x-1)/x;
return v;
}
int main()
{
read(a),read(m),phi=get_phi(m);
scanf("%s",s+1),len=strlen(s+1);
for(int i=1;i<=len;++i)
{
b=((b<<1)+(b<<3)+(s[i]^48));
if(b>=phi) b%=phi,flag=true;
}
if(flag) b+=phi;
printf("%lld",qp(a,b));
return 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
#include<bits/stdc++.h>
#define maxn 12
#define maxm 1000010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll n,m,p,cnt;
ll pri[maxn],pk[maxn],a[maxn][maxm];
void exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b) x=1,y=0;
else exgcd(b,a%b,y,x),y-=a/b*x;
}
ll inv(ll a,ll p)
{
ll x,y;
exgcd(a,p,x,y);
return (x%p+p)%p;
}
ll qp(ll x,ll y,ll p)
{
ll v=1;
while(y)
{
if(y&1) v=v*x%p;
x=x*x%p,y>>=1;
}
return v;
}
ll f(ll n,int x)
{
if(!n) return 1;
ll k=pk[x];
return f(n/pri[x],x)*qp(a[x][k],n/k,k)%k*a[x][n%k]%k;
}
ll C(ll n,ll m,int x)
{
ll v=0,p=pri[x],k=pk[x];
for(ll i=n;i;i=i/p) v+=i/p;
for(ll i=m;i;i=i/p) v-=i/p;
for(ll i=n-m;i;i=i/p) v-=i/p;
return f(n,x)*inv(f(m,x),k)%k*inv(f(n-m,x),k)%k*qp(p,v,k)%k;
}
ll crt(ll x,ll m,ll p)
{
return x*(m/p)*inv(m/p,p);
}
ll exlucas(ll n,ll m)
{
ll v=0;
for(int i=1;i<=cnt;++i) v=(v+crt(C(n,m,i),p,pk[i]))%p;
return v;
}
void init(int x)
{
a[x][0]=1;
for(int i=1;i<=pk[x];++i)
{
a[x][i]=a[x][i-1];
if(i%pri[x]) a[x][i]=a[x][i]*i%pk[x];
}
}
void pre(ll x)
{
for(int i=2;i*i<=x;++i)
{
if(x%i) continue;
ll v=1;
while(x%i==0) x/=i,v*=i;
pri[++cnt]=i,pk[cnt]=v;
}
if(x!=1) pri[++cnt]=x,pk[cnt]=x;
for(int i=1;i<=cnt;++i) init(i);
}
int main()
{
read(n),read(m),read(p),pre(p),printf("%lld",exlucas(n,m));
return 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
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll T,n,p,t,v1,v2;
struct Complex
{
ll x,y;
Complex(ll a=0,ll b=0)
{
x=a,y=b;
}
};
Complex operator * (const Complex &a,const Complex &b)
{
return Complex((a.x*b.x%p+a.y*b.y%p*t%p)%p,(a.x*b.y%p+a.y*b.x%p)%p);
}
Complex qp(Complex x,ll y)
{
Complex v=Complex(1,0);
while(y)
{
if(y&1) v=v*x;
x=x*x,y>>=1;
}
return v;
}
ll qp(ll x,ll y)
{
ll v=1;
while(y)
{
if(y&1) v=v*x%p;
x=x*x%p,y>>=1;
}
return v;
}
ll solve(ll n,ll p)
{
if(p==2) return n;
if(qp(n,(p-1)/2)==p-1) return -1;
ll a;
while(true)
{
a=rand()%p,t=((a*a%p-n)%p+p)%p;
if(qp(t,(p-1)/2)==p-1) break;
}
return qp(Complex(a,1),(p+1)/2).x;
}
int main()
{
read(T);
while(T--)
{
read(n),read(p),n%=p;
if(!n)
{
puts("0");
continue;
}
v1=solve(n,p);
if(v1==-1)
{
puts("Hola!");
continue;
}
v2=p-v1;
if(v1>v2) swap(v1,v2);
if(v1==v2) printf("%lld\n",v1);
else printf("%lld %lld\n",v1,v2);
}
return 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
#include<bits/stdc++.h>
#define p 1000000007
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll T,n;
struct matrix
{
ll a[6][6];
matrix()
{
memset(a,0,sizeof(a));
}
}m,ans;
matrix operator *(const matrix &a,const matrix &b)
{
matrix c;
for(int i=0;i<5;++i)
for(int j=0;j<5;++j)
for(int k=0;k<5;++k)
c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%p+p)%p;
return c;
}
matrix qp(matrix x,ll y)
{
matrix e;
for(int i=0;i<5;++i) e.a[i][i]=1;
while(y)
{
if(y&1) e=e*x;
x=x*x,y>>=1;
}
return e;
}
ll m1[6][6]=
{
{2,0,1,1,1}
};
ll m2[6][6]=
{
{1,1,0,0,0},
{1,0,0,0,0},
{2,0,1,1,1},
{0,0,1,0,0},
{2,0,0,0,1}
};
int main()
{
read(T);
memcpy(ans.a,m1,sizeof(ans.a));
memcpy(m.a,m2,sizeof(m.a));
while(T--)
{
read(n);
if(n<3) puts("0");
else printf("%lld\n",(ans*qp(m,n-3)).a[0][0]);
}
return 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
#include<bits/stdc++.h>
#define maxn 55
#define eps 1e-10
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,ans,cnt=1000;
struct node
{
int a,b;
}p[maxn];
int calc()
{
int v=0;
for(int i=1;i<=n+m;++i)
{
v+=p[i].a+p[i].b;
if(p[i-1].a==10) v+=p[i].a+p[i].b;
else if(p[i-1].a+p[i-1].b==10) v+=p[i].a;
}
return v;
}
void SA()
{
double T=1000000,delta=0.9895;
while(T>eps)
{
int x=rand()%(n+m)+1,y=rand()%(n+m)+1,v;
while(x==y||(m&&(x==n||y==n))) x=rand()%(n+m)+1,y=rand()%(n+m)+1;
swap(p[x],p[y]),v=calc();
if(v>ans) ans=v;
else if(exp((v-ans)/T)*RAND_MAX<rand()) swap(p[x],p[y]);
T*=delta;
}
}
int main()
{
srand((long long)new char),read(n);
for(int i=1;i<=n;++i) read(p[i].a),read(p[i].b);
if(p[n].a==10) m=1,read(p[n+1].a),read(p[n+1].b);
while(cnt--) SA();
printf("%d",ans);
return 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
#include<bits/stdc++.h>
#define maxn 35
#define inf 200000000
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=x*10+(c^48);c=getchar();}
if(flag)x=-x;
}
int t,n,num;
int a[maxn];
int ans;
int work()
{
int u=0,v=0;
for(int i=1;i<=(n+1)/2;++i) u+=a[i];
for(int i=(n+1)/2+1;i<=n;++i) v+=a[i];
return abs(u-v);
}
void SA()
{
double T=3000,delta=0.925;
while(T>1e-10)
{
int x=rand()%n+1, y=rand()%n+1;
swap(a[x],a[y]);
int now=work();
if(now<ans) ans=now;
else if(exp((ans-now)/T)*RAND_MAX<rand()) swap(a[x],a[y]);
T*=delta;
}
}
void clear()
{
ans=inf;
num=1111;
}
int main()
{
srand(19260817);
read(t);
while(t--)
{
clear();
read(n);
for(int i=1;i<=n;++i) read(a[i]);
while(num--) SA();
printf("%d\n",ans);
}
return 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
#include<bits/stdc++.h>
#define maxn 600010
#define inf 1000000000
#define lowbit(x) (x&(-x))
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,cnt;
int a[maxn],ans[maxn],tr[maxn];
char s[8];
struct node
{
int l,r,k,id,pos;
}p[maxn],q1[maxn],q2[maxn];
void update(int x,int v)
{
while(x<=n)
tr[x]+=v,x+=lowbit(x);
}
int query(int x)
{
int ans=0;
while(x)
ans+=tr[x],x-=lowbit(x);
return ans;
}
void solve(int L,int R,int l,int r)
{
if(L>R) return;
if(l==r)
{
for(int i=L;i<=R;++i)
if(p[i].l)
ans[p[i].id]=l;
return;
}
int mid=(l+r)>>1,cnt1=0,cnt2=0;
for(int i=L;i<=R;++i)
{
if(p[i].l)
{
int v=query(p[i].r)-query(p[i].l-1);
if(p[i].k<=v) q1[++cnt1]=p[i];
else p[i].k-=v,q2[++cnt2]=p[i];
}
else
{
if(p[i].k<=mid) q1[++cnt1]=p[i],update(p[i].pos,p[i].id);
else q2[++cnt2]=p[i];
}
}
for(int i=1;i<=cnt1;++i) p[L+i-1]=q1[i];
for(int i=1;i<=cnt2;++i) p[L+cnt1+i-1]=q2[i];
for(int i=1;i<=cnt1;++i)
if(q1[i].pos)
update(q1[i].pos,-q1[i].id);
solve(L,L+cnt1-1,l,mid),solve(L+cnt1,R,mid+1,r);
}
int main()
{
read(n),read(m);
for(int i=1;i<=n;++i)
read(a[i]),p[++cnt].k=a[i],p[cnt].pos=i,p[cnt].id=1;
for(int i=1;i<=m;++i)
{
scanf("%s",s);
if(s[0]=='Q')
read(p[++cnt].l),read(p[cnt].r),read(p[cnt].k),p[cnt].id=i;
else
{
int x,y;
read(x),read(y);
p[++cnt].k=a[x],p[cnt].pos=x,p[cnt].id=-1;
p[++cnt].k=a[x]=y,p[cnt].pos=x,p[cnt].id=1;
}
}
solve(1,cnt,-inf,inf);
for(int i=1;i<=cnt;++i)
if(ans[i])
printf("%d\n",ans[i]);
return 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
#include<bits/stdc++.h>

#define fi first
#define se second
#define mp make_pair
#define pb push_back

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

template<typename T> bool chkmax(T &x,T y){return x<y?x=y,1:0;}
template<typename T> bool chkmin(T &x,T y){return x>y?x=y,1:0;}

int readint(){
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}

int n,tot,cnt;
int v[2000005],nxt[2000005],h[1000005];
ull hs[1000005];
mt19937_64 mrand(chrono::steady_clock::now().time_since_epoch().count());

void addedge(int x,int y){
v[++tot]=y; nxt[tot]=h[x]; h[x]=tot;
v[++tot]=x; nxt[tot]=h[y]; h[y]=tot;
}

ull f(ull x){return x*x*x+x+7;}
ull calc(ull x){return f(x);}

void dfs(int u,int fa){
hs[u]=1;
for(int p=h[u];p;p=nxt[p]){
if(v[p]==fa) continue;
dfs(v[p],u);
hs[u]+=hs[v[p]];
}
hs[u]=calc(hs[u]);
}

int main(){
n=readint();
for(int i=1;i<n;i++) addedge(readint(),readint());
dfs(1,-1);
sort(hs+1,hs+n+1);
printf("%d\n",unique(hs+1,hs+n+1)-hs-1);
return 0;
}

Prufer 序列

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
#include<bits/stdc++.h>
#define maxn 5000010
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m;
ll ans;
ll fa[maxn],p[maxn],d[maxn];
void Prufer()
{
for(int i=1;i<n;++i) read(fa[i]),d[fa[i]]++;
int pos=1;
for(int i=1;i<=n-2;++i)
{
while(d[pos]) pos++;
p[i]=fa[pos];
while(i<=n-2&&--d[p[i]]==0&&p[i]<pos) p[i+1]=fa[p[i]],i++;
pos++;
}
for(int i=1;i<=n-2;++i) ans^=i*p[i];
}
void Father()
{
for(int i=1;i<=n-2;++i) read(p[i]),d[p[i]]++;
p[n-1]=n;
int pos=1;
for(int i=1;i<n;++i)
{
while(d[pos]) pos++;
fa[pos]=p[i];
while(i<n&&--d[p[i]]==0&&p[i]<pos) fa[p[i]]=p[i+1],i++;
pos++;
}
for(int i=1;i<n;++i) ans^=i*fa[i];
}
int main()
{
read(n),read(m);
if(m==1) Prufer();
else Father();
printf("%lld",ans);
return 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
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
struct bign
{
int len, s[30005];

bign (){
memset(s, 0, sizeof(s));
len = 1;
}
bign (int num) { *this = num; }
bign (const char *num) { *this = num; }

bign operator = (const int num){
char s[30005];
sprintf(s, "%d", num);
*this = s;
return *this;
}

void clean(){
while(len > 1 && !s[len-1]) len--;
}

bign operator = (const char *num){
memset(s, 0, sizeof s);
len = strlen(num);
for(int i = 0; i < len; i++) {
if (isdigit(num[len-i-1])) s[i] = num[len-i-1] - '0';
else s[i] = num[len-i-1] - 'A' + 10;
}
clean();
return *this;
}

bign operator + (const bign &b) const{
bign c;
c.len = max(len, b.len);
for (int i = 0; i < c.len; i++){
c.s[i] += s[i] + b.s[i];
c.s[i+1] += c.s[i] / 10;
c.s[i] %= 10;
}
if (c.s[c.len]) c.len++;
c.clean();
return c;
}

bign operator += (const bign &b){
*this = *this + b;
return *this;
}

bign operator * (const bign &b) {
bign c;
c.len = len + b.len;
for(int i = 0; i < len; i++)
for(int j = 0; j < b.len; j++){
c.s[i+j] += s[i] * b.s[j];
}

for(int i = 0; i < c.len; i++){
c.s[i+1] += c.s[i]/10;
c.s[i] %= 10;
}

c.clean();
return c;
}
bign operator *= (const bign &b){
*this = *this * b;
return *this;
}
bign operator - (const bign &b){
bign c;
c.len = 0;
for(int i = 0, g = 0; i < len; i++){
int x = s[i] - g;
if(i < b.len) x -= b.s[i];
if(x >= 0) g = 0;
else{
g = 1;
x += 10;
}
c.s[c.len++] = x;
}
c.clean();
return c;
}
bign operator -= (const bign &b){
*this = *this - b;
return *this;
}
bign operator / (const int &b){
int f = 0;
bign c;
for (int i = len-1; i >= 0; i--){
f = f *10 + s[i];
c.s[i] = f / b;
f %= b;
}
c.len = len;
c.clean();
return c;
}
bign operator / (const bign &b){
bign c, f = 0;
for(int i = len-1; i >= 0; i--){

f = f*10;
f.s[0] = s[i];
while(f > b || f == b){

f -= b;
c.s[i]++;
}
}
c.len = len;
c.clean();
return c;
}
bign operator /= (const bign &b){
*this = *this / b;
return *this;
}
bign operator % (const bign &b){
bign r = *this / b;
r = *this - r*b;
return r;
}
bign operator %= (const bign &b){
*this = *this % b;
return *this;
}

bool operator < (const bign &b){
if(len != b.len) return len < b.len;
for(int i = len-1; i >= 0; i--){
if(s[i] != b.s[i]) return s[i] < b.s[i];
}
return false;
}

bool operator > (const bign &b){
if(len != b.len) return len > b.len;
for(int i = len-1; i >= 0; i--){
if(s[i] != b.s[i]) return s[i] > b.s[i];
}
return false;
}

bool operator == (const bign &b){
return !(*this > b) && !(*this < b);
}

string str() const{
string res = "";
for (int i = 0; i < len; i++) {
if (s[i] < 10) res = char(s[i]+'0') + res;
else res = char(s[i] + 'A' - 10) + res;
}
return res;
}
} a, b, c;
 评论