模板

[TOC]

小技巧

位运算

  • __builtin_ctz() 二进制末尾 $0$ 的个数
  • __builtin_clz() 二进制前导 $0$ 的个数
  • __builtin_popcount() 二进制中 $1$ 的个数
  • __builtin_ffs() 二进制第一个 $1$ 的位置(从 $1$ 计数)

Python

读入一整行:

1
a = list(map(int,input().split()))

函数:

1
2
def square(x) :
return x ** 2

数据结构

字符串

扩展KMP

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
//求T与S所有后缀的最长公共前缀
ll extend[N],ne[N],ans;
char s[N],t[N];
int len;
void getne(chat *s) {
int len = strlen(s);
memset(ne,0,sizeof(ne));
ne[0]=len;
ll a,p;
a=1;
while( a<len && s[a]==s[a-1]) a++;
ne[1]=a-1;
a=1;
for(int i=2;i<len;i++) {
p=a+ne[a]-1;
if((i-1)+ne[i-a]<p) ne[i]=ne[i-a];
else {
int j=max(p-i+1,0);
while(i+j<len&&s[i+j]==s[j]) j++;
ne[i]=j; a=i;
}
}
}
void exkmp() {
getne(t);
int a = 0,p;
p = len;
extend[0]=p;
for(int i=1;i<len;i++) {
p = a + extend[a] - 1;
if( (i - 1) + ne[i-a] < p)
extend[i] = ne[i-a];
else {
int j = max(p-i+1,0);
while (j < len && i + j < len && s[i+j] == t[j]) j++;
extend[i] = j; a = i;
}
}
}

Lyndon 分解

最小表示的初始位置为 $s+s$ 的Lyndon分解中字符串初始位置 $\le |s|$ 且最大的那个。

1
2
3
4
5
6
7
//一个串是Lyndon串当且仅当该串字典序严格小于所有后缀
//一个串的Lyndon分解为t1,t2...tm,满足ti为Lyndon串,且ti>=ti+1
scanf("%s",s+1); n=strlen(s+1);
for(int i=1,j,k;i<=n;) {
for(j=i,k=i+1;k<=n&&s[j]<=s[k];j=(s[j]==s[k++])?j+1:i);
for(;i<=j;i+=k-j,ans^=(i-1)); //以0开头的字符串,以i-1为结尾
}

AC自动机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
queue<int> q;
int ne[N][Size],fail[N],cnt;
void getfail() {
ff(i,0,Size) if(ne[0][i]) q.push(ne[0][i]);
for(int u;!q.empty();) {
u=q.front(); q.pop();
ff(i,0,Size) {
int v=ne[u][i];
if(!v) ne[u][i]=ne[fail[u]][i];
else fail[v]=ne[fail[u]][i],q.push(v);
}
}
}
inline void ins(char *s) {
int n=strlen(s+1),u=0,c;
fo(i,1,n) {
c=s[i]-'a';
if(!ne[u][c]) ne[u][c]=++cnt;
u=ne[u][c];
//do sth
}
//do sth
}

SAM

单个串的SAM

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
namespace SAM {
int las, siz;
int ne[N << 1][S], fa[N << 1], len[N << 1];
inline void init() {
for(int i = 1; i <= siz; i ++) memset(ne[i], 0, sizeof(ne[i])), fa[i] = len[i] = 0;
las = siz = 1;
}
inline void extend(int c) {
int cur=++siz;
len[cur]=len[las]+1;
int p=las;
for(;p&&!ne[p][c];p=fa[p]) ne[p][c]=cur;
if(!p) fa[cur]=1;
else {
int q=ne[p][c];
if(len[q]==len[p]+1) fa[cur]=q;
else {
int clone=++siz;
len[clone]=len[p]+1;
memcpy(ne[clone],ne[q],sizeof(ne[q]));
fa[clone]=fa[q];
for(;p&&ne[p][c]==q;p=fa[p]) ne[p][c]=clone;
fa[cur]=fa[q]=clone;
}
}
las=cur;
}
}

bfs建广义SAM

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
namespace SAM {
int ne[N<<1][S],fa[N<<1],len[N<<1],si[N<<1],f[N<<1][21],siz;
void init() {siz=1;}
inline int ins(int c,int las) {
if(ne[las][c]) {
int q=ne[las][c];
if(len[q]=len[las]+1) {si[q]++; return q;}
int clone=++siz;
len[clone]=len[las]+1;
fa[clone]=fa[q];
memcpy(ne[clone],ne[q],sizeof(ne[q]));
for(;las&&ne[las][c]==q;las=fa[las]) ne[las][c]=clone;
fa[q]=clone;
si[clone]++;
return clone;
}
int cur=++siz,p=las;
len[cur]=len[p]+1;
for(;p&&!ne[p][c];p=fa[p]) ne[p][c]=cur;
if(!p) fa[cur]=1;
else {
int q=ne[p][c];
if(len[q]==len[p]+1) fa[cur]=q;
else {
int clone=++siz;
fa[clone]=fa[q];
len[clone]=len[p]+1;
memcpy(ne[clone],ne[q],sizeof(ne[q]));
for(;p&&ne[p][c]==q;p=fa[p]) ne[p][c]=clone;
fa[q]=fa[cur]=clone;
}
}
si[cur]++;
return cur;
}
vector<int> adj[N<<1];
void dfs(int u) {
for(auto v:adj[u]) {
f[v][0]=u;
fo(i,1,20) f[v][i]=f[f[v][i-1]][i-1];
dfs(v);
si[u]+=si[v];
}
}
void solve() {
fo(i,2,siz) adj[fa[i]].pb(i);
dfs(1);
}
inline int find(int x,int l) {
fd(i,20,0)
if(f[x][i]&&len[f[x][i]]>=l)
x=f[x][i];
return x;
}
}

struct node{
int u,x;
};
queue<node> q;
int n,m,pos[N],dep[N];
char s[N];
vector<int> adj[N];
void bfs(int u) {
SAM::init();
q.push((node){u,1});
dep[u]=1;
for(;!q.empty();)
{
u=q.front().u;
pos[u]=SAM::ins(s[u]-'A',q.front().x);
q.pop();
for(auto v:adj[u])
{
dep[v]=dep[u]+1;
q.push((node){v,pos[u]});
}
}
SAM::solve();
}

SA

SAIS - O(n)

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
namespace SA {
int sa[N], rk[N], height[N], s[N<<1], t[N<<1], p[N], cnt[N],cur[N];
#define pushS(x) sa[cur[s[x]]--] = x
#define pushL(x) sa[cur[s[x]]++] = x
#define induceSort(v) fill_n(sa,n,-1); fill_n(cnt,m,0); \
ff(i,0,n) cnt[s[i]] ++; \
ff(i,1,m) cnt[i] += cnt[i-1]; \
ff(i,0,m) cur[i] = cnt[i] - 1; \
fd(i,n1-1,0) pushS(v[i]); \
ff(i,1,m) cur[i] = cnt[i-1]; \
ff(i,0,n) if(sa[i] > 0 && t[sa[i] - 1]) pushL(sa[i] - 1); \
ff(i,0,m) cur[i] = cnt[i] - 1; \
fd(i,n-1,0) if(sa[i] > 0 && !t[sa[i] - 1]) pushS(sa[i] - 1)

void sais(int n,int m,int *s,int *t,int *p) {
int n1 = t[n-1] = 0, ch = rk[0] = -1, *s1 = s + n;
fd(i,n-2,0) t[i] = s[i] == s[i+1] ? t[i+1] : s[i] > s[i+1];
ff(i,1,n) rk[i] = t[i-1] && !t[i] ? (p[n1] = i,n1++) : -1;
induceSort(p);
for(int i = 0,x,y;i<n;i++)
if(~(x=rk[sa[i]])) {
if(ch < 1 || p[x+1] - p[x] != p[y+1] - p[y]) ch++;
else for(int j = p[x],k = p[y]; j <= p[x+1]; j++,k++)
if((s[j]<<1|t[j]) != (s[k]<<1|t[k])) {ch++; break;}
s1[y=x] = ch;
}
if(ch + 1 < n1) sais(n1,ch+1,s1,t+n,p+n1);
else ff(i,0,n1) sa[s1[i]] = i;
ff(i,0,n1) s1[i] = p[sa[i]];
induceSort(s1);
}
template<typename T>
void SuffixArray(int n, const T *str) {
++ n;
ff(i,0,n) s[i] = str[i];
sais(n,127,s,t,p);
ff(i,0,n) rk[sa[i]] = i;
for(int i = 0, k = height[0] = 0; i < n - 1; i++) {
for(int j = sa[rk[i] - 1]; i + k < n && j + k < n && s[i + k] == s[j + k]; k++);
if(height[rk[i]] = k) k--;
}
}
#undef pushS
#undef pushT
#undef induceSort
}
char s[N];
void Solve() {
scanf("%s",s);
int n = strlen(s);
s[n] = 0; // 需要注意,这里必须比所有字母要小。
SA::SuffixArray(n,s);

fo(i,1,n) printf("%d ",SA::sa[i] + 1);
printf("\n");
fo(i,2,n) printf("%d ",SA::height[i]);
}

倍增 - O(n log n)

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
inline bool cmp(int x,int y,int k){return t[x]==t[y]&&t[x+k]==t[y+k];}
struct SuufixArray{
int height[N],sa[N],rk[N],f[N][K],len; ll sum[N];
inline void rsort(int n,int m)
{
fo(i,0,m) base[i]=0;
fo(i,1,n) base[rk[t[i]]]++;
fo(i,1,m) base[i]+=base[i-1];
fd(i,n,1) sa[base[rk[t[i]]]--]=t[i];
}
inline void build(char *s,int n)
{
memset(t,0,sizeof(t));
len=n; s[n+1]=0;
fo(i,1,n) t[i]=i,rk[i]=s[i]-'a'+1;
rsort(n,128);
for(int w=1,p=0;p<n;w<<=1)
{
p=0;
fo(i,n-w+1,n) t[++p]=i;
fo(i,1,n) if(sa[i]>w) t[++p]=sa[i]-w;
rsort(n,p);
fo(i,1,n) t[i]=rk[i];
rk[sa[1]]=p=1;
fo(i,2,n) rk[sa[i]]=cmp(sa[i],sa[i-1],w)?p:++p;
}
for(int i=1,j,k=0;i<=n;height[rk[i++]]=k)
for(j=sa[rk[i]-1],(k?(k--):0);s[i+k]==s[j+k];k++);
fo(i,1,n) f[i][0]=height[i],sum[i]=sum[i-1]+(n-sa[i]+1)-height[i];
fo(j,1,K-1)
fo(i,1,n)
{
f[i][j]=f[i][j-1];
if(i+(1<<j)-1<=n) f[i][j]=min(f[i][j],f[i+(1<<(j-1))][j-1]);
}
}
inline int ask(int x,int y)
{
if(x==y) return len-x+1;
x=rk[x]; y=rk[y];
if(x>y) swap(x,y);
x++; int k=l2[y-x+1];
return min(f[x][k],f[y-(1<<k)+1][k]);
}
};

离线求区间 [l,r] 的border

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
const int N=4e5+5;
const int S=26;
const int M=7.5e6+5;
const int inf=2e9;
int ans[N>>1],n;

namespace S1{
int ls[M],rs[M],mx[M],cnt;
void ins(int &u,int l,int r,int p) {
u=++cnt;
if(l==r) return (void)(mx[u]=p);
int mid=l+r>>1;
p<=mid?ins(ls[u],l,mid,p):ins(rs[u],mid+1,r,p);
mx[u]=max(mx[ls[u]],mx[rs[u]]);
}
int merge(int x,int y) {
if(!x||!y) return x+y;
int u=++cnt;
ls[u]=merge(ls[x],ls[y]);
rs[u]=merge(rs[x],rs[y]);
mx[u]=max(mx[ls[u]],mx[rs[u]]);
return u;
}
int query(int u,int l,int r,int L,int R) {
if(!u||L>R) return 0;
if(l==r) return mx[u];
int mid=l+r>>1;
if(mid<R&&rs[u]) {
int tmp=query(rs[u],mid+1,r,L,R);
return tmp?tmp:query(ls[u],l,mid,L,R);
}
else return L<=mid?query(ls[u],l,mid,L,R):0;
}
}
namespace S2{
int mi[N<<2];
#define lc (u<<1)
#define rc (u<<1|1)
#define ls lc,l,mid
#define rs rc,mid+1,r
void init(int n) {
fo(i,0,n<<2) mi[i]=inf;
}
void add(int u,int l,int r,int p,int v)
{
if(p<l||p>r) return;
if(l==r) {mi[u]=v; return;}
int mid=l+r>>1;
p<=mid?add(ls,p,v):add(rs,p,v);
mi[u]=min(mi[lc],mi[rc]);
}
void del(int u,int l,int r,int p) {
mi[u]=inf;
if(l==r) return;
int mid=l+r>>1;
p<=mid?del(ls,p):del(rs,p);
}
int query(int u,int l,int r,int L,int R) {
if(mi[u]>L) return 0;
if(l==r) return mi[u]<=L?l:0;
int mid=l+r>>1;
if(mid<R&&mi[rc]<=L)
{
int tmp=query(rs,L,R);
return tmp?tmp:query(ls,L,R);
}
else return L<=mid?query(ls,L,R):0;
}
}

int p[N];
namespace Tree{
struct node{
int l,r,id;
};
vector<node> q[N];
vector<int> adj[N];
int siz[N],top[N],son[N],fa[N],val[N],rt[N];
inline void add(int x,int y){adj[x].pb(y);}
void dfs(int u) {
siz[u]=1;
if(p[u]) S1::ins(rt[u],1,n,p[u]);
for(auto v:adj[u]) {
fa[v]=u;
dfs(v);
rt[u]=S1::merge(rt[u],rt[v]);
siz[u]+=siz[v];
if(siz[son[u]]<=siz[v]) son[u]=v;
}
}
void dfs(int u,int tp) {
top[u]=tp; if(son[u]) dfs(son[u],tp);
for(auto v:adj[u]) if(v!=son[u]) dfs(v,v);
}
inline void jump(int x,int l,int r,int id) {
for(;x;x=fa[top[x]]) q[x].pb((node){l,r,id});
}
void ins(int u,int len) {
if(p[u]) S2::add(1,1,n,p[u],p[u]-len+1);
for(auto v:adj[u]) ins(v,len);
}
void del(int u) {
if(p[u]) S2::del(1,1,n,p[u]);
for(auto v:adj[u]) del(v);
}
void solve(int u) {
vector<int> vec;
int l,r,id,tmp;
vec.clear();
for(int v=u;v;v=son[v]) vec.pb(v);
for(auto v:vec) {
if(p[v]) S2::add(1,1,n,p[v],p[v]-val[v]+1);
for(auto w:adj[v]) if(son[v]!=w) ins(w,val[v]);
for(auto qu:q[v]) {
l=qu.l,r=qu.r,id=qu.id;
tmp=S1::query(rt[v],1,n,l,min(l+val[v]-1,r-1));
if(l<=tmp&&tmp<r) ans[id]=max(ans[id],tmp-l+1);
tmp=S2::query(1,1,n,l,r-1);
if(l<=tmp&&tmp<r) ans[id]=max(ans[id],tmp-l+1);
}
}
for(auto v:vec) {
if(p[v]) S2::del(1,1,n,p[v]);
for(auto w:adj[v]) if(son[v]!=w) del(w);
}
for(auto v:vec) for(auto w:adj[v]) if(son[v]!=w) solve(w);
}
}
namespace SAM{
int ne[N][S],len[N],fa[N],siz,las;
void init() {
siz=las=1;
}
inline int extend(int c,int id)
{
int p=las,cur=++siz;
len[cur]=len[p]+1;
for(;p&&!ne[p][c];p=fa[p]) ne[p][c]=cur;
if(!p) fa[cur]=1;
else {
int q=ne[p][c];
if(len[q]==len[p]+1) fa[cur]=q;
else {
int clone=++siz;
memcpy(ne[clone],ne[q],sizeof(ne[q]));
len[clone]=len[p]+1;
fa[clone]=fa[q];
for(;p&&ne[p][c]==q;p=fa[p]) ne[p][c]=clone;
fa[q]=fa[cur]=clone;
}
}
las=cur; ::p[las]=id;
return las;
}
void build_tree() {
fo(i,2,siz) Tree::add(fa[i],i);
fo(i,1,siz) Tree::val[i]=len[i];
}
}
char s[N>>1];
int m,pos[N];
void init() {
scanf("%s",s+1);
SAM::init();
n=strlen(s+1);
fo(i,1,n) pos[i]=SAM::extend(s[i]-'a',i);
SAM::build_tree();
Tree::dfs(1);
Tree::dfs(1,1);
S2::init(n);
}
void work() {
m=read();
int l,r;
fo(i,1,m)
{
l=read(); r=read();
Tree::jump(pos[r],l,r,i);
}
Tree::solve(1);
fo(i,1,m) printf("%d\n",ans[i]);
}
int main() {
init(); work();
return 0;
}

manacher

1
2
3
4
5
6
7
8
9
10
11
12
13
void manacher(char *a, int *f) {
static char s[N];
int n = strlen(a);
s[0] = '#';
for (int i = 0; i < n; i++) s[(i<<1)+1] = a[i],s[(i<<1)+2] = '#';
int id=0, mx=0, ans=0;
n = n * 2 - 1;
for (int i = 0; i < n; i++) {
if (mx > i) f[i] = min(f[(id<<1)-i], mx - i); else f[i] = 1;
while (i - f[i] >= 0 && i + f[i] < n && s[i+f[i]] == s[i-f[i]]) f[i] ++;
if (i + f[i] > mx) id = i, mx = i + f[i];
}
}

PAM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
namespace PAM {
int n,s[N],num[N],p,las,nex[N][S],fail[N],len[N],sz[N];
void init() {
for(int i=0;i<=n;i++) s[i]=0;
for(int i=1;i<=p;i++) len[i]=fail[i]=sz[i]=0,memset(nex[i],0,sizeof(nex[i]));
s[n=0] = len[p=2] = -1;
fail[las=1] = fail[2] = 2;
}
int getfail(int x) {for(;s[n-1-len[x]]!=s[n];x=fail[x]); return x;}
void add(int c) {
s[++n]=c;
int cur=getfail(las);
if(!nex[cur][c]) {
len[++p] = len[cur] + 2;
fail[p] = nex[getfail(fail[cur])][c];
if(!fail[p]) fail[p] = 1;
nex[cur][c] = p;
}
las = nex[cur][c];
sz[las]++;
}
}

猫树

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
// O(n log n)预处理,O(1) 查询区间乘积
namespace CatTree{
db a[N],s[K][N];
int len,pos[N];
void build(int u,int l,int r,int d) {
if(l==r) {
pos[l]=u; return;
}
int mid=(l+r)>>1;
s[d][mid]=a[mid]; s[d][mid+1]=a[mid+1];
fd(i,mid-1,l) s[d][i]=s[d][i+1]*a[i];
fo(i,mid+2,r) s[d][i]=s[d][i-1]*a[i];
build(u<<1,l,mid,d+1);
build(u<<1|1,mid+1,r,d+1);
}
inline void build(int *b,int n) {
fo(i,1,n) a[i]=w[i][b[i]-l[i]];
len=1;
for(;len<n;len<<=1);
fo(i,n+1,len) a[i]=0;
build(1,1,len,1);
}
inline db ask(int x,int y) {
if(x>y) return 1.;
if(x==y) return a[x];
int d=l2[pos[x]]-l2[pos[x]^pos[y]];
return s[d][x]*s[d][y];
}
}

杨氏图表

插入一个数 $x$:

找到当前行中最小的比 $x$ 大的数 $y$:

  • 若有,$x$ 将 $y$ 替换,往下一行插入 $y$;
  • 否则将 $x$ 放置在最后一个位置,并结束。

Treap

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
template<class T>
class Treap {
private:
int son[N][2], siz[N], rnd[N], cnt, root;
T val[N], sum[N];
inline int newnode(T v) {
int u = ++cnt;
val[u] = sum[u] = v;
siz[u] = 1;
rnd[u] = rand();
return u;
}
inline void pushup(int u) {
siz[u] = siz[son[u][0]] + siz[son[u][1]] + 1;
sum[u] = sum[son[u][0]] + sum[son[u][1]] + val[u];
}
int merge(int x, int y) {
if (!x || !y) return x + y;
if (rnd[x] < rnd[y]) {
son[x][1] = merge(son[x][1], y);
pushup(x);
return x;
}
else {
son[y][0] = merge(x, son[y][0]);
pushup(y);
return y;
}
}
void split_val(int u, T k, int &x, int &y) {
if (!u) return void(x = y = 0);
if (val[u] <= k) x = u, split_val(son[x][1], k, son[x][1], y);
else y = u, split_val(son[y][0], k, x, son[y][0]);
pushup(u);
}
void split_rank(int u, int k, int &x, int &y) {
if (!u) return void(x = y = 0);
if (k <= siz[son[u][0]]) y = u, split_rank(son[y][0], k, x, son[y][0]);
else x = u, split_rank(son[x][1], k - siz[son[u][0]] - 1, son[x][1], y);
pushup(u);
}
int kth(int u,int k) {
int s = siz[son[u][0]] + 1;
if (s == k) return u;
if (s < k) return kth(son[u][1], k - s);
return kth(son[u][0], k);
}
public:
Treap() {
cnt = root = 0;
}
void insert(T v) {
int x, y;
split_val(root,v,x,y);
root = merge(merge(x, newnode(v)), y);
}
void erase(T v) {
int x, y, z;
split_val(root,v,x,y);
split_val(x,v-1,x,z);
root = merge(merge(x, merge(son[z][0], son[z][1])), y);
}
T Kth(int v) {
return val[kth(root,v)];
}
T pre(T v) {
int x, y;
split_val(root, v, x, y);
T ans = val[kth(x, siz[x] - 1)];
root = merge(x, y);
return ans;
}
T nex(T v) {
int x, y;
split_val(root, v, x, y);
T ans = val[kth(y,1)];
root = merge(x, y);
return ans;
}
};

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
// LCT 维护最小生成树,维护子树和
struct Tree {
int ch[N][2], fa[N]; bool r[N];
int siz[N], si[N];
int val[N], mx[N];

Tree(int n = 0) {
for (int i = 0; i <= n; i ++)
ch[i][0] = ch[i][1] = fa[i] = r[i] = siz[i] = si[i] = 0,
val[i] = 0, mx[i] = i;
}
void clear(int n) {
for (int i = 0; i <= n; i ++)
ch[i][0] = ch[i][1] = fa[i] = r[i] = siz[i] = si[i] = 0,
val[i] = 0, mx[i] = i;
}

inline int getmax(int x, int y) {
return val[x] <= val[y] ? y : x;
}

#define lc ch[u][0]
#define rc ch[u][1]
#define son(x) (ch[fa[x]][1]==x)
#define isr(x) (ch[fa[x]][1]!=x&&ch[fa[x]][0]!=x)
inline void pushup(int u) {
if (!u)
return;
siz[u] = 1 + siz[lc] + siz[rc] + si[u];
mx[u] = u;
if (lc)
mx[u] = getmax(mx[u], mx[lc]);
if (rc)
mx[u] = getmax(mx[u], mx[rc]);
}
inline void torev(int u) {
if (!u)
return;
swap(lc, rc);
r[u] ^= 1;
}
inline void pushdown(int u) {if (r[u]) torev(lc), torev(rc), r[u]=0;}
inline void rotate(int x) {
int y = fa[x], z = fa[y], d = son(x);
fa[x] = z; if(!isr(y)) ch[z][son(y)] = x;
fa[ch[y][d] = ch[x][1-d]] = y;
fa[ch[x][1-d] = y] = x;
pushup(y);
}
inline void push(int u) {if(!isr(u)) push(fa[u]); pushdown(u);}
inline void splay(int x) {
push(x);
for(int y=fa[x]; !isr(x); rotate(x), y=fa[x]) {
if(!isr(y))
rotate(son(x) == son(y) ? y : x);
}
pushup(x);
}
inline void access(int x) {
for(int y=0; x;y = x,x = fa[x]) {
splay(x);
si[x] += siz[ch[x][1]];
ch[x][1] = y;
si[x] -= siz[y];
pushup(x);
}
}
inline void makeroot(int x) {access(x); splay(x); torev(x);}
inline void split(int x,int y) {makeroot(x); access(y); splay(y);}
inline void link(int x,int y) {split(x, y); fa[x] = y; si[y] += siz[x]; pushup(y);}
inline void cut(int x,int y) {split(x, y); ch[y][0] = fa[x] = 0;}
inline int findroot(int x) {access(x); splay(x); for(;ch[x][0];x=ch[x][0]); return x;}
inline int findmax(int x, int y) {
split(x, y);
return mx[y];
}
};

KD-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
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
struct point {
int v[5];
point (){}
point (int *a) {ff(i,0,5) v[i] = a[i];}
};
bool operator == (point a,point b) {
ff(i,0,5)
if (a.v[i] != b.v[i])
return 0;
return 1;
}
bool operator <= (point a,point b) {
ff(i,0,5)
if (a.v[i] > b.v[i])
return 0;
return 1;
}
bool operator < (point a,point b) {
ff(i,0,5)
if (a.v[i] >= b.v[i])
return 0;
return 1;
}
int now_c;
bool cmp(point a, point b) {
return a.v[now_c] < b.v[now_c];
}
point min(point a,point b) {
point c;
ff(i,0,5) c.v[i] = min(a.v[i], b.v[i]);
return c;
}
point max(point a,point b) {
point c;
ff(i,0,5) c.v[i] = max(a.v[i], b.v[i]);
return c;
}

const int N = 50004;

struct node {
point l,r,v;
int lc,rc;
int q,s;
int siz;
};
int rt,nt;
node t[N];

void update(int u) {
t[u].s = t[u].q; t[u].l=t[u].v; t[u].r=t[u].v;
t[u].siz = 1;
if (t[u].lc) {
t[u].s = max(t[u].s, t[t[u].lc].s);
t[u].l = min(t[u].l, t[t[u].lc].l);
t[u].r = max(t[u].r, t[t[u].lc].r);
t[u].siz += t[t[u].lc].siz;
}
if (t[u].rc) {
t[u].s = max(t[u].s, t[t[u].rc].s);
t[u].l = min(t[u].l, t[t[u].rc].l);
t[u].r = max(t[u].r, t[t[u].rc].r);
t[u].siz += t[t[u].rc].siz;
}
}

int tot, id[N];
point g[N];
int val[N];

void kth(int l,int r,int k,int d) {
int a,b;
a=l; b=r;
point x=g[(l+r)>>1];
now_c = d;
do {
while (cmp(g[a],x)) a++;
while (cmp(x,g[b])) b--;
if (a<=b) swap(g[a],g[b]), swap(val[a], val[b]), a++,b--;
}
while (a<=b);
if (k>=a&&a<r) kth(a,r,k,d);
if (k<=b&&l<b) kth(l,b,k,d);
}

int build(int d,int l,int r) {
if (l>r) return 0;
int u, mid = (l + r) >> 1;
u = id[tot --];
now_c = d;
kth(l, r, mid, d);
t[u].v = g[mid]; t[u].q = val[mid];
t[u].lc = build((d+1)%5,l,mid-1);
t[u].rc = build((d+1)%5,mid+1,r);
update(u);
return u;
}

void print(int x) {
if (!x) return;
print(t[x].lc);
id[++tot] = x;
g[tot] = t[x].v;
val[tot] = t[x].q;
print(t[x].rc);
}
void rebuild(int &x, int d) {
tot = 0;
print(x);
x = build(d, 1, tot);
assert(tot == 0);
}
bool bad(int u) {
return 3 * t[u].siz <= 4 * max(t[t[u].lc].siz, t[t[u].rc].siz);
}
void ins(int &u,int d,point s,int c) {
if (!u) {
u = ++nt; t[u].l = s; t[u].r = s; t[u].v = s;
t[u].siz = 1;
t[u].q = c;
update(u);
return;
}
if (s.v[d] <= t[u].v.v[d]) ins(t[u].lc, (d+1)%5, s, c);
else ins(t[u].rc, (d+1)%5, s, c);
update(u);
if (bad(u))
rebuild(u, d);
}
bool in(const point &a, const point &b) {
ff(i,0,5)
if (a.v[i] < b.v[i])
return 0;
return 1;
}
void qry(int u, int d, point r, int &ans) {
if (!u || !in(r, t[u].l) || ans > t[u].s) return;
if (t[u].r <= r) {
ans = max(ans, t[u].s);
return;
}
if (t[u].v <= r) {
ans = max(ans, t[u].q);
}
qry(t[u].lc,(d+1)%5,r, ans);
qry(t[u].rc,(d+1)%5,r, 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
int n;
bool flag[N];
int fa[N],val[N],dis[N],ls[N],rs[N];
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);
if(dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);
dis[x]=dis[rs[x]]+1;
return x;
}
void pop(int x)
{
fa[x]=merge(ls[x],rs[x]);
fa[fa[x]]=fa[x]; ls[x]=rs[x]=val[x]=dis[x]=0;
}
int main()
{
dis[0]=-1;
n=read();
for(int i=1;i<=n;i++) val[i]=read(),fa[i]=i;
char s[10];
for(int T=read(),x,y,fx,fy;T--;)
{
scanf("%s",s);
if(s[0]=='M')
{
x=read(); y=read();
if(flag[x]||flag[y]) continue;
fx=find(x); fy=find(y);
if(fx==fy) continue;
fa[fx]=fa[fy]=merge(fx,fy);
}
else
{
if(flag[x=read()]) {puts("0"); continue;}
flag[x=find(x)]=1;
printf("%d\n",val[x]);
pop(x);
}
}
}

数学

数学公式

斯特林数

$ S_2(n,k) = S_2(n-1,k-1) + k S_2(n-1,k)$

$ S_2(n,m)=\frac{1}{m!}\sum_{i=0}^m(-1)^{m-i}\binom{m}{i}i^n$

$ S_1(n,k) = S_1(n-1,k-1) + (n-1) S_1(n-1,k) $

$ F_n(x) = \sum_{i=0}^n S_1(n,i) x^i = \prod_{i=0}^{n-1} (x+i) $

$ x^{\overline{n}}=\sum_{k} S_1(n,k)x^k $

$ x^n=\sum_{k} S_2(n,k) (-1)^{n-k} x^{\overline{ k } } $

$ x^{\underline{n}}=\sum_{k} S_1(n,k) (-1) ^ { n- k } x^k $

$ x^n=\sum_{k} S_2(n,k) x^{\underline{ k } } $

贝尔数

$B_{n+1} = \sum_{k=0}^n\binom{n}{k} B_k$

$B_n = \sum_{k=0}^n S_2(n,k)$

伯努利数

$S_m(n) = \sum_{k=0}^{n-1} k^m = \frac{1}{m+1} \sum_{k=0}^m \binom{m+1}{k}B_kn^{m+1-k}$

$B(x) = \sum_{i} \frac{B_i}{i!}x^i = \frac{x}{e^x-1}$.

快速乘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
inline ll mul(const ll &a,const ll &b,const ll &p)
{
ll d=((long double)a/p*b+1e-9),res=a*b-d*p;
return res<0?res+p:res;
}
inline ll mul(ll a,ll b,const ll &p)
{
ll ans=0;
for(a%=p,b%=p;b;)
{
if(b&1) {ans+=a; if(ans>=p) ans-=p;}
b>>=1; a+=a; if(a>=p) a-=p;
}
return ans;
}

gcd & ex_gcd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ll gcd(ll x,ll y){return !y?x:gcd(y,x%y);}
ll ex_gcd(ll a,ll b,ll &x,ll &y) {
if(!b) {x=1; y=0; return a;}
ll d=ex_gcd(b,a%b,y,x);
y-=a/b*x; return d;
}
inline ll inv(ll a,ll m) {
ll x,y,d;
d=ex_gcd(a,m,x,y);
return (x+m)%m;
}
inline ll cal(ll a, ll b, ll c) {
ll x, y;
ll d = ex_gcd(a, b, x, y);
if (c % d != 0)
return -1;
x *= c / d;
b /= d;
if (b < 0) b = -b;
ll ans = x % b;
if (ans < 0) //最小非负整数解
ans += b;
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
namespace QuadraticResidue{
ll mod,n,w;
struct num {
ll a,b;
friend inline num operator *(const num &A,const num &B) {
return (num){(A.a*B.a+A.b*B.b%mod*w)%mod,(A.b*B.a+A.a*B.b)%mod};
}
};
inline ll Pow(num x,ll y) {
num ans=(num){1,0};
for(;y;y>>=1,x=x*x) if(y&1) ans=ans*x;
return ans.a;
}
inline ll L(ll x) {
ll ans=Pow(x,(mod-1)>>1);
return (ans==mod-1)?-1:ans;
}
inline ll work(ll x) {
x%=mod; int l=L(x);
if(l!=1) return l;
ll a=rand()%mod;
for(;L(w=(a*a%mod-x+mod)%mod)!=-1;a=rand()%mod);
num y=(num){a,1};
return Pow(y,(mod+1)>>1);
}
}

ex-CRT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
namespace CRT{
bool exCRT(int n,ll *a,ll *m,ll &A) {
ll M; //lcm
fo(i,1,n) a[i] = (a[i] % m[i] + m[i]) % m[i];
A = a[1], M = m[1];
ll c, d, x, y, t;
fo(i,2,n) {
c = a[i] - A;
d = ex_gcd(M, m[i], x, y);
if (c % d != 0) return 0;
t = m[i] / d;
x = mul(x, c / d, t); // x * (c/d) mod t
x = (x % t + t) % t;
A = A + M * x; M = M / d * m[i]; A = (A + M) % M;
}
A = (A + M - 1) % M + 1;
return 1;
}
}

Miller_Rabin & Pollard-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
namespace Pollard_Rho{
const __int128 ONE=1;
inline ll RandInt(ll l,ll r) {
static mt19937 rnd(time(0));
uniform_int_distribution<ll> dis(l,r);
return dis(rnd);
}
inline ll Pow(ll x,ll y,ll mod) {
ll ans=1;for(;y;y>>=1,x=ONE*x*x%mod)if(y&1) ans=ONE*ans*x%mod;
return ans;
}
inline bool Miller_Rabin(ll n){
static vector<ll> p = {2,3,5,7,11,13,17,19,23,29,31,37};
if(n<3||(!(n&1ll))) return n==2ll;
ll a=n-1,b=0;
for(;!(a&1);a>>=1) b++;
auto chk = [&](const ll &x) {
ll v = Pow(x,a,n);
if(v==1) return 1;
int j=1;
for(;j<=b;j++) {
if(v==n-1) break;
v=ONE*v*v%n;
}
if(j>b) return 0;
return 1;
};

if(n>37) {
for(auto x:p) if(!chk(x)) return 0;
return 1;
}
else {
for(auto x:p) if(n==x) return 1;
return 0;
}
}
ll Pollard_Rho(ll n) {
ll s=0,t=0,c=RandInt(1,n-1),val=1;
auto f = [&](const ll &x) {return (ONE*x*x+c)%n;};
for(int g=1;;g<<=1,s=t,val=1) {
for(int st=1;st<=g;st++) {
t=f(t);
val = (ONE*val*abs(t-s))%n;
if(st%127==0) {
ll d = __gcd(val,n);
if(d>1) return d;
}
}
ll d = __gcd(val,n);
if(d>1) return d;
}
}
void Divide(ll n) {
if(n<2) return;
if(Miller_Rabin(n)) {
//do something
return;
}
ll p = n;
for(;p==n;) p = Pollard_Rho(n);
Divide(n/p); Divide(p);
}
}

Powerful Number

一个数 $n$ 满足 $\forall p((p\in \mathbb{P} \and p|n )\rightarrow p^2|n )$,则称 $n$ 为Powerful Number(简称PN)。

求出 $n$ 以内的PN数可以暴力搜索质因数。

假设我们要求 $\sum_{i=1}^nf(i)$,构造两个函数 $g,h$ 使得:

  • $g$ 是积性函数。且 $g$ 的前缀和 $S_g(n)$ 容易求出。
  • $\forall p\in \mathbb{P},g(p)=f(p)$。
  • $f=g * h$。

那么,将原式变一变:

$$\sum_{i=1}^n f(i)\=\sum {i=1}^n\sum{d|i}h(d)g(i/d)\=\sum_{i=1}^nh(i)\sum_{j=1}^{n/i}g(j)\=\sum_{i=1}^n h(i)S_g(\left \lfloor \frac{n}{i} \right \rfloor)$$

可以发现 $f(p)=g(1)h(p)+g(p)h(1)=h(p)+g(p)$,又因为有 $g(p)=f(p)$,因此 $h(p)=0$。

显然 $h$ 是个积性函数,那么由积性函数的定义,对于 $h$ 的所有非 PN 数下标的取值均为 $0$。

我们只需快速求出所有 PN 数下标的 $h$ ,就可以了。

只需要求出 $h(p^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
struct Ans{
ll f,g,h;
// f = \sum_{i=0}^n (ai+b)/c
// g = \sum_{i=0}^n [(ai+b)/c]^2
// h = \sum_{i=0}^n i[(ai+b)/c]
};

Ans solve(int a,int b,int c,int n)
{
if(!a) return {
(ll)b/c*(n+1)%mod,
(ll)Sqr(b/c)*(n+1)%mod,
(ll)b/c*n%mod*(n+1)%mod*inv2%mod
};
else if(a>=c||b>=c) {
Ans d=solve(a%c,b%c,c,n);
return {
((ll)a/c*n%mod*(n+1)%mod*inv2+(ll)b/c*(n+1)+d.f)%mod,
((ll)n*(n+1)%mod*(n*2ll+1)%mod*inv6%mod*Sqr(a/c)+
(ll)(n+1)*Sqr(b/c)+(ll)n*(n+1)%mod*(a/c)%mod*(b/c)+
b/c*2ll*d.f+a/c*2ll%mod*d.h+d.g)%mod,
((ll)n*(n+1)%mod*(n*2ll+1)%mod*inv6%mod*(a/c)+
(ll)n*(n+1)%mod*inv2%mod*(b/c)+d.h)%mod
};
}
else {
int m=((ll)a*n+b)/c;
Ans d=solve(c,c-b-1,a,m-1);
return {
((ll)n*m-d.f+mod)%mod,
((ll)n*m%mod*m%mod-d.f-d.h*2+mod*3)%mod,
((ll)n*m%mod*(n+1)-d.g-d.f+mod+mod)%mod*inv2%mod
};
}
}

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
//y^x==z (mod p) −>x=?
int bsgs(int y,ll z,int p) {
y %= p, z %= p; j = z;
if(y==0){puts(”Cannot␣find␣x”);continue;}
for(k=s=1;k*k<=p;k++);
std::map<int,int>hash; flag=0;
for(int i=0;i<k;i++,s=1LL*s*y%p,j=1LL*j*y%p) hash[j]=i;
for(int i=1,j=s;i<=k&&!flag;i++,j=1LL*j*s%p)
if(hash.count(j)) ans=i*k−hash[j],flag=1;
if(flag==0) puts(”Cannot find x”);
else printf(”%d\n”,ans);
}
//exBSGS
int bsgs(int a,ll b,int p)
{
if(a%=p,b%=p,b==1)return 0;
ll t=1;int f,g,delta=0,m=sqrt(p)+1,i;
for(g=gcd(a,p);g!=1;g=gcd(a,p))
{
if(b%g)return1;
b/=g,p/=g,t=t*(a/g)%p,delta++;
if(b==t)return delta;
}
std::map<int,int>hash;

for(i=0;i<m;i++,b=b*a%p)hash[b]=i;
for(i=1,f=power(a,m);i<=m+1;i++)
if(t=t*f%p,hash.count(t))return i*m−hash[t]+delta;
return1;
}

扩展Lucas

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
inline pair<ll,ll> excrt(int n,ll *c,ll *m) {
ll nc=c[1],nm=m[1],d;
fo(i,2,n) {
d=gcd(m[i],nm);
if((c[i]-nc)%d!=0) return mp(-1,-1);
nc=inv(nm/d,m[i]/d)*((c[i]-nc)/d)*nm+nc;
nm=nm/d*m[i];
nc=(nc%nm+nm)%nm;
}
return mp(nc,nm);
}
const int M=1e6+5;
namespace ExLucas{
ll sum[M];
int t,p;
void init(int _p,int k) {
p=_p; t=1;
fo(i,1,k) t=t*p;
sum[0]=1;
fo(i,1,t) sum[i]=(sum[i-1]*((i%p==0)?1:i))%t;
}
int nq,sq;
ll fac(ll n) {
if(!n) return 1;
nq+=n/p;
if(sum[t]==1||(n/t)%2==0) return sum[n%t]*fac(n/p)%t;
return (t-sum[n%t]%t*fac(n/p)%t)%t;
}
inline ll C(ll n,ll m,int p,int k) {
init(p,k); sq=0;
ll ans,sum;
nq=0; ans=fac(n); sq+=nq;
nq=0; sum=fac(m); sq-=nq;
nq=0; sum=sum*fac(n-m)%t; sq-=nq;
return ans*inv(sum,t)%t*Pow(p,sq,t)%t;
}
}
ll n,m,mod,q,c[100],mo[100];
int top;
int main() {
cin>>n>>m>>mod; q=mod;
int t;
for(int i=2;i*i<=q;i++)
if(q%i==0) {
for(t=0;q%i==0;q/=i) t++;
++top;
c[top]=ExLucas::C(n,m,i,t);
mo[top]=ExLucas::t;
}
if(q!=1) {
c[++top]=ExLucas::C(n,m,q,1);
mo[top]=ExLucas::t;
}
pair<ll,ll> ans=excrt(top,c,mo);
printf("%lld",ans.fi);
return 0;
}

Min25筛

前提:积性函数;表示为 $\sum_{i\in \mathbb{P}}i^k$ 或 直接算;$f(p^k)$ 快速算。

$g(n,j)=\sum_{i=1}^{n}[i\in \mathbb{P} \text{ or }\min_i(p)> P_j]$

$$
g(n,j)=\begin{cases}
g(n,j-1) & \text{ if } P_j^2>n \newline
g(n,j-1)-P_j^k\left ( g(\left \lfloor \frac{n}{P_j} \right \rfloor,j-1)-g(P_{j-1},j-1) \right ) & \text{ otherwise }
\end{cases}
$$

$\sum_{i=1}^n[i\in \mathbb{P}]i^k=g(n,\pi( \left \lfloor \sqrt n\right \rfloor))$。

$$S(n,j)=\left ( g(n,\pi( \left \lfloor \sqrt n\right \rfloor)) \right )-g(P_{j-1},j-1))+ \sum_{k=j}^{\sqrt n}\sum_{P_k^{q+1}\leq n}(f(P_k^q)S(\left \lfloor \frac{n}{P_k^q} \right \rfloor,k+1)+f(P_{k}^{q+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
const int N=1000000;
ll n,m,Sqr,pri[N],sp[N],id1[N],id2[N],w[N];
ll h[N],g[N];
bool vis[N];int tot;
void init_prime(int n) {
vis[1]=1;
for(int i=2;i<=n;i++) {
if(!vis[i]) {pri[++tot]=i; sp[tot]=(sp[tot-1]+i)%mod;}
for(int j=1;j<=tot&&1ll*i*pri[j]<=n;j++) {
vis[i*pri[j]]=1;
if(i%pri[j]==0) break;
}
}
}
ll S(ll n,int j) {
if(n<=1||pri[j]>n) return 0;
ll t=(n<=Sqr)?id1[n]:id2[::n/n];
ll ans=(mod+(g[t]-sp[j-1])-(h[t]-(j-1))%mod)%mod;
if(j==1) ans+=2;
for(int k=j;k<=tot&&pri[k]*pri[k]<=n;k++) {
ll tmp=pri[k];
for(int q=1;tmp*pri[k]<=n;q++,tmp*=pri[k])
ans=Add(ans,Add(pri[k]^(q+1),Mul(pri[k]^q,S(n/tmp,k+1))));
}
return ans;
}
int main() {
scanf("%lld",&n); Sqr=sqrt(n);
init_prime(Sqr);
for(ll i=1,j;i<=n;i=j+1) {
j=n/(n/i); w[++m]=n/i;
h[m]=(w[m]-1)%mod;
g[m]=(w[m]%mod)*((w[m]+1)%mod)%mod*inv2%mod-1;
if(w[m]<=Sqr) id1[w[m]]=m;
else id2[j]=m;
}
for(int i=1;i<=tot;i++) {
ll k=pri[i]*pri[i];//可用long double 优化
for(int j=1;j<=m&&k<=w[j];j++) {
int t=(w[j]/pri[i]<=Sqr)?id1[w[j]/pri[i]]:id2[n/(w[j]/pri[i])];
h[j]=Add(h[j],(mod-(h[t]-(i-1)%mod))%mod);
g[j]=Add(g[j],mod-1ll*pri[i]*(g[t]-sp[i-1]+mod)%mod);
}
}
printf("%lld",(S(n,1)+1)%mod);
}

Stern-Brocot树计算 d(n) 前缀和

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
//时间复杂度O(n^{1/3} log n)
#define lll __int128

struct pr {
ll x, y;
pr (ll x0 = 0, ll y0 = 0) : x(x0), y(y0) {}
inline pr operator + (const pr &B) const {return pr(x + B.x, y + B.y);}
};
int top = 0;
pr stack[N];
ll S1(ll n) {
if(!n)
return 0;
auto inner = [&](ll x,ll y) {return n < (lll)x * y;};
auto steep = [&](ll x, pr v) {return (lll)n * v.x <= (lll)x * x * v.y;};
top = 0;
int i, crn = cbrt(n);
ll srn = sqrt(n), x = n / srn, y = srn + 1;
ll ret = 0;
pr L, R, M;
push(pr(1, 0)); push(pr(1, 1));
for (; ; ) {
for (L = stack[top--]; inner(x + L.x, y - L.y); x += L.x, y -= L.y)
ret += x * L.y + (L.y + 1) * (L.x - 1) / 2;
if (y <= crn) break;
for (R = stack[top]; !inner(x + R.x, y - R.y); R = stack[--top]) L = R;
for (; M = L + R, 1; )
if (inner(x + M.x, y - M.y)) push(R = M);
else {
if (steep(x + M.x, R)) break;
L = M;
}
}
for (i = 1; i < y; ++i) ret += n / i;
return ret * 2 - srn * srn;
}

O(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
82
83
84
85
86
87
88
89

const int n = 1000, m = n * n, mod = 1e9 + 7, lim = mod / n;

inline int inc(int x, int y) { return x += y - mod, x += x >> 31 & mod; }
inline int dec(int x, int y) { return x -= y, x += x >> 31 & mod; }
inline int mul(int x, int y) { return ll(x) * y % mod; }

int qpow(int a, int b) {
b += b >> 31 & (mod - 1);
int c = 1;
for (; b; b >>= 1, a = mul(a, a)) {
if (b & 1) c = mul(a, c);
}
return c;
}

const int maxn = n + 5, maxm = m + 5, maxl = lim + 5;

int sum[maxm], frac[maxm], N, F[maxm];

int cp, p[maxl], I[maxl];

bool np[maxl];

void init() {
rep(q, 1, n - 1) {
rep(p, 0, q) {
int i = p * m / q;
if (!sum[i]) {
sum[i] = 1, frac[i] = p * n + q;
}
}
}
rep(i, 1, m) {
sum[i] += sum[i - 1];
}
rep(i, 0, m) if (frac[i]) {
F[N++] = frac[i];
}
I[1] = 1;
rep(i, 2, lim) {
if (!np[i]) {
p[cp++] = i, I[i] = qpow(i, mod - 2);
}
int k;
for (int j = 0; j < cp && (k = i * p[j]) <= lim; j++) {
np[k] = 1, I[k] = mul(I[i], I[p[j]]);
if (i % p[j] == 0) {
break;
}
}
}
}

int inv(int a) {
int i = ll(a) * m / mod;
int k = sum[i];
ll f, t;
int p, q;
if (k < N) {
f = F[k];
p = f / n;
q = f - p * n;
t = ll(a) * q - ll(p) * mod;
if (abs(t) <= lim) {
return mul(q, t > 0 ? I[t] : mod - I[-t]);
}
}
if (--k >= 0) {
f = F[k];
p = f / n;
q = f - p * n;
t = ll(a) * q - ll(p) * mod;
if (abs(t) <= lim) {
return mul(q, t > 0 ? I[t] : mod - I[-t]);
}
}
return -1;
}

int main() {
init();
int n = read();
int x = 0;
rep(i, 1, n)
x = inc(mul(998244353, x), inv(read()));
put(x, '\n');
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
void FWT_or(int *a,int opt) {
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
if(opt==1)a[i+j+k]=(a[j+k]+a[i+j+k])%MOD;
else a[i+j+k]=(a[i+j+k]+MOD-a[j+k])%MOD;
}
void FWT_and(int *a,int opt) {
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
if(opt==1)a[j+k]=(a[j+k]+a[i+j+k])%MOD;
else a[j+k]=(a[j+k]+MOD-a[i+j+k])%MOD;
}
void FWT_xor(int *a,int opt) {
for(int i=1;i<N;i<<=1)
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
int X=a[j+k],Y=a[i+j+k];
a[j+k]=(X+Y)%MOD;a[i+j+k]=(X+MOD-Y)%MOD;
if(opt==-1)a[j+k]=1ll*a[j+k]*inv2%MOD,a[i+j+k]=1ll*a[i+j+k]*inv2%MOD;
}
}
//子集卷积,形如 f[S] = a[S] * sum_{U or V = S,U and V = 空集} f[U] * g[V],可多记一维,O(2^n*n^2)。
//k 进制FWT,相当于将上面的改成乘以范德蒙德矩阵,及 A[i][j] = w_k^{ij}或者w_k^{-ij}/k。

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
struct Com{
db x,y;
Com(db _x=0,db _y=0) {x=_x,y=_y;}
friend inline Com operator + (const Com &A,const Com &B)
{return Com(A.x+B.x,A.y+B.y);}
friend inline Com operator - (const Com &A,const Com &B)
{return Com(A.x-B.x,A.y-B.y);}
friend inline Com operator * (const Com &A,const Com &B)
{return Com(A.x*B.x-A.y*B.y,A.x*B.y+B.x*A.y);}
};
const int M=1<<21;
namespace Poly{
const db pi=acos(-1);
int R[M]; Com W[M];
void fft(Com *a,int n,int opt) {
fo(i,0,n-1) {
R[i]=(R[i>>1]>>1)|((i&1)*(n>>1));
if(i<R[i]) swap(a[i],a[R[i]]);
}
Com w;
for(int i=1;i<n;i<<=1)
for(int j=0;j<n;j+=(i<<1))
for(int k=0;k<i;k++)
w=W[i+k]*a[i+j+k],
a[i+j+k]=a[j+k]-w,
a[j+k]=a[j+k]+w;
if(opt!=-1) return;
reverse(a+1,a+n);
for(int i=0;i<n;i++) a[i].x/=n;
}
void init() {
for(int i=1;i<M;i<<=1)
fo(j,0,i-1)
W[i+j]=Com(cos(pi*j/i),sin(pi*j/i));
}
}

拆系数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
const ll mo=1e9+7;
db pi=acos(-1.);
struct P{
db x,y;
P(db _x=0,db _y=0) {x=_x,y=_y;}
friend inline P operator +(const P&A,const P&B){return (P){A.x+B.x,A.y+B.y};}
friend inline P operator -(const P&A,const P&B){return (P){A.x-B.x,A.y-B.y};}
friend inline P operator *(const P&A,const P&B){return (P){A.x*B.x-A.y*B.y,A.x*B.y+A.y*B.x};}
friend inline P operator /(const P&A,const db &x){return (P){A.x/x,A.y/x};}
};
P conj(P A) {return (P){A.x,-A.y};}
const int M=1<<20;
P W[M]; int R[M];
inline void PolyInit() {
for(int i=1;i<M;i<<=1)
fo(j,0,i-1)
W[i+j]=(P){cos(pi*j/i),sin(pi*j/i)};
}
typedef vector<P> Poly;
inline void ntt(P *a,int n,int t) {
fo(i,0,n-1) {
R[i]=(R[i>>1]>>1)|((i&1)*(n>>1));
if(i<R[i]) swap(a[i],a[R[i]]);
}
P w;
for(int i=1;i<n;i<<=1)
for(int j=0;j<n;j+=(i<<1))
for(int k=0;k<i;k++)
w=W[i+k]*a[i+j+k],
a[i+j+k]=a[j+k]-w,
a[j+k]=a[j+k]+w;
if(t==1) return;
reverse(a+1,a+n);
fo(i,0,n-1) a[i]=a[i]/n;
}
inline void ntt(Poly &A,int n,int t){ntt(&A[0],n,t);}
inline Poly operator +(Poly A,Poly B) {
A.resize(max(A.size(),B.size()));
fo(i,0,B.size()-1) A[i]=A[i]+B[i];
return A;
}
#define Real(A) ((ll)floor(A.x+0.5))
#define Imag(A) ((ll)floor(A.y+0.5))
inline Poly operator *(Poly A,Poly B) {
static Poly C,D;
int n=A.size(),m=B.size(),k=n+m-1,len=1;
for(;len<k;len<<=1);
C.resize(len); D.resize(len); A.resize(len); B.resize(len);
ff(i,0,len) C[i]=(P){Real(A[i])&32767,Real(A[i])>>15},D[i]=(P){Real(B[i])&32767,Real(B[i])>>15};
ntt(C,len,1); ntt(D,len,1);
int j;
ff(i,0,len) {
P d4,d0,d1,d2,d3;
j=(len-i)&(len-1);
d4=conj(C[j]); d0=(d4+C[i])*P(0.5,0); d1=(d4-C[i])*P(0,0.5);
d4=conj(D[j]); d2=(d4+D[i])*P(0.5,0); d3=(d4-D[i])*P(0,0.5);
A[i]=d0*d2+d1*d3*P(0,1);
B[i]=d0*d3+d1*d2;
}
ntt(A,len,-1); ntt(B,len,-1);
ff(i,0,len) C[i]=(Real(A[i]) + (Imag(A[i]) % mo << 30) + (Real(B[i]) % mo << 15))%mo;
C.resize(k); return C;
}

NTT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
const ll mod=998244353;
const int M=1<<20;
ll W[M]; int R[M];
inline void PolyInit() {
ll w;
for(int i=1;i<M;i<<=1) {
W[i]=1; w=Pow(3,(mod-1)/2/i);
fo(j,1,i-1) W[i+j]=W[i+j-1]*w%mod;
}
}
typedef vector<ll> Poly;
inline void ntt(ll *a,int n,int t) {
fo(i,0,n-1) {
R[i]=(R[i>>1]>>1)|((i&1)*(n>>1));
if(i<R[i]) swap(a[i],a[R[i]]);
}
ll w;
for(int i=1;i<n;i<<=1)
for(int j=0;j<n;j+=(i<<1))
for(int k=0;k<i;k++)
w=W[i+k]*a[i+j+k]%mod,
a[i+j+k]=Dec(a[j+k],w),
a[j+k]=Add(a[j+k],w);
if(t==1) return;
reverse(a+1,a+n);
w=Pow(n,mod-2);
fo(i,0,n-1) a[i]=w*a[i]%mod;
}
inline void ntt(Poly &A,int n,int t){ntt(&A[0],n,t);}
inline Poly operator +(Poly A,Poly B) {
A.resize(max(A.size(),B.size()));
fo(i,0,B.size()-1) A[i]=Add(A[i],B[i]);
return A;
}
inline Poly operator -(Poly A,Poly B) {
A.resize(max(A.size(),B.size()));
fo(i,0,B.size()-1) A[i]=Dec(A[i],B[i]);
return A;
}
inline Poly df(Poly A) {
fo(i,1,A.size()-1) A[i-1]=A[i]*i%mod;
A.resize(A.size()-1);
return A;
}
inline Poly jf(Poly A) {
A.pb(0);
fd(i,A.size()-1,1) A[i]=A[i-1]*Pow(i,mod-2)%mod;
A[0]=0; return A;
}
inline Poly operator *(Poly A,ll k) {
fo(i,0,A.size()-1) A[i]=Mul(A[i],k);
return A;
}
inline Poly operator *(Poly A,Poly B) {
int n=A.size(),m=B.size(),k=n+m-1,len=1;
for(;len<k;len<<=1);
A.resize(len); ntt(A,len,1);
B.resize(len); ntt(B,len,1);
fo(i,0,len-1) A[i]=A[i]*B[i]%mod;
ntt(A,len,-1);
A.resize(k);
return A;
}
inline Poly operator ~(Poly f) {
int n=f.size();
Poly g,h;
g.pb(Pow(f[0],mod-2));
int m=2;
for(;m<n;m<<=1) {
h.resize(m<<1); g.resize(m<<1);
fo(i,0,m-1) h[i]=f[i];
ntt(h,m<<1,1); ntt(g,m<<1,1);
fo(i,0,(m<<1)-1) g[i]=Mul(2+mod-Mul(g[i],h[i]),g[i]);
ntt(g,m<<1,-1); g.resize(m);
}
g.resize(m<<1); f.resize(m<<1);
ntt(f,m<<1,1); ntt(g,m<<1,1);
fo(i,0,(m<<1)-1) g[i]=Mul(2+mod-Mul(g[i],f[i]),g[i]);
ntt(g,m<<1,-1); g.resize(n);
return g;
}
inline Poly Ln(Poly A) {
int n=A.size();
A=jf((~A)*df(A));
A.resize(n); return A;
}
inline Poly Exp(const Poly &A) {
int n=1; for(;n<A.size();n<<=1);
Poly B,C,D; B.clear(); B.pb(1);
for(int m=2;m<=n;m<<=1) {
C=B; C.resize(m); D=A; D.resize(m);
C=D-Ln(C); C[0]=Add(C[0],1);
B=B*C; B.resize(m);
}
B.resize(A.size()); return B;
}
inline Poly operator ^(Poly A,ll k) {
if(!A.size()) return A;
ll tmp=A[0],w=Pow(tmp,k);
tmp=Pow(tmp,mod-2);
fo(i,0,A.size()-1) A[i]=A[i]*tmp%mod;
A=Exp(Ln(A)*k);
fo(i,0,A.size()-1) A[i]=A[i]*w%mod;
return A;
}
inline Poly Cos(Poly A) {
static ll w4=Pow(3,(mod-1)/4);
return (Exp(A*w4)+Exp(A*(mod-w4)))*((mod+1)/2);
}
inline Poly Sin(Poly A) {
static ll w4=Pow(3,(mod-1)/4);
return (Exp(A*w4)-Exp(A*(mod-w4)))*(Pow(w4,mod-2)*((mod+1)/2)%mod);
}
inline Poly Sqrt(Poly A) {
Poly C,D,B(1,1);
C.clear(); D.clear();
int n=A.size();
for(int l=4;(l>>2)<n;l<<=1) {
C=A; C.resize(l>>1);
D=B; D.resize(l>>1); D=(~D);
C.resize(l); D.resize(l);
ntt(C,l,1); ntt(D,l,1);
ff(i,0,l) C[i]=C[i]*D[i]%mod;
ntt(C,l,-1);
B.resize(l>>1);
ff(i,0,(l>>1)) B[i]=Add(C[i],B[i])*((mod+1)>>1)%mod;
}
B.resize(n);
return B;
}
inline Poly operator/(Poly A,Poly B) {
int len=1,deg=A.size()-B.size()+1;
reverse(all(A)); reverse(all(B));
for(;len<=deg;len<<=1);
B.resize(len); B=~B; B.resize(deg);
A=A*B; A.resize(deg);
reverse(all(A));
return A;
}
inline Poly operator%(const Poly &A,const Poly &B) {
if(A.size()<B.size()) return A;
Poly C=A-(A/B)*B;
C.resize(B.size()-1);
return C;
}
inline Poly Pow(Poly A,ll n,Poly M) {
Poly B=A; n--;
for(;n;n>>=1,A=(A*A)%M) if(n&1ll) B=(B*A)%M;
return B;
}
const int N=64005*4;
#define lc (u<<1)
#define rc ((u<<1)|1)
Poly P[N<<2];
ll ans[N],a[N];
inline Poly MulT(Poly A,Poly B) {
int n=A.size(),m=B.size();
reverse(all(B));
int len=1;
for(;len<n;len<<=1);
A.resize(len); B.resize(len);
ntt(A,len,1); ntt(B,len,1);
ff(i,0,len) A[i]=A[i]*B[i]%mod;
ntt(A,len,-1);
B.clear();
len--;
fo(i,m-1,n+m-2) B.pb(A[i&len]);
return B;
}
void solve(int u,int l,int r) {
if(l==r) {
P[u].pb(1); P[u].pb(Dec(0,a[l]));return;
}
int mid=(l+r)>>1;
solve(lc,l,mid); solve(rc,mid+1,r);
P[u]=P[lc]*P[rc];
}
void solve(int u,int l,int r,Poly A) {
A.resize(r-l+1);
if(l==r) return (void)(ans[l]=A[0]);
int mid=(l+r)>>1;
solve(lc,l,mid,MulT(A,P[rc])); solve(rc,mid+1,r,MulT(A,P[lc]));
}
int n,m,k;
Poly F,G;
int main() {
PolyInit();
n=read(); m=read(); k=max(n,m);
fo(i,0,n) F.pb(read());
F.resize(n+k+1);
fo(i,1,m) a[i]=read();
solve(1,1,k);
F=MulT(F,(~P[1]));
solve(1,1,k,F);
fo(i,1,m) printf("%lld\n",ans[i]);
return 0;
}

const int N=1e5+5;
ll fc[N],fv[N],iv[N];
inline void init(int n) {
PolyInit();
fc[0]=1;
fo(i,1,n) fc[i]=fc[i-1]*i%mod;
fv[n]=Pow(fc[n],mod-2);
fd(i,n,1) fv[i-1]=fv[i]*i%mod;
iv[1]=1;
fo(i,2,n) iv[i]=(mod-mod/i)*iv[mod%i]%mod;
}

inline void calcS(int n) {//第一类斯特林数
if(!n) {s[0]=1; return;}
if(n==1) {s[1]=1; return;}
if(n&1) {
calcS(n-1);
fd(i,n,1) s[i]=Add(s[i-1],Mul(s[i],n-1));
return;
}
else {
calcS(n>>1); int l=n>>1,len;
for(len=1;len<=n;len<<=1);
d[0]=1; fo(i,1,l) d[i]=d[i-1]*l%mod;
fo(i,0,l) d[i]=d[i]*fv[i]%mod,c[i]=s[i]*fc[i]%mod;
reverse(&d[0],&d[l+1]);
ntt(d,len,1); ntt(c,len,1);
fo(i,0,len-1) d[i]=d[i]*c[i]%mod;
ntt(d,len,-1);
fo(i,0,l) d[i]=d[i+l]*fv[i]%mod;
fo(i,l+1,len-1) d[i]=c[i]=0;
fo(i,0,l) c[i]=s[i];
ntt(d,len,1); ntt(c,len,1);
fo(i,0,len-1) d[i]=d[i]*c[i]%mod;
ntt(d,len,-1);
fo(i,0,n) s[i]=d[i];
fo(i,0,len-1) d[i]=c[i]=0;
}
}
inline void calcB(int n) {
//求伯努利数
//原理:伯努利数的EGF=x/(e^x-1)
//sum(i=[0,n-1])[B_i*C(n,i)]=0(n>1)
n+=2;
B.resize(n+1);
fo(i,0,n-1) B[i]=fv[i+1];
B=~B;
fo(i,0,n) B[i]=B[i]*fc[i]%mod;
B.resize(n);
}

生成函数相关

图计数问题

以下问题,$n\leq 10^5$。模$998244353$。

带标号无向图数量

$f_i=2^{\binom{i}{2}}$

带标号无向连通图数量

设答案为$g_i$,其生成函数为$G$,第1题的生成函数是$F$,那么一个无向图就是由一些无向连通图组合起来的。

那么可以枚举一个无向图是由多少个连通图组成的,假设为$i$,那么答案跟$F^i(x)$所对应的系数有关系。因为无序,所以要除一个$i!$,因此有:

$F(x)=\sum_{i=0}^{\infty}\frac{G^i(x)}{i!}=e^{G(x)}$

同时求$\ln$得$G(x)=\ln F(x)$

多项式求$\ln$即可。

带标号DAG数量

很明显先dp,设$f_i$为$i$个点带标号的DAG数量。$f_0=1$。先假设有$j$个点度数为0,符合上述条件的$j$个点的集合有$\binom{i}{j}$个;这$j$个点可以任意向其他$i-j$个点连边,方案数有$2^{j(i-j)}$个;剩下的$i-j$个点也必须是个DAG,方案数为$f_{i-j}$乘法原理得方案数为$\binom{i}{j}2^{j(i-j)}f_{i-j}$,可是剩下的$i-j$个点中也还是有一些度数为0的点的,那么容斥一下就好了。

得到dp方程式:$f_i=\sum_{j=1}^i(-1)^{j-1}\binom{i}{j}2^{j(i-j)}f_{i-j}$

$$\frac{f_i}{(\sqrt{2})^{i^2}i!}=\sum_{j=1}^i\frac{(-1)^{j-1}}{(\sqrt{2})^{j^2}j!}\times \frac{f_{i-j}}{(\sqrt{2})^{({i-j})^2}(i-j)!}$$

设$F(i)=\frac{f_i}{(\sqrt{2})^{i^2}i!}$,$G(i)=\frac{(-1)^{i-1}}{(\sqrt{2})^{i^2}i!}(i\geq1),G(0)=0$

那么$F=GF+1$,即$F=\frac{1}{1-G}$

多项式求逆,时间复杂度$O(n \log n)$

带标号的DAG数量,要求图弱连通

和第二题是一样的。设$H(x)$为该题答案的指数型生成函数,$F(x)$为第三题的指数型生成函数,那么$F(x)=e^{H(x)}$,$H(x)=\ln F(x)$,多项式求$\ln$即可。

拉格朗日反演

若两个函数 $F(x),G(x)$ 满足:$F(G(x))=x$。

则有:$[x^n]G(x)=\frac{1}{n}[x^{n-1}] (\frac{x}{F(x)})^n$

证明:懒得证了,会用就行。

线性代数

线性规划

单纯形法

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
/*
a为(m+1)行(n+1)列的矩阵,前 m 行为限制条件,一共 n 个未知数:
\sum_{j=0}^{n-1} a[i][j]x[j] <= a[i][n]
最大化 \sum_{j=0}^{m-1} a[m][j]x[j] - a[m][n]
并满足 x[j] >= 0
*/
int n,m;
db a[N][N];
const db inf = 1e100;
const db eps = 1e-10;
int f[N],d[N];
void pivot(int r,int c) {
swap(d[c],f[r]);
a[r][c] = 1 / a[r][c];
fo(j,0,n) if(j!=c) a[r][j] *= a[r][c];
fo(i,0,m)
if(i!=r) {
fo(j,0,n) if(j!=c) a[i][j] -= a[i][c] * a[r][j];
a[i][c] = -a[i][c] * a[r][c];
}
}
bool feasible() {
for(;;) {
int r,c;
db p = inf;
fo(i,0,m-1) if(a[i][n] < p) p =a[r=i][n];
if(p>-eps)
return true;
p = 0;
ff(i,0,n)
if(a[r][i]<p)
p=a[r][c=i];
if(p>-eps)
return false;
p=a[r][n]/a[r][c];
for(int i=r+1;i<m;i++)
if(a[i][c] > eps) {
db v = a[i][n]/a[i][c];
if(v<p) r=i,p=v;
}
pivot(r,c);
}
}
int simplex(int _n,int _m,db *x,db &ans) {
n = _n, m = _m;
ff(i,0,n) d[i] = i;
ff(i,0,m) f[i] = n+i;
if(!feasible())
return 0;
for(;;) {
int r,c;
db p = 0;
ff(i,0,n) if(a[m][i] > p) p = a[m][c=i];
if(p < eps) {
ff(i,0,n) if(d[i] < n) x[d[i]] = 0;
ff(i,0,m) if(f[i] < n) x[f[i]] = a[i][n];
ans = -a[m][n];
return 1;
}
p = inf;
ff(i,0,m)
if(a[i][c] > eps) {
db v = a[i][n]/a[i][c];
if(v<p) r=i,p=v;
}
if(p == inf)
return -1;
pivot(r,c);
}
}

矩阵相关

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
const int N=502;
struct matrix{
ll a[N][N];
matrix(){memset(a,0,sizeof(a));}
void clear(){memset(a,0,sizeof(a));}
};
namespace Mat{
static ll a[N][N],b[N][N+N],c[N][N],d[N][N];
static matrix B,C;
inline ll det(const matrix &A,int n)//行列式
{
fo(i,1,n) fo(j,1,n) a[i][j]=A.a[i][j];
ll d=1,iv,tmp;
fo(i,1,n)
{
int k=i;
fo(j,i+1,n) if(a[j][i]) {k=j; break;}
if(k!=i) {fo(j,i,n) swap(a[k][j],a[i][j]); d=(mod-d)%mod;}
if(!a[i][i]) return 0;
iv=Pow(a[i][i],mod-2);
fo(j,i+1,n)
{
tmp=a[j][i]*iv%mod;
fo(k,i,n) a[j][k]=Dec(a[j][k],a[i][k]*tmp%mod);
}
d=d*a[i][i]%mod;
}
return d;
}
inline matrix inv(const matrix &A,int n)//求逆
{
fo(i,1,n) fo(j,1,n) b[i][j+n]=0,b[i][j]=A.a[i][j];
fo(i,1,n) b[i][i+n]=1;
ll iv,tmp;
fo(i,1,n)
{
int k=n+1;
fo(j,i,n) if(b[j][i]) {k=j; break;}
if(k==n+1) continue;
if(k!=i) fo(j,1,n+n) swap(b[i][j],b[k][j]);
iv=Pow(b[i][i],mod-2);
fo(j,i+1,n)
{
tmp=b[j][i]*iv%mod;
fo(k,i,n+n) b[j][k]=Dec(b[j][k],b[i][k]*tmp%mod);
}
}
fd(i,n,1)
{
iv=Pow(b[i][i],mod-2);
fo(j,i,n+n) b[i][j]=b[i][j]*iv%mod;
fd(j,i-1,1)
if(b[j][i])
{
tmp=b[j][i];
fo(k,i,n+n) b[j][k]=Dec(b[j][k],b[i][k]*tmp%mod);
}
}
B.clear();
fo(i,1,n) fo(j,1,n) B.a[i][j]=b[i][j+n];
return B;
}
int r(const matrix A,int n)//求秩
{
fo(i,1,n) fo(j,1,n) a[i][j]=A.a[i][j];
int d=0;
ll iv,tmp;
fo(i,1,n)
{
int k=n+1;
fo(j,i,n) if(a[j][i]) {k=j; break;}
if(k==n+1) continue;
d++;
if(k!=i) fo(j,i,n) swap(a[i][j],a[k][j]);
iv=Pow(a[i][i],mod-2);
fo(j,i+1,n)
{
tmp=a[j][i]*iv%mod;
fo(k,i,n) a[j][k]=Dec(a[j][k],tmp*a[i][k]%mod);
}
}
return d;
}
static ll v[N],w[N];
inline bool ins(ll *v,int n,int id,ll *ans)
{
fo(i,1,n) w[i]=0;
w[id]=1;
ll tmp;
fd(i,n,1)
if(v[i])
{
if(!c[i][i])
{
fd(j,i,1) c[i][j]=v[j];
fo(j,1,n) d[i][j]=w[j];
return 0;
}
tmp=Pow(c[i][i],mod-2)*v[i]%mod;
fd(j,i,1) v[j]=Dec(v[j],c[i][j]*tmp%mod);
fo(j,1,n) w[j]=Dec(w[j],d[i][j]*tmp%mod);
}
fo(i,1,n) ans[i]=w[i];
return 1;
}
inline void get_G(const matrix &A,int n,ll *p)//解齐次线性方程非零解
{
fo(i,1,n) fo(j,1,n) a[i][j]=A.a[i][j];
memset(c,0,sizeof(c)); memset(d,0,sizeof(d));
fo(i,1,n)
{
fo(j,1,n) v[j]=a[j][i];
if(ins(v,n,i,p)) return;
}
}
inline matrix solve(const matrix &A,int n)//求所有代数余子式
{
int rank=r(A,n);
if(rank == n)
{
ll d=det(A,n);
B=inv(A,n);
fo(i,1,n) fo(j,1,n) C.a[i][j]=B.a[j][i]*d%mod;
return C;
}
else if(rank <= n-2)
{
C.clear();
return C;
}
else
{
static ll p[N],q[N];
get_G(A,n,q);
fo(i,1,n) fo(j,1,n) B.a[j][i]=A.a[i][j];
get_G(B,n,p);
int c=0,r=0;
fo(i,1,n) if(q[i]) {c=i; break;}
fo(i,1,n) if(p[i]) {r=i; break;}
fo(i,1,n)
if(i!=r)
fo(j,1,n)
if(j!=c)
B.a[i-(i>r)][j-(j>c)]=A.a[i][j];
ll d=det(B,n-1);
C.a[r][c]=((r+c)%2==1)?(mod-d)%mod:d;
ll iv=Pow(q[c]*p[r]%mod,mod-2);
fo(i,1,n)
fo(j,1,n)
C.a[i][j]=C.a[r][c]*iv%mod*p[i]%mod*q[j]%mod;
return C;
}
}
}

有向图两点间连通性问题(问删掉k个点后是否某两个点是否联通)

Yuhao Du Contest 11 Graph Problem:给定 $n$ 个点的图,每次问假设删掉 $k$ 个点 $p_1,p_2,\cdots,p_k$ 后, $w$ 对 $(i,j)$ 是否能从 $i$ 到 $j$。

对于邻接矩阵 $A$,能否从点 $i$ 到 $j$ 可以使用 $I+A+A^2+\cdots=(I-A)^{-1}$ 判断,由于不一定有逆,那么将 $A_{i,j}$ 随便设一个值,大概率下就有逆元了。更改 $k$ 行 $k$ 列对矩阵的影响不会太大,可以使用https://en.wikipedia.org/wiki/Woodbury_matrix_identity。

设 $M=(I-A)^{-1}$,$U_{i,j}=[i=p_j],V_{j,i}=A_{p_j,i}$。其中 $U$ 为 $n\times k$ 的矩阵,$V$ 为 $k\times n$ 的矩阵。

那么删掉这 $k$ 个点后的矩阵就是 $(I-A)+UV$。我们要判断这个矩阵的逆中某个位置是否相等,则有:

$(M^{-1}+UV)^{-1}=M-MU(I+VMU)^{-1}VM$。

化简后可得,只需判断 $M_{i,j}$ 与 $\sum_{1\le x,y\le k}M_{i,p_x}B_{x,y}^{-1}M_{y,j}$ 是否不相等即可。其中 $B_{i,j}=M_{p_i,p_j}$,为一个 $k\times 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
int n,m;
matrix A;
int main() {
read(n,m);
A.init(n);
fo(i,1,m) {
int x,y;
read(x,y);
A.a[x][y] = rnd() % (mod - 1) + 1;
}
fo(i,1,n) A.a[i][i] = 1;
matrix M = Mat::inv(A);
int q; read(q);
matrix B;
for(int cnt = 0;q--;) {
int k,w,x,y;
read(k);
vector<int> p(k+1);
fo(i,1,k) read(p[i]),p[i] = (p[i] + cnt - 1) % n + 1;
B.init(k);
fo(i,1,k)
fo(j,1,k)
B.a[i][j] = M.a[p[i]][p[j]];
B = Mat::inv(B);
read(w);
for(;w--;) {
read(x,y);
x = (x + cnt - 1) % n + 1;
y = (y + cnt - 1) % n + 1;
ll s = 0;
fo(i,1,k)
fo(j,1,k)
(s += M.a[x][p[i]] * B.a[i][j] % mod * M.a[p[j]][y]) %= mod;
int flag = s != M.a[x][y];
cnt += flag;
putchar(flag + '0');
}
putchar('\n');
}
return 0;
}

图论

KM算法

二分图带全匹配

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
const int N=505;
const int inf=1e9;
namespace Graph{
int n,w[N][N],l[N];
int lx[N],ly[N],slk[N],pre[N];
bool visy[N];
void bfs(int u) {
int v=0;
fo(i,0,n) pre[i]=visy[i]=0,slk[i]=inf;
l[v]=u;
for(int t,d,id;;) {
t=l[v]; d=inf; visy[v]=1;
fo(i,1,n)
if(!visy[i]) {
if(slk[i]>lx[t]+ly[i]-w[t][i])
slk[i]=lx[t]+ly[i]-w[t][i],pre[i]=v;
if(d>slk[i]) d=slk[i],id=i;
}
fo(i,0,n)
if(!visy[i]) slk[i]-=d;
else lx[l[i]]-=d,ly[i]+=d;
v=id;
if(l[v]==0) {
for(;v;l[v]=l[pre[v]],v=pre[v]);
return;
}
}
}
inline int KM() {
//calculate w
fo(i,1,n) lx[i]=ly[i]=0;
fo(i,1,n) fo(j,1,n) lx[i]=max(lx[i],w[i][j]);
fo(i,1,n) bfs(i);
int ans=0;
fo(i,1,n) ans+=w[l[i]][i];
return ans;
}
}

网络流

最小割

  • 最小割输出方案,只需要从 $S$ 出发在残量网络上dfs,枚举每条边,如果两个点分属不同集合,则需要割掉。

Dilworth定理

  • 偏序集中,最小链覆盖=最长反链,最小反链覆盖=最长链。
  • 覆盖指的是若干集合,集合的并等于全集,任意两个集合的交为空。
  • 最小链覆盖:把每个节点拆成入点和出点,对于原图中的两个点,连边 $(out_u,in_v)$,变成二分图最大匹配。
  • 最小可重链覆盖:求出传递闭包后,变成最小链覆盖。

最大权闭合子图

  • 闭合子图:对于一个图,图有点权。选择若干个点,若 $u$ 被选择,则所有 $(u,v)$ 的 $v$ 都要被选。
  • 最大权闭合子图:点权和最大的闭合子图。
  • 假设先全部选了正权点,那么如果存在一个负权点不选,但它的某个正权前驱选了,就不行。于是建立超级源汇 $S,T$,正权点连边 $(S,i,a_i)$,割掉表示不要这个正权点;负权点连边 $(i,T,-a_i)$,割掉表示选上这个负权点;原图边连 $(u,v,\infty)$。最后答案为所有正权点之和减去最小割。

混合图欧拉回路

  • 一个由单向和双向边组成的混合图,你需要给双向边定向,使得定向后存在欧拉回路。

  • 首先图要连通,不连通则一定不行。

  • 先对双向边随意定向,统计 $d_i$ 为入度-出度。若 $d_i$ 为奇数则一定无解。

  • 再弄一个超级源汇即可。

最大密度子图

每个边有边权,点有点权,要去选出一个子图,使得 $\frac{\sum_{i\in E}e_i}{\sum_{i\in V} v_i}$ 最大。

显然0/1分数规划,二分答案 $mid$,有:$\frac{\sum_{i\in E}e_i}{\sum_{i\in V} v_i}\geq mid$,即 $e-mid\times v\geq 0$。

一条边选了,则它的两个点都必须选。

十分像最大权闭合子图的形式!

对于原图的每条边看做一个点,做最大权闭合子图即可判断 $mid$ 是否合法。

上下界网络流

有上下界无源无汇可行流

对于一条边 $(u,v,l,r)$,我们强制让他流 $l$,剩下 $r-l$ 可以选择。设 $d_i$ 为 $i$ 点的所有入度-所有出度。若 $d_i>0$ ,则说明入度>出度,他要增加出度,因此连边 $(S,i,d_i)$;否则要增加入度,连边 $(i,T,d_i)$。

跑 $S\rightarrow T$ 的最大流,若 $S$ 所有出边(或 $T$ 所有入边)满流则有解。每条边的流量=下界+最大流中的流量。

有上下界有源有汇可行流

连边 $(t,s,\infty)$,变成有上下界无源无汇可行流。

有上下界有源有汇最大流

先求出可行流,然后去掉 $(t,s,\infty)$ 这条边,跑从 $s$ 到 $t$ 的最大流。最大流= $(t,s)$ 的流量+$s$ 到 $t$ 的最大流。

有上下界有源有汇最小流

先求出可行流,然后去掉 $(t,s,\infty)$ 这条边,跑从 $t$ 到 $s$ 的最大流。最小流= $(t,s)$ 的流量-$t$ 到 $s$ 的最大流。

消圈算法

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
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mxn=16384,inf=0x3f3f3f3f;
int N,M,S,T,dst,sum,flw,head[mxn],cur[mxn],dep[mxn],gap[mxn];
struct ed{int to,nxt,val,fee;}edge[mxn];
void addedge(int u,int v,int w,int f){
edge[++M]=(ed){v,head[u],w,f},head[u]=M;
edge[++M]=(ed){u,head[v],0,-f},head[v]=M;
}
int dfs(int u,int mx){
if (u==T) return mx;
int num=0;
for (int &i=cur[u],v;i;i=edge[i].nxt)
if (dep[v=edge[i].to]==dep[u]-1&&edge[i].val){
int f=dfs(v,min(mx-num,edge[i].val));
edge[i].val-=f,edge[i^1].val+=f,num+=f;
if (num==mx) return num;
}
if (!--gap[dep[u]++]) dep[S]=N+1;
++gap[dep[u]],cur[u]=head[u];
return num;
}
int MaxFlow_solve(){
gap[1]=N;
for (int i=1;i<=N;++i)
dep[i]=1,cur[i]=head[i];
int flw=0;
for (;dep[S]<=N;flw+=dfs(S,inf));
return flw;
}
int dis[mxn],pre[mxn];
int check(){
memset(dis,0,sizeof(dis));
for (int i=1;i<N;++i)
for (int j=2;j<=M;++j)
if (edge[j].val){
int u=edge[j^1].to,v=edge[j].to,w=edge[j].fee;
if (dis[v]>dis[u]+w) dis[v]=dis[u]+w,pre[v]=j;
}
for (int j=2;j<=M;++j)
if (edge[j].val){
int u=edge[j^1].to,v=edge[j].to,w=edge[j].fee;
if (dis[v]>dis[u]+w) return v;
}
return 0;
}
void upd(int i){
int fl=inf;
for (int j=i;;){
fl=min(fl,edge[j].val);
j=pre[edge[j^1].to];
if (j==i) break;
}
for (int j=i;;){
edge[j].val-=fl,edge[j^1].val+=fl;
j=pre[edge[j^1].to];
if (j==i) break;
}
}
int solve(){
for (int u=check();u;u=check()){
for (;dis[u]<=0;dis[u]=1,u=edge[pre[u]^1].to);
upd(pre[u]);
}
int sum=0;
for (int i=3;i<=M;i+=2)
sum-=edge[i].val*edge[i].fee;
return sum;
}
int main() {
MaxFlow_solve();
printf("%d", solve());
return 0;
}

Dinic

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

namespace Dinic{
const ll inf=1e18;
#define N 100000
#define M 10000000
int n,s,t,d[N]; queue<int> q;
int head[N],cur[N],ne[M],ver[M];
ll val[M];
int tot=1;
inline void init() {
fo(i,0,n) head[i]=0; tot=1; n=0;
}
inline void add(int x,int y,ll v) {
//cerr<<x<<' '<<y<<','<<v<<endl;
ver[++tot]=y; val[tot]=v; ne[tot]=head[x]; head[x]=tot;
ver[++tot]=x; val[tot]=0; ne[tot]=head[y]; head[y]=tot;
}
inline bool bfs() {
fo(i,0,n) cur[i]=head[i];
for(;!q.empty();q.pop());
fo(i,0,n) d[i]=-1; q.push(s); d[s]=0;
for(int u,v;!q.empty();) {
u=q.front(); q.pop();
for(int i=head[u];i;i=ne[i])
if(val[i]&&d[v=ver[i]]==-1) {
d[v]=d[u]+1,q.push(v);
if(v==t) return 1;
}
}
return 0;
}
ll dfs(int u,ll flow) {
if(!flow||u==t) return flow;
ll res=flow,r,v;
for(int &i=cur[u];i;i=ne[i])
if(val[i]&&d[v=ver[i]]==d[u]+1) {
r=dfs(v,min(res,val[i]));
if(!r) continue;
val[i]-=r; val[i^1]+=r;
res-=r; if(!res) break;
}
return flow-res;
}
ll dinic(ll flow=0) {
while(bfs()) flow+=dfs(s,inf);
return flow;
}
}

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
56
57
58
59
60
61
62
63
namespace Graph{
int n,m,s,t;
int ver[M],val[M],cost[M],ne[M],head[N],tot=1;
inline void add(int x,int y,int v,int c)
{
ver[++tot]=y; val[tot]=v; cost[tot]=c; ne[tot]=head[x]; head[x]=tot;
ver[++tot]=x; val[tot]=0; cost[tot]=-c;ne[tot]=head[y]; head[y]=tot;
}
int h[N],dis[N];
bool vis[N];
void spfa(int s,int t)
{
for(int i=1;i<=n;i++) h[i]=inf;
queue<int> q;
for(h[s]=0,q.push(s);!q.empty();)
{
int u=q.front(); q.pop();
for(int i=head[u],v;i;i=ne[i])
if(val[i]&&h[v=ver[i]]>h[u]+cost[i])
{
h[v]=h[u]+cost[i];
if(!vis[v]) q.push(v),vis[v]=1;
}
vis[u]=0;
}
}
struct node{
int u,dis;
friend inline bool operator<(const node &A,const node &B){return A.dis>B.dis;}
};
int pv[N],pe[N];
void MCMF(int s,int t)
{
int flow=0,co=0;
priority_queue<node> q;
for(;;)
{
for(int i=1;i<=n;i++) dis[i]=inf;
for(dis[s]=0,q.push((node){s,dis[s]});!q.empty();)
{
node now=q.top(); q.pop();
int u=now.u;
if(dis[u]<now.dis) continue;
for(int i=head[u],v;i;i=ne[i])
if(val[i])
{
v=ver[i];
if(dis[v]+h[v]>dis[u]+h[u]+cost[i])
dis[v]=dis[u]+h[u]+cost[i]-h[v],
q.push((node){v,dis[v]}),
pv[v]=u,pe[v]=i;
}
}
if(dis[t]==inf) break;
for(int i=1;i<=n;i++) h[i]+=dis[i];
int tmp=inf;
for(int u=t;u!=s;u=pv[u]) tmp=min(tmp,val[pe[u]]);
flow+=tmp; co+=h[t]*tmp;
for(int u=t,i;u!=s;u=pv[u]) i=pe[u],val[i]-=tmp,val[i^1]+=tmp;
}
printf("%d %d\n",flow,co);
}
}

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
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
const int N=2.6e5;
const int M=N*3;
const int K=2e7;
struct Edge{
int head[N],ne[M],ver[M],tot;
ll val[M];
Edge(){tot=0;}
void add(int x,int y,int z) {
ver[++tot]=y; val[tot]=z; ne[tot]=head[x]; head[x]=tot;
}
}E1,E2;
struct node{
int u;ll d;
node* operator=(node a){u=a.u,d=a.d; return this;}
friend inline bool operator<(node A,node B) {
return A.d>B.d;
}
};
int rt[N];
struct Heap{
int cnt,lc[K],rc[K],d[K];
node v[K];
Heap(){d[0]=-1;}
int newnode(node w) {
v[++cnt]=w;
return cnt;
}
int merge(int x,int y) {
if(!x||!y) return x+y;
if(v[x]<v[y]) swap(x,y);
int u=++cnt;
lc[u]=lc[x];
v[u]=v[x];
rc[u]=merge(rc[x],y);
if(d[lc[u]]<d[rc[u]]) swap(lc[u],rc[u]);
d[u]=d[rc[u]]+1;
return u;
}
}st;
int n,m;
priority_queue<node> q;
bool bo[N],vis[N],on_tree[M];
ll dis[N];
int fa[N];
void dfs(int u) {
bo[u]=1;
for(int i=E2.head[u],v;i;i=E2.ne[i]) {
v=E2.ver[i];
if(bo[v]) continue;
if(dis[v]==dis[u]+E2.val[i])
fa[v]=u,on_tree[i]=1,dfs(v);
}
}
void dijkstra(int s) {
memset(dis,0x3f,sizeof(dis));
q.push({s,0}); dis[s]=0;
for(;!q.empty();) {
auto a=q.top(); q.pop();
if(vis[a.u]) continue;
vis[a.u]=1;
for(int i=E2.head[a.u],v;i;i=E2.ne[i]) {
v=E2.ver[i];
if(dis[v]>dis[a.u]+E2.val[i]) {
dis[v]=dis[a.u]+E2.val[i];
q.push({v,dis[v]});
}
}
}
}
void dfs2(int u) {
bo[u]=1;
if(fa[u]) rt[u]=st.merge(rt[u],rt[fa[u]]);
for(int i=E2.head[u],v;i;i=E2.ne[i]) {
v=E2.ver[i];
if(!bo[v]&&fa[v]==u) dfs2(v);
}
}
inline void add(int x,int y,int z) {
E1.add(x,y,z); E2.add(y,x,z);
}
int a[N];
int s,t;
int main() {
n=read(); m=read();
int x,y,z;
fo(i,1,m) {
x=read(); y=read(); z=read(); add(x,y,z);
}
s=1; t=n;
dijkstra(t);
dfs(t);
printf("%lld\n",dis[s]);
k--;
if(!k) return 0;
fo(u,s,t)
if(vis[u])
for(int i=E1.head[u],v;i;i=E1.ne[i])
if(!on_tree[i]&&vis[v=E1.ver[i]])
rt[u]=st.merge(rt[u],st.newnode({v,dis[v]-dis[u]+E1.val[i]}));
fo(i,s,t) bo[i]=0;
dfs2(t);
if(rt[s]) q.push((node){rt[s],dis[s]+st.v[rt[s]].d});
for(;!q.empty();) {
auto now=q.top(); q.pop();
k--; printf("%lld\n",now.d);
if(!k) return 0;
if(st.lc[now.u]) q.push((node){st.lc[now.u],now.d-st.v[now.u].d+st.v[st.lc[now.u]].d});
if(st.rc[now.u]) q.push((node){st.rc[now.u],now.d-st.v[now.u].d+st.v[st.rc[now.u]].d});
int u=rt[st.v[now.u].u];
if(u) q.push((node){u,now.d+st.v[u].d});
}
fo(i,1,k) printf("-1\n");
return 0;
}

环计数,K4计数

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

void build(int n,int m) {
for(int i=1;i<=n;i++)
d[i]=0,
adj[i].clear(),vec[i].clear();
for(int i=1;i<=m;i++) {
int x,y;
read(x); read(y);
d[x]++; d[y]++;
adj[x].pb(y); adj[y].pb(x);
}
for(int i=1;i<=n;i++)
for(int j:adj[i])
if(d[i] > d[j] || (d[i] == d[j] && i > j))
vec[i].pb(j);
}

inline int Count3() {
int ans = 0;
for(int u=1;u<=n;u++) {
for(int v:vec[u])
tmp[v] = 1;
for(int v:vec[u])
for(int w:vec[v])
if(tmp[w])
ans++;
for(int v:vec[u])
tmp[v] = 0;
}
return ans;
}
inline ll Count4() {
ll ans = 0;
for(int u=1;u<=n;u++) {
for(int v:vec[u])
for(int w:adj[v])
if(d[u] > d[w] || (d[u] == d[w] && u > w)) {
ans += tmp[w];
tmp[w] ++;
}
for(int v:vec[u])
for(int w:adj[v])
tmp[w] = 0;
}
return ans;
}

// bitset<sqrt(2m)> b[sqrt(2m)];
// 时间复杂度 O(m^1.5+m^2/64)
inline ll CountK4() {
ll ans = 0;
fo(i,1,n) id[i] = i;
sort(id+1,id+n+1,[&](const int &x,const int &y) {
if(deg[x] != deg[y])
return deg[x] < deg[y];
return x < y;
});
fo(i,1,n) rk[id[i]] = i;
fo(i,1,n)
for(int j:adj[i])
if(rk[i] < rk[j])
vec[i].pb(j);
ll ans = 0;
memset(num,-1,sizeof(num));
fo(i,1,n) {
int u = id[i], tim = 0;
for(int v:vec[u])
num[v] = tim++;
assert(tim <= K);
ff(j,0,tim)
b[j].reset();
for(int v:vec[u])
for(int w:vec[v])
if(num[w] != -1)
b[num[v]][num[w]] = 1;
for(int v:vec[u])
for(int w:vec[v])
if(num[w] != -1) {
a = b[num[v]] & b[num[w]];
ans += a.count();
}
for(int v:vec[u])
num[v] = -1;
}
return ans;
}

弦图

$N(x)$,与 $x$ 直接相连的点集。

单纯点:${x}+N(x)$ 的诱导子图为一个团。

完美消除序列:$v_1,v_2,\cdots,v_n$,其中 $v_i$ 在 ${v_i,v_{i+1},\cdots,v_n}$ 中为单纯点。

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
//v即为完美消除序列,pos即为点i在完美消除序列上的位置
//判断v是否为完美消除序列,根据定义即可 O(n+m) 判断。
//极大团可通过不存在nxt_y=x且|N(x)|+1<=|N(y)|判断
//完美消除序列从后往前染色=色数=最大团点数
//完美消除序列从后往前选能选的点=最大独立集=最小团覆盖
//最大势算法求完美消除序列:
int id[N];
vector<int> adj[N];
inline void addedge(int x,int y){adj[x].pb(y); adj[y].pb(x);}
int head[N],ne[N],ver[N],tot=1;
int best,label[N];
bool bo[N];
int pos[N],v[N];
inline void add(int i,int x) {
ver[++tot]=x; ne[tot]=head[i]; head[i]=tot;
}
inline void del(int i) {
head[i]=ne[head[i]];
for(;!head[best];best--);
}
inline ll MCS() {
best=0; fo(i,1,n) add(0,i),label[i]=0;
fd(i,n,1) {
int x=ver[head[best]];
while(bo[x]) del(best),x=ver[head[best]];
bo[x]=1;
pos[x]=i; v[i]=x;
for(auto y:adj[x]) if(!bo[y]) add(++label[y],y),best=max(best,label[y]);
}
ll ans=1;
fd(i,n,1) {
int x=v[i],num=c;
for(auto y:adj[x]) if(pos[y]>pos[x]) num--;
ans=ans*num%1000000007ll;
}
return ans;
}

最小树形图(朱-刘算法)

有向图,以 $rt$ 为根的最小生成树。$O(m \log n)$。

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
int find(int x);
struct node{
int val,v,id;
friend inline bool operator>(const node &A,const node &B)
{return A.val>B.val;}
};
struct Heap{
node v[N];
int dis[N],ls[N],rs[N],tag[N];
bool del[N];
int rt[N];
inline void pushtag(int u,int t){if(u) tag[u]+=t,v[u].val+=t;}
inline void pushdown(int u) {
if(!tag[u]) return;
pushtag(ls[u],tag[u]);
pushtag(rs[u],tag[u]);
tag[u]=0;
}
int merge(int x,int y) {
if(!x||!y) return x+y;
if(v[x].val>v[y].val) swap(x,y);
pushtag(y,-tag[x]);
rs[x]=merge(rs[x],y);
if(dis[ls[x]]<dis[rs[x]]) swap(ls[x],rs[x]);
dis[x]=dis[rs[x]]+1;
return x;
}
inline int pop(int x) {
pushdown(x); return merge(ls[x],rs[x]);
}
inline void ins(int y,int va,int id,int to) {
v[id]=(node){va,to,id};
rt[y]=merge(rt[y],id);
}
inline void dec(int id,int v) {pushtag(id,-v);}
inline node top(int u) {
for(;rt[u]&&find(v[rt[u]].v)==u;) rt[u]=pop(rt[u]);
if(!rt[u]) {printf("-1"); exit(0);}
v[rt[u]].v=find(v[rt[u]].v);
return v[rt[u]];
}
};
Heap h;
int fa[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void Union(int u,int v) {
u=find(u); v=find(v);
if(u==v) return;
h.rt[v]=h.merge(h.rt[u],h.rt[v]),fa[u]=v;
}
int n,m,rt,pre[N],bel[N];
ll ans;
int main() {
n=read(); m=read(); rt=read();
int x,y,z;
fo(i,1,m) {
x=read(),y=read(),z=read();
h.ins(y,z,i,x);
}
int cnt=n;
node now;
bel[rt]=rt;
fo(i,1,n<<1) fa[i]=i;
fo(i,1,n) {
int j=i;
for(;!bel[j];) {
while(!bel[j]) bel[j]=i,now=h.top(j),ans+=now.val,j=now.v;
if(bel[j]!=i) break;
for(;bel[j]!=-1;) bel[j]=-1,j=pre[j]=(now=h.top(j)).v,h.dec(now.id,now.val);
++cnt;
for(;bel[j]!=i;) bel[j]=i,Union(j,cnt),j=pre[j];
j=cnt;
}
}
printf("%lld",ans);
return 0;
}

带花树 $O(n^3)$

一般图匹配,不带权。

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
namespace Graph{
int n,tim;
queue<int> q;
vector<int> adj[N];
int fa[N],t[N],vst[N],pre[N],matc[N];
inline void add(int x,int y) {
adj[x].pb(y); adj[y].pb(x);
}
int LCA(int x,int y) {
tim++; x=fa[x]; y=fa[y];
for(;vst[x]!=tim;) {
if(x) {
vst[x]=tim; x=fa[pre[matc[x]]];
}
swap(x,y);
}
return x;
}
void blossom(int x,int y,int lca) {
for(;fa[x]!=lca;) {
pre[x]=y; y=matc[x];
if(t[y]==1) {
t[y]=0; q.push(y);
}
fa[x]=fa[y]=fa[lca]; x=pre[y];
}
}
int Augument(int s) {
fo(i,1,n) fa[i]=i;
fo(i,0,n) t[i]=-1;
q=queue<int>();
t[s]=0; q.push(s);
for(;!q.empty();) {
int u=q.front(); q.pop();
for(int v:adj[u])
if(t[v]==-1) {
pre[v]=u; t[v]=1;
if(!matc[v]) {
for(int to=v,from=u;to;from=pre[to]) {
matc[to]=from;
swap(matc[from],to);
}
return 1;
}
t[matc[v]]=0;
q.push(matc[v]);
}
else if(t[v]==0&&fa[u]!=fa[v]) {
int lca=LCA(u,v);
blossom(u,v,lca);
blossom(v,u,lca);
}
}
return 0;
}
int solve(int _n,int m,int *x,int *y,vector<pair<int,int> > &vec) {
int ans=0;
n=_n;
fo(i,1,m) add(x[i],y[i]);
fd(i,n,1) if(!matc[i]) ans+=Augument(i);
vec.clear();
fo(i,1,n) if(matc[i]&&i<matc[i]) vec.pb(make_pair(i,matc[i]));
fo(i,1,n) matc[i]=t[i]=fa[i]=pre[i]=vst[i]=0,adj[i].clear();
for(;!q.empty();q.pop());
return ans;
}
}

SCC

求强连通分量

1
2
3
4
5
6
7
8
9
10
11
12
13
void Tarjan(int u) {
low[u]=dfn[u]=++tim; s.push(u); vis[u]=true;
int len=adj[u].size(),v;
for(int i=0;i<len;i++)
if(!dfn[v=adj[u][i]]) Tarjan(v),low[u]=min(low[u],low[v]);
else if(vis[v]) low[u] = min(low[u],dfn[v]);
if(dfn[u]!=low[u]) return;
SCC_num++;
do {
v = s.top(); s.pop(); vis[v] = false;
SCC[SCC_num].push_back(v);
}while(v!=u);
}

BCC

求点双、边双、圆方树。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void dfs(int u) {
dfn[u]=low[u]=++tim; st[++top]=u;
for(int i=head[u],v,x;i;i=ne[i])
if(!dfn[v=ver[i]]) {
dfs(v); low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u])/*if(low[v]>dfn[u])*/
{
Tree::val[++n]=1; x=0;
do {
x=st[top--]; Tree::add(x,n); Tree::val[n]++;
}while(x!=v);
Tree::add(u,n);
}
}
else /*if(v!=pre)*/ low[u]=min(low[u],dfn[v]);
}

树论

树哈希

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//必须想一种特殊的哈希函数乱搞。
inline ll F(ll x) {
return x * x * x * 1237123 + 19280617;
}
ll hash_f(ll x) {
ll cur = F(x & ((1 << 31) - 1)) + F(x >> 31);
return cur % mod + mod;
}
void dfs(int u,int pre) {
g[u] = 1;
for(int v:adj[u])
if(v != pre && !vis[v]) {
dfs(v,u);
g[u] = (g[u] + hash_f(g[v])) % mod;
}
}

虚树

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
//虚树清空必须使用dfs清空所有东西,或者把虚树上所有节点记录下来。
vector<int> _adj[N];
void _add(int x,int y) {
_adj[x].pb(y); _adj[y].pb(x);
}
int rt;
int st[N],top;
void build(vector<int> &a) {
rt=a[0];
for(auto v:a) rt=lca(rt,v);
a.pb(rt);
sort(all(a),[&](const int &x,const int &y){return dfn[x]<dfn[y];});
a.resize(unique(all(a))-a.begin());
top=0;
int y;
for(auto x:a) {
if(top) {
y=lca(x,st[top]);
if(y!=st[top]) {
for(;top>=2&&dep[st[top-1]]>=dep[y];top--) _add(st[top],st[top-1]);
if(st[top]!=y) _add(st[top],y),st[top]=y;
}
}
if(st[top]!=x) st[++top]=x;
}
if(top) for(;--top;) _add(st[top],st[top+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
bool vis[N];
int mx[N],siz[N],Cnt,rt;
void calc_siz(int u,int pre) {
siz[u] = 1; mx[u] = 0;
for(auto [v,z,i]:adj[u])
if(v != pre && !vis[v]) {
calc_siz(v,u);
siz[u] += siz[v];
mx[u] = max(mx[u],siz[v]);
}
mx[u] = max(mx[u],Cnt - siz[u]);
if(mx[u] < mx[rt])
rt = u;
}
int fa[N];
void divide(int u,int si,int pre_root) {
Cnt = si;
rt = 0;
calc_siz(u,0);
vis[rt] = 1;
fa[rt] = pre_root;
vector<pair<int,int>> vec;
for(auto [v,z,i]:adj[rt])
if(!vis[v])
vec.pb({v,siz[v]>siz[rt]?(si-siz[rt]):siz[v]});
int now_root = rt;
for(auto [v,ne_si]:vec)
divide(v,ne_si,now_root);
}
mx[0] = inf;

边分治

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
//注意,边分治重构后,点数变成原来的两倍。
//最好使用链式前向星存新的边,因为需要记录某条边是否被访问过。
int n,m;
vector<pair<int,int>> adj[N];
int ver[N<<1],head[N],val[N<<1],ne[N<<1],tot = 1;
void add_tree(int x,int y,int z) {
ver[++tot] = y; ne[tot] = head[x]; val[tot] = z; head[x] = tot;
ver[++tot] = x; ne[tot] = head[y]; val[tot] = z; head[y] = tot;
}
int siz[N];
void build(int u,int pre) {
vector<pair<int,int>> vec;
for(auto [v,z]:adj[u])
if(v != pre) {
build(v,u);
vec.push_back({v,z});
}
if(vec.size() == 1)
add_tree(u,vec[0].fi,vec[0].se);
else {
int las = u;
for(auto [v,z]:vec) {
++ m;
add_tree(las,m,0);
add_tree(m,v,z);
las = m;
}
}
}
bool vis[N<<1];
int u1,u2,id,mi,now_cnt;
void find_root(int u,int pre) {
siz[u] = 1;
for(int i=head[u],v;i;i=ne[i])
if((v = ver[i]) != pre && !vis[i]) {
find_root(v,u);
siz[u] += siz[v];
if(max(siz[v], now_cnt - siz[v]) < mi) {
mi = max(siz[v], now_cnt - siz[v]);
u1 = v; u2 = u; id = i;
}
}
}
vector<pair<ll,int>> v1,v2; // 统计信息
void dfs(int u,int pre,ll x,vector<pair<ll,int>> &A) {
A.push_back({x,u});
for(int i=head[u],v;i;i=ne[i])
if((v = ver[i]) != pre && !vis[i])
dfs(v,u,x + val[i],A);
}
void calc(vector<pair<ll,int>> &v1,vector<pair<ll,int>> &v2,ll t) {
// do something
}
void divide(int u,int cnt) {
if(cnt <= 1)
return;
now_cnt = cnt;
u1 = u2 = 0; mi = inf;
find_root(u,0);
int s1 = siz[u1], s2 = cnt - siz[u1], u3 = u2;
vis[id] = vis[id ^ 1] = 1;
v1.clear(); v2.clear();
dfs(u1,u2,0,v1); dfs(u2,u1,0,v2);
calc(v1,v2,val[id]);
divide(u1,s1); divide(u3,s2);
}
int main() {
read(n);
fo(i,2,n) {
int x,y,z;
read(x,y,z);
adj[x].push_back({y,z});
adj[y].push_back({x,z});
}
m = n; tot = 1;
build(1,0);
divide(1,m);
}

计算几何

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
const db eps=1e-8;
const db pi=acos(-1);
const db inf=1e20;
int sgn(db x) {
if(fabs(x)<eps) return 0;
if(x<0) return -1;
else return 1;
}
inline db sqr(db x){return x*x;}

struct Point{
db x,y;
Point() {x=y=0;}
Point(db _x,db _y) {x=_x,y=_y;}
void input(){scanf("%lf%lf",&x,&y);}
friend inline bool operator == (Point A,Point B){return sgn(A.x-B.x)==0&&sgn(A.y-B.y)==0;}
friend inline bool operator < (Point A,Point B){return sgn(A.x-B.x)==0?sgn(A.y-B.y)<0:A.x<B.x;}
friend inline Point operator - (Point A,Point B){return Point(A.x-B.x,A.y-B.y);}
friend inline Point operator + (Point A,Point B){return Point(A.x+B.x,A.y+B.y);}
friend inline Point operator * (Point A,db k){return Point(A.x*k,A.y*k);}
friend inline Point operator / (Point A,db k){return Point(A.x/k,A.y/k);}
friend inline db operator ^(Point A,Point B){return A.x*B.y-A.y*B.x;}
friend inline db operator *(Point A,Point B){return A.x*B.x+A.y*B.y;}
inline db len2() {return x*x+y*y;}
inline db len() {return sqrt(len2());}
inline db angle() {return atan2(y,x);}
inline db rad(Point A,Point B) {
Point P=*this;
return fabs(atan2((A-P)^(B-P),(A-P)*(B-P)));
}
inline Point trunc(db r) {
db l=len(); if(!sgn(l)) return *this;
r/=l; return Point(x*r,y*r);
}
inline Point rotate_left() {return Point(-y,x);}
inline Point rotate_right() {return Point(y,-x);}
//anti-clockwise
inline Point rotate(Point P,db ang) {
Point v=(*this)-P;
db c=cos(ang),s=sin(ang);
return Point(P.x+v.x*c-v.y*s,P.y+v.x*s+v.y*c);
}
inline Point rotate(db ang) {return rotate(Point(0,0),ang);}
};
inline db area(Point A,Point B,Point C){return fabs((A-B)^(C-B))/2;}
struct Line{
Point s,e;
Line() {}
Line(Point _s,Point _e) {s=_s; e=_e;}
void input(){s.input();e.input();}
void adjust(){if(e<s) swap(e,s);}
friend inline bool operator==(Line A,Line B){return A.s==B.s&&A.e==B.e;}
Line(Point p,db ang) {
s=p;
e=p+(sgn(ang-pi/2)==0?Point(0,1):Point(1,tan(ang)));
}
Line(db a,db b,db c) { //ax+by+c==0
if(sgn(a)==0) s=Point(0,-c/b),e=Point(1,-c/b);
else if(sgn(b)==0) s=Point(-c/a,0),e=Point(-c/a,1);
else s=Point(0,-c/b),e=Point(1,(-c-a)/b);
}
inline db len(){return (e-s).len();}
inline db angle2(){return atan2(e.y-s.y,e.x-s.x);}
inline db angle() { //angle in [0,pi)
db k=angle2();
if(sgn(k)<0) k+=pi;
if(sgn(k-pi)==0) k-=pi;
return k;
}
int relation(Point p) { //1:p on line's left;3:p on line
int c=sgn((p-s)^(e-s));
return !c?3:1+(c>0);
}
bool PointOnSegment(Point p){return sgn((p-s)^(e-s))==0&&sgn((p-s)*(p-e))<=0;}
bool parallel(Line v){return sgn((e-s)^(v.e-v.s))==0;}
// 2 规范相交;1 非规范相交;0 不相交
int segcrossseg(Line v) {
int d1 = sgn((e-s)^(v.s-s));
int d2 = sgn((e-s)^(v.e-s));
int d3 = sgn((v.e-v.s)^(s-v.s));
int d4 = sgn((v.e-v.s)^(e-v.s));
if( (d1^d2)==-2 && (d3^d4)==-2 )return 2;
return (d1==0 && sgn((v.s-s)*(v.s-e))<=0) ||
(d2==0 && sgn((v.e-s)*(v.e-e))<=0) ||
(d3==0 && sgn((s-v.s)*(s-v.e))<=0) ||
(d4==0 && sgn((e-v.s)*(e-v.e))<=0);
}
int linecrossseg(Line v) { //*this is line
int d1 = sgn((e-s)^(v.s-s));
int d2 = sgn((e-s)^(v.e-s));
if((d1^d2)==-2) return 2;
return (d1==0||d2==0);
}
int linecrossline(Line v) { //0 平行;1 重合;2 相交
if((*this).parallel(v)) return v.relation(s)==3;
return 2;
}
Point Intersection(Line v) {
db a1=(v.e-v.s)^(s-v.s);
db a2=(v.e-v.s)^(e-v.s);
return Point((s.x*a2-e.x*a1)/(a2-a1),(s.y*a2-e.y*a1)/(a2-a1));
}
db dispointtoline(Point p) {return fabs((p-s)^(e-s))/len();}//点到直线的距离
db dispointtoseg(Point p) {
if(sgn((p-s)*(e-s))<0 || sgn((p-e)*(s-e))<0)
return min((p-s).len(),(p-e).len());
return dispointtoline(p);
}
//`返回线段到线段的距离`
//`前提是两线段不相交,相交距离就是0了`
db dissegtoseg(Line v){
return min(min(dispointtoseg(v.s),dispointtoseg(v.e)),min(v.dispointtoseg(s),v.dispointtoseg(e)));
}
//点p在直线上的投影
Point lineprog(Point p) {
return s + ( ((e-s)*((e-s)*(p-s)))/((e-s).len2()) );
}
//点p关于直线的对称点
Point symmetrypoint(Point p) {
Point q = lineprog(p);
return Point(2*q.x-p.x,2*q.y-p.y);
}
};
struct Circle{
Point p; db r;
Circle(){}
Circle(Point p,db r){this->p=p; this->r=r;}
Circle(db x,db y,db r){p=Point(x,y); this->r=r;}
Circle(Point a,Point b,Point c,bool opt) {//0 外接圆;1 内切圆
Line u,v;
if(opt==0) {
u=Line((a+b)/2,((a+b)/2)+((b-a).rotate_left()));
v=Line((b+c)/2,((b+c)/2)+((c-b).rotate_left()));
p=u.Intersection(v);
r=(p-a).len();
}
else {
db m = atan2(b.y-a.y,b.x-a.x), n = atan2(c.y-a.y,c.x-a.x);
u.s = a;
u.e = u.s + Point(cos((n+m)/2),sin((n+m)/2));
v.s = b;
m = atan2(a.y-b.y,a.x-b.x) , n = atan2(c.y-b.y,c.x-b.x);
v.e = v.s + Point(cos((n+m)/2),sin((n+m)/2));
p = u.Intersection(v);
r = Line(a,b).dispointtoseg(p);
}
}
friend inline bool operator == (Circle A,Circle B) {return A.p==B.p&&sgn(A.r-B.r)==0;}
db area(){return pi*r*r;}
db circumference(){return 2*pi*r;}
int relation(Point b) { //0 圆外;1 圆上;2 圆内
int opt=sgn((b-p).len()-r);
return opt<0?2:(opt==0);
}
int relationseg(Line v) {
int opt=sgn(v.dispointtoseg(p)-r);
return opt<0?2:(opt==0);
}
int relationline(Line v) {
int opt=sgn(v.dispointtoline(p)-r);
return opt<0?2:(opt==0);
}
// 5 相离
// 4 外切
// 3 相交
// 2 内切
// 1 内含
int relationcircle(Circle A) {
db d=(p-A.p).len();
if(sgn(d-r-A.r)>0) return 5;
if(sgn(d-r-A.r)==0) return 4;
return 2+sgn(d-fabs(r-A.r));
}
vector<Point> pointcrossline(Line v) {
vector<Point> vec; vec.clear();
if(!(*this).relationline(v)) return vec;
Point a=v.lineprog(p);
db d=v.dispointtoline(p);
d=sqrt(r*r-d*d);
if(sgn(d)==0) vec.pb(a);
else vec.pb(a+(v.e-v.s).trunc(d)),vec.pb(a-(v.e-v.s).trunc(d));
return vec;
}
vector<Point> pointcrosscircle(Circle A) {
vector<Point> vec; vec.clear();
int t=relationcircle(A);
if(t==5||t==1) return vec;
db d=(p-A.p).len();
db l=(d*d+r*r-A.r*A.r)/(2.*d);
db h=sqrt(r*r-l*l);
Point q=p+(A.p-p).trunc(l);
if(t==2||t==4) {
vec.pb(q); return vec;
}
vec.pb(q+((A.p-p).rotate_left()).trunc(h));
vec.pb(q+((A.p-p).rotate_right()).trunc(h));
return vec;
}
};
//最小圆覆盖
inline Circle smallestcircle(vector<Point> p) {
int n = p.size();
random_shuffle(all(p));
Circle C=Circle(p[0],0.0);
for(int i=1;i<n;i++)
if(C.relation(p[i])==0) {
C=Circle(p[i],0.0);
for(int j=0;j<i;j++)
if(C.relation(p[j])==0) {
C=Circle((p[i]+p[j])/2,(p[i]-p[j]).len()/2);
for(int k=0;k<j;k++)
if(C.relation(p[k])==0)
C=Circle(p[i],p[j],p[k],0);
}
}
return C;
}

struct Polygon {
int n;
vector<Point> p;
vector<Line> l;
Polygon() {
n = 0; p.clear(); l.clear();
}
Polygon(vector<Point> a) {
n=a.size(); p=a; l.resize(n);
for(int i=0;i<n;i++) l[i]=Line(p[i],p[(i+1)%n]);
}
db area() {
db ans=0;
for(int i=2;i<n;i++) ans+=(p[i]-p[0])^(p[i-1]-p[0]);
return fabs(ans)/2;
}
};

Polygon ConvexHull(vector<Point> a) {
// 严格的凸包(即不存在三点共线),若非严格则将 <= 改成 <
int n = a.size(), m = -1;
vector<Point> p(n*2);
sort(all(a));
for(int i=0;i<n;i++) {
for(;m > 0 && ((p[m] - p[m-1]) ^ (a[i] - p[m-1])) <= 0; m--);
p[++m] = a[i];
}
int k = m;
for(int i=n-2;i>=0;i--) {
for(;m > k && ((p[m] - p[m-1]) ^ (a[i] - p[m-1])) <= 0; m--);
p[++m] = a[i];
}
p.resize(m);
return Polygon(p);
}
Polygon ConvexHull(Polygon A) {
return ConvexHull(A.p);
}
Polygon HalfPlanes(vector<Line> l) {
vector<Point> p;
int n=l.size();
auto cmp = [](Line A,Line B) {
db r=A.angle2()-B.angle2();
if(sgn(r)!=0) return sgn(r)<0;
return sgn((A.e-A.s)^(B.e-A.s))<0;
};
sort(all(l),cmp);
vector<Line> q(n+2);
vector<Point> b(n+2);
int head=0,tail=0;
q[0]=l[0];
for(int i=1;i<n;i++)
if(sgn(l[i].angle2()-l[i-1].angle2())!=0) {
if(head<tail&&q[head].parallel(q[head+1])) return Polygon(p);
if(head<tail&&q[tail].parallel(q[tail-1])) return Polygon(p);
while(head<tail&&l[i].relation(b[tail-1])==2) tail--;
while(head<tail&&l[i].relation(b[head])==2) head++;
q[++tail]=l[i];
if(head<tail) b[tail-1]=q[tail].Intersection(q[tail-1]);
}
while(head<tail&&l[head].relation(b[tail-1])==2) tail--;
while(head<tail&&l[tail].relation(b[head])==2) head++;
if(tail-head<=1) return Polygon(p);
b[tail]=q[head].Intersection(q[tail]);
p.resize(tail-head+1);
for(int i=head;i<=tail;i++) p[i-head]=b[i];
return Polygon(p);
}
Polygon Minkowski(Polygon A, Polygon B) {
// 闵可夫斯基和
A = ConvexHull(A);
B = ConvexHull(B);
vector<Point> c(A.n + B.n + 1), vA(A.n), vB(B.n);
for (int i = 0; i < A.n; i ++)
vA[i] = A.p[(i+1)%A.n] - A.p[i];
for (int i = 0; i < B.n; i ++)
vB[i] = B.p[(i+1)%B.n] - B.p[i];
int i = 0, j = 0, k = 1;
c[0] = A.p[0] + B.p[0];
for (;i < A.n || j < B.n; k ++) {
if ( j == B.n || (i < A.n && (vA[i]^vB[j]) >= 0))
c[k] = c[k-1] + vA[i ++];
else
c[k] = c[k-1] + vB[j ++];
}
c.pop_back();
return Polygon(c);
}

double TriangleCircleIntersection(Point A, Point B, Circle C) {
// 三角形(其中一个点为圆心)与圆求交
Point OA = A - C.p, OB = B - C.p;
Point BA = A - B, BC = C.p - B;
Point AB = B - A, AC = C.p - A;
db dOA = OA.len(), dOB = OB.len(), dAB = AB.len();
if (sgn(OA^OB) == 0)
return 0;
if (sgn(dOA - C.r) < 0 && sgn(dOB - C.r) < 0)
return (OA ^ OB) * 0.5;
else if (dOB < C.r && dOA >= C.r) {
db x = ((BA * BC) + sqrt(sqr(C.r) * sqr(dAB) - sqr(BA^BC))) / dAB;
db ts = (OA^OB) * 0.5;
return asin(ts * (1-x/dAB)*2/C.r/dOA) * sqr(C.r) * 0.5 + ts * x / dAB;
}
else if (dOB >= C.r && dOA < C.r) {
db y = ((AB * AC) + sqrt(sqr(C.r) * sqr(dAB) - sqr(AB^AC))) / dAB;
db ts = (OA^OB) * 0.5;
return asin(ts * (1-y/dAB)*2/C.r/dOB) * sqr(C.r) * 0.5 + ts * y / dAB;
}
else if (fabs(OA ^ OB) >= C.r * dAB || (AB*AC) <= 0 || (BA*BC) <= 0) {
if ((OA*OB) >= 0)
return asin((OA^OB)/dOA/dOB) * sqr(C.r) * 0.5;
else {
if ((OA^OB) < 0)
return (-pi - asin((OA^OB)/dOA/dOB)) * sqr(C.r) * 0.5;
else
return ( pi - asin((OA^OB)/dOA/dOB)) * sqr(C.r) * 0.5;
}
}
else {
db x=((BA*BC) + sqrt(sqr(C.r) * sqr(dAB) - sqr(BA^BC))) / dAB;
db y=((AB*AC) + sqrt(sqr(C.r) * sqr(dAB) - sqr(AB^AC))) / dAB;
db ts = (OA^OB) * 0.5;
return (asin(ts*(1-x/dAB)*2/C.r/dOA)+asin(ts*(1-y/dAB)*2/C.r/dOB))*sqr(C.r)*0.5 + ts*((x+y)/dAB-1);
}
}

double PolygonCircleIntersection(Polygon A, Circle C) {
// 简单多边形与圆求交
double ans = 0;
for (auto [s, e] : A.l)
ans += TriangleCircleIntersection(s, e, C);
return fabs(ans);
}