XCPC模板
XCPC算法竞赛模板 by MorphLing
图论
单源最短路径(dijkstra)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+5;
const LL INF=0x3f3f3f3f3f3f3f3f;
struct Dijkstra {
struct Edge {
int to;LL val;
};
struct Node {
LL dis;int key;
friend bool operator < (const Node &x,const Node &y) {
return x.dis>y.dis;
}
};
bool vis[N];
int n,m,s;
LL dis[N];
vector<Edge>G[N];
priority_queue<Node>Q;
void init(int _n,int _s) {
n=_n,s=_s;
for (int i=0;i<=n;i++) G[i].clear();
for (int i=1;i<=n;i++) dis[i]=INF;
}
void addEdge(int from,int to,LL val) {
G[from].push_back({to,val});
}
void go() {
dis[s]=0;
Q.push({0,s});
while(!Q.empty()) {
Node top=Q.top(); Q.pop();
int now=top.key;
if (vis[now]) continue;
vis[now]=1;
for (int i=0;i<G[now].size();i++) {
int to=G[now][i].to;
if (dis[to]>dis[now]+G[now][i].val) {
dis[to]=dis[now]+G[now][i].val;
if (!vis[to]) Q.push({dis[to],to});
}
}
}
}
}dijkstra;
int n,m,s;
int main() {
cin>>n>>m>>s;
dijkstra.init(n,s);
for (int i=1;i<=m;i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
dijkstra.addEdge(u,v,w);
}
dijkstra.go();
for (int i=1;i<=n;i++) printf("%lld ",dijkstra.dis[i]);
return 0;
}
树的重心
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1e5+5;
int ans = N,head[N],nxt[N*2],e[N*2],tot,n;//无向图开两倍空间
bool st[N];
void add(int u,int v) {
e[tot] = v;
nxt[tot] = head[u];
head[u] = tot++;
}
int dfs(int u) {
st[u] = true;
int cnt = 0,sum = 0;
for(int i=head[u];i != -1;i = nxt[i]) {
int j = e[i];
if(!st[j]) {
int s = dfs(j);
cnt = max(cnt,s);//每次统计以u为根的子树中点的个数的最大值
sum += s;
}
}
cnt = max(cnt,n - sum - 1);//与剩余的点的个数比较
ans = min(ans,cnt);//比较各个最大值中的最小值
return sum + 1;//返回以u为根的子树中的点的个数(加上本身)
}
int main() {
cin >> n;
int l,r;
memset(head,-1,sizeof(head));
for(int i=0;i<n;i++) {
cin >> l >> r;
add(l,r);
add(r,l);
}
dfs(1);
cout << ans;
return 0;
}
无向图tarjan求割点、割边、点双连通分量
##include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 5;
struct Edge {
int from, to;
Edge(){}
Edge(int f, int t) {
from = f;
to = t;
}
};
vector<int> G[N]; // 存图
vector<Edge> bridge; // 存桥
vector<int> cut_vertex; // 存割点
vector<int> compo_vertex[N]; // 存点双连通分量点
vector<Edge> compo_edge[N]; // 存点双连通分量边
vector<Edge> st_edge; // tarjan
vector<int> st_vertex; // tarjan
int dfn[N], low[N], IDX; // tarjan
int cut[N]; // cut[x] > 0 则为割点
int TOT; // 点双连通分量的数量
int n, m;
void init() {
for (int i = 1; i <= n; i++) {
G[i].clear();
compo_vertex[i].clear();
compo_edge[i].clear();
dfn[i] = cut[i] = 0;
}
IDX = TOT = 0;
bridge.clear(); cut_vertex.clear();
}
void tarjan(int x, int ff) {
dfn[x] = low[x] = ++IDX;
for (auto to : G[x]) {
if (to == ff) continue;
if (!dfn[to]) {
st_edge.push_back(Edge(x, to));
st_vertex.push_back(to);
tarjan(to, x);
low[x] = min(low[x], low[to]);
if (low[to] >= dfn[x]) {
cut[x]++;
TOT++;
compo_vertex[TOT].clear(); compo_vertex[TOT].push_back(x);
compo_edge[TOT].clear();
while(!st_vertex.empty() && st_vertex.back() != x) { // 存点
compo_vertex[TOT].push_back(st_vertex.back());
st_vertex.pop_back();
}
while(st_edge.back().from != x) { // 存边
compo_edge[TOT].push_back(st_edge.back());
st_edge.pop_back();
}
compo_edge[TOT].push_back(st_edge.back());
st_edge.pop_back();
}
if (low[to] > dfn[x]) bridge.push_back(Edge(x, to));
} else {
if (dfn[to] < dfn[x]) st_edge.push_back(Edge(x, to));
low[x] = min(low[x], dfn[to]);
}
}
}
void solve() {
scanf("%d %d", &n, &m);
init();
for (int i = 1; i <= m; i++) {
int u, v; scanf("%d %d", &u, &v);
G[u].push_back(v);
G[v].push_back(u);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) {
cut[i] = -1;
tarjan(1, -1);
}
}
for (int i = 1; i <= n; i++) {
if (cut[i]) cut_vertex.push_back(i);
}
int mx = 0;
for (int i = 1; i <= TOT; i++) mx = max(mx, (int) compo_edge[i].size());
int gcd = __gcd(TOT, mx);
printf("%d %d %d %d\n", cut_vertex.size(), bridge.size(), TOT / gcd, mx / gcd);
}
int main() {
int T; cin >> T;
while(T--) {
solve();
}
return 0;
}
有向图tarjan缩点
#include <bits/stdc++.h>
using namespace std;
const int N = 3e4 + 5, INF = 0x3f3f3f3f;
struct Edge {
int to, v;
Edge(){}
Edge(int T, int V) {
to = T;
v = V;
}
};
vector<Edge> G[N];
vector<int> g[N];
vector<int> bcc[N];
queue<int> topo;
vector<int> ord;
int n, x, y, s, deg[N], dis[N];
int dfn[N], low[N], IDX, belong[N], sta[N], top, TAR;
bool vis[N];
void tarjan(int x) {
dfn[x] = low[x] = ++IDX;
vis[x] = 1, sta[++top] = x;
for (auto T : G[x]) {
int to = T.to;
if (!dfn[to]) {
tarjan(to);
low[x] = min(low[x], low[to]);
} else if (vis[to]) low[x] = min(low[x], dfn[to]);
}
if (low[x] == dfn[x]) {
int now = sta[top];
TAR++; bcc[TAR].clear();
while(sta[top + 1] != x) {
vis[now] = 0;
belong[now] = TAR;
bcc[TAR].push_back(now);
now = sta[--top];
}
}
}
void tarjan_go() {
for (int i = 1; i <= TAR; i++) g[i].clear();
for (int i = 1; i <= n; i++) {
for (auto T : G[i]) {
int u = i, v = T.to;
u = belong[u], v = belong[v];
if (u != v) {
g[u].push_back(v);
deg[v]++;
}
}
}
}
struct Node {
int key, dis;
bool operator < (const Node &N) const {
return dis > N.dis;
}
Node(){}
Node(int k, int d) {
key = k;
dis = d;
}
};
priority_queue<Node> p;
void dijkstra(int now) {
for (auto x : bcc[now]) {
if (dis[x] != INF) p.push(Node(x, dis[x]));
}
while(!p.empty()) {
int now = p.top().key; p.pop();
if (vis[now]) continue;
vis[now] = 1;
for (auto T : G[now]) {
int to = T.to, d = T.v;
if (vis[to]) continue;
if (dis[to] > dis[now] + d) {
dis[to] = dis[now] + d;
if (belong[to] != belong[now]) continue;
p.push(Node(to, dis[to]));
}
}
}
}
int main() {
scanf("%d %d %d %d", &n, &x, &y, &s);
for (int i = 1; i <= x; i++) {
int u, v, w; scanf("%d %d %d", &u, &v, &w);
G[u].push_back(Edge(v, w));
G[v].push_back(Edge(u, w));
}
for (int i = 1; i <= y; i++) {
int u, v, w; scanf("%d %d %d", &u, &v, &w);
G[u].push_back(Edge(v, w));
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) tarjan(i);
}
tarjan_go();
for (int i = 1; i <= TAR; i++) {
if (deg[i] == 0) topo.push(i);
}
while(!topo.empty()) {
int now = topo.front(); topo.pop();
ord.push_back(now);
for (auto to : g[now]) {
deg[to]--;
if (deg[to] == 0) topo.push(to);
}
}
memset(dis, INF, sizeof(dis));
memset(vis, 0, sizeof(vis));
dis[s] = 0;
for (auto i : ord) {
dijkstra(i);
}
for (int i = 1; i <= n; i++) {
if (dis[i] != INF) printf("%d\n", dis[i]);
else printf("NO PATH\n");
}
return 0;
}
虚树+LCA优化
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int pre[N][25],dfn[N],deep[N],sta[N],TOP,lg[N];
int A[N],n,s,NODE,x,y;
ll ans=0;
vector<int>G[N];
vector<int>E[N];
vector<int>D[N];
void DFS(int x,int fa) {
int to;
dfn[x]=++NODE;
deep[x]=deep[fa]+1;
D[deep[x]].push_back(x);
pre[x][0]=fa;
for (int i=1;(1<<i)<=deep[x];i++) pre[x][i]=pre[pre[x][i-1]][i-1];
for (int i=0;i<G[x].size();i++) {
to=G[x][i];
if (to!=fa)
DFS(to,x);
}
}
int LCA(int x,int y) {
if (deep[x]<deep[y]) swap(x,y);
while(deep[x]>deep[y]) x=pre[x][lg[deep[x]-deep[y]]-1];
if (x==y) return x;
for (int i=lg[deep[x]]-1;i>=0;i--)
if (pre[x][i]!=pre[y][i])
x=pre[x][i],y=pre[y][i];
return pre[x][0];
}
void Insert(int x) {
if (TOP==1) {
if (sta[TOP]!=x) sta[++TOP]=x;
return;
}
int lca=LCA(x,sta[TOP]);
while(TOP>=2 && dfn[sta[TOP-1]]>=dfn[lca]) {
E[sta[TOP-1]].push_back(sta[TOP]);
TOP--;
}
if (sta[TOP]!=lca) E[lca].push_back(sta[TOP]),sta[TOP]=lca;
sta[++TOP]=x;
}
ll solve(int x) {
if (E[x].empty())
return 1LL*A[x];
ll ret=0;
for (int i=0;i<E[x].size();i++) {
int to=E[x][i];
ll sol=solve(to);
if (sol!=0)
ret+=max(1LL,sol-(deep[to]-deep[x]));
}
E[x].clear();
return ret;
}
int main() {
cin>>n>>s;
for (int i=1;i<=n;i++) lg[i]=lg[i-1]+(1<<(lg[i-1])==i);
for (int i=1;i<=n;i++) scanf("%d",&A[i]);
for (int i=1;i<=n-1;i++) {
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
DFS(s,0);
for (int i=1;!D[i].empty();i++) {
sort(D[i].begin(),D[i].end(),[&](int u,int v){return dfn[u]<dfn[v];});
TOP=0;
sta[++TOP]=s;
for (int j=0;j<D[i].size();j++) {
Insert(D[i][j]);
}
while(TOP>1) {
E[sta[TOP-1]].push_back(sta[TOP]);
TOP--;
}
ll sol=solve(s);
if (sol) ans+=max(1LL,sol-1);
}
printf("%lld",ans);
return 0;
}
根据深度建虚树
void build() {
sum = 1;
dep[1] = 1;
ans = 0;
for (int i = 2; i <= m; i++) {
int j = i;
for (; j != mindiv[j]; j /= mindiv[j]) sum++;
lcadep[i] = bit.query(m) - bit.query(j - 1) + 1;
sum++;
dep[i] = sum;
for (j = i; j > 1; j /= mindiv[j]) bit.modify(mindiv[j]);
ans += 1ll * (dep[i] - 1) * w[i];
}
TOP = 0, TOT = 1;
st[++TOP] = {1, 1};
W[1] = w[1];
for (int i = 2; i <= m; i++) {
if (lcadep[i] != dep[i - 1]) {
while(TOP > 1 && st[TOP - 1].dep >= lcadep[i]) {
G[st[TOP - 1].num].push_back({st[TOP].num, st[TOP].dep - st[TOP - 1].dep});
TOP--;
}
if (lcadep[i] != st[TOP].dep) {
G[++TOT].push_back({st[TOP].num, st[TOP].dep - lcadep[i]});
st[TOP] = {TOT, lcadep[i]};
}
}
st[++TOP] = {++TOT, dep[i]};
W[TOT] = w[i];
}
while(TOP > 1) {
G[st[TOP - 1].num].push_back({st[TOP].num, st[TOP].dep - st[TOP - 1].dep});
TOP--;
}
}
点分治
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e4 + 5, M = 1e7 + 5, P = 1e2 + 5, INF = 0x3f3f3f3f;
struct Edge {
int to, v;
};
struct BIT {
int t[N<<1];
int lowbit(int x) {return x &(-x);}
void add(int x, int v) {
while(x < N) {
t[x] += v;
x += lowbit(x);
}
}
int ask(int x) {
int res = 0;
while(x) {
res += t[x];
x -= lowbit(x);
}
return res;
}
}bit;
int n, m, k, q[P], cnt[P], bucket[M];
vector<int> D;
/**variables of tree divide*/
int tot, sz[N], MN, rt;
bool vis[N];
vector<Edge> G[N];
int Getroot(int x, int f) {
int sum = 0, mx = 0, tmp;
for (auto T : G[x]) {
int to = T.to;
if (vis[to] || to == f) continue;
tmp = Getroot(to, x);
sum += tmp;
mx = max(mx, tmp);
}
sum++;
mx = max(mx, tot - sum);
if (mx < MN) {
MN = mx;
rt = x;
}
return sum;
}
void Getsz(int x, int f) {
sz[x] = 0;
for (auto T : G[x]) {
int to = T.to;
if (vis[to] || to == f) continue;
Getsz(to, x);
sz[x] += sz[to];
}
sz[x]++;
}
void Getdis(int x, int len, int f) {
D.push_back(len);
for (auto T : G[x]) {
int to = T.to, v = T.v;
if (vis[to] || to == f) continue;
Getdis(to, len + v, x);
}
}
void calc(int x, int len, int type) {
D.clear();
Getdis(x, len, -1);
for (int y : D) {
if (y > M) continue;
for (int i = 1; i <= m; i++) {
if (q[i] - y < 0) continue;
cnt[i] += type * bucket[q[i] - y];
}
bucket[y]++;
}
for (int y : D) {
if (y > M) continue;
bucket[y]--;
}
}
void solve(int x) {
vis[x] = 1;
calc(x, 0, 1);
for (auto T : G[x]) {
int to = T.to, v = T.v;
if (vis[to]) continue;
calc(to, v, -1);
tot = sz[to], MN = INF;
Getroot(to, -1);
Getsz(rt, -1);
solve(rt);
}
}
int main() {
cin >> n >> m;
for (int i = 1, u, v, w; i <= n - 1; i++) {
scanf("%d %d %d", &u, &v, &w);
G[u].push_back({v, w});
G[v].push_back({u, w});
}
for (int i = 1; i <= m; i++) scanf("%d", &q[i]);
tot = n, MN = INF;
Getroot(1, -1);
Getsz(rt, -1);
solve(rt);
for (int i = 1; i <= m; i++) {
printf("%s\n", cnt[i] ? "AYE" : "NAY");
}
return 0;
}
树上启发式合并dsu on tree
#include <bits/stdc++.h>
#define debug(x) cerr << #x << " = " << x << ' '
using namespace std;
typedef long long ll;
const int N = 1e5 + 5, M = 2e6 + 5;
int n, sz[N], son[N], a[N], tot[M], num[M][25], ndfn[N], IDX, f[N], g[N];
ll ans;
vector<int> G[N];
vector<int> vc;
void dfs1(int x, int ff) {
f[x] = ++IDX;
ndfn[IDX] = x;
sz[x] = 1;
for (int to : G[x]) {
if (to == ff) continue;
dfs1(to, x);
sz[x] += sz[to];
if (sz[to] > sz[son[x]]) son[x] = to;
}
g[x] = IDX;
}
void modify(int x, int v) {
tot[a[x]] += v;
for (int j = 0; j <= 20; j++) {
if ((x >> j) & 1) num[a[x]][j] += v;
}
}
void dfs2(int x, int ff, int keep) {
for (int to : G[x]) {
if (to == ff || to == son[x]) continue;
dfs2(to, x, 0);
}
if (son[x]) dfs2(son[x], x, 1);
for (int to : G[x]) {
if (to == ff || to == son[x]) continue;
for (int i = f[to]; i <= g[to]; i++) {
int now = ndfn[i];
int k = a[x] ^ a[now];
for (int j = 0; j <= 20; j++) {
if ((now >> j) & 1) ans += (1 << j) * (tot[k] - num[k][j]);
else ans += (1 << j) * num[k][j];
}
}
for (int i = f[to]; i <= g[to]; i++) {
int j = ndfn[i];
modify(j, 1);
}
}
modify(x, 1);
if (!keep) {
for (int i = f[x]; i <= g[x]; i++) {
modify(ndfn[i], -1);
}
}
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n - 1; i++) {
int x, y; scanf("%d %d", &x, &y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs1(1, -1);
dfs2(1, -1, 1);
cout << ans;
return 0;
}
树链剖分(轻重链剖分)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+5;
LL q[N];
int n,m,r,p,ord[N];
struct segTree {
LL t[N<<2];
LL lztag[N<<2];
void init() {
memset(t,0,sizeof(t));
memset(lztag,0,sizeof(lztag));
}
void pushdown(int o,int l,int r) {
if (lztag[o]) {
int mid=(l+r)>>1;
t[o*2]+=lztag[o]*(mid-l+1);
t[o*2]%=p;
t[o*2+1]+=lztag[o]*(r-mid);
t[o*2+1]%=p;
lztag[o*2]+=lztag[o];
lztag[o*2]%=p;
lztag[o*2+1]+=lztag[o];
lztag[o*2+1]%=p;
lztag[o]=0;
}
}
void pushup(int o,int l,int r) {
t[o]=(t[o*2]+t[o*2+1])%p;
}
void build(int o,int l,int r) {
if (l==r) {
t[o]=ord[l]%p;
return;
}
int mid=(l+r)>>1;
build(o*2,l,mid);
build(o*2+1,mid+1,r);
pushup(o,l,r);
}
void modify(int o,int l,int r,int tl,int tr,int v) {
if (tl<=l && r<=tr) {
t[o]+=1LL*v*(r-l+1);
t[o]%=p;
lztag[o]+=v;
lztag[o]%=p;
return;
}
int mid=(l+r)>>1;
pushdown(o,l,r);
if (tl<=mid) modify(o*2,l,mid,tl,tr,v);
if (tr>mid) modify(o*2+1,mid+1,r,tl,tr,v);
pushup(o,l,r);
}
LL query(int o,int l,int r,int tl,int tr) {
if (tl<=l && tr>=r) return t[o];
int mid=(l+r)>>1;
pushdown(o,l,r);
if (tr<=mid) return query(o*2,l,mid,tl,tr)%p;
if (tl>mid) return query(o*2+1,mid+1,r,tl,tr)%p;
return (query(o*2,l,mid,tl,tr)+query(o*2+1,mid+1,r,tl,tr))%p;
}
}ST;
int sz[N],dep[N],son[N],fa[N],top[N],num[N],cnt;
vector<int>G[N];
void init() {
memset(sz,0,sizeof(sz));
cnt=0;
fa[r]=-1;
dep[r]=1;
top[r]=r;
}
void dfs1(int x) {
int mxson=-1;
son[x]=-1;
for (auto to:G[x]) {
if (to==fa[x]) continue;
dep[to]=dep[x]+1;
fa[to]=x;
dfs1(to);
if (sz[to]>mxson) mxson=sz[to], son[x]=to;
sz[x]+=sz[to];
}
sz[x]++;
}
void dfs2(int x,int tp) {
num[x]=++cnt;
ord[cnt]=q[x];
top[x]=tp;
if (~son[x]) dfs2(son[x],tp);
for (auto to:G[x]) {
if (to==fa[x] || to==son[x]) continue;
dfs2(to,to);
}
}
void chainModify(int x,int y,int z) {
while(top[x]!=top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y);
ST.modify(1,1,n,num[top[x]],num[x],z);
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
ST.modify(1,1,n,num[x],num[y],z);
}
LL chainQuery(int x,int y) {
LL res=0;
while(top[x]!=top[y]) {
if (dep[top[x]]<dep[top[y]]) swap(x,y);
res+=ST.query(1,1,n,num[top[x]],num[x]);
res%=p;
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
res+=ST.query(1,1,n,num[x],num[y]);
res%=p;
return res;
}
void subtreeModify(int x,int z) {
ST.modify(1,1,n,num[x],num[x]+sz[x]-1,z);
}
LL subtreeQuery(int x) {
return ST.query(1,1,n,num[x],num[x]+sz[x]-1);
}
int main() {
cin>>n>>m>>r>>p;
for (int i=1;i<=n;i++) {
scanf("%lld",&q[i]);
q[i]%=p;
}
for (int i=1;i<=n-1;i++) {
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
init();
dfs1(r);
dfs2(r,r);
ST.build(1,1,n);
for (int i=1;i<=m;i++) {
int opt,x,y;
LL z;
scanf("%d",&opt);
if (opt==1) {
scanf("%d%d%lld",&x,&y,&z);
z%=p;
chainModify(x,y,z);
}
if (opt==2) {
scanf("%d%d",&x,&y);
printf("%lld\n",chainQuery(x,y));
}
if (opt==3) {
scanf("%d%lld",&x,&z);
z%=p;
subtreeModify(x,z);
}
if (opt==4) {
scanf("%d",&x);
printf("%lld\n",subtreeQuery(x));
}
}
return 0;
}
动态树LCT
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,INF=0x3f3f3f3f;
struct Link_Cut_Tree{
int top,root,ch[N][2],ff[N],val[N],xr[N],tag[N],q[N];
bool isroot(int x) {
return ch[ff[x]][0]!=x && ch[ff[x]][1]!=x;
}
void push_up(int x) {
xr[x]=xr[ch[x][0]]^xr[ch[x][1]]^val[x];
}
void push_down(int x) {
if (tag[x]) {
swap(ch[x][0],ch[x][1]);
tag[ch[x][0]]^=1;
tag[ch[x][1]]^=1;
tag[x]=0;
}
}
void Rotate(int x) {
int y=ff[x],z=ff[y],k=ch[y][1]==x;
if (!isroot(y)) {
ch[z][ch[z][1]==y]=x;
}
ff[x]=z;
ch[y][k]=ch[x][k^1],ff[ch[y][k]]=y;
ch[x][k^1]=y,ff[y]=x;
push_up(y);push_up(x);
}
void Splay(int x) {
top=1;q[top]=x;
for (int i=x;!isroot(i);i=ff[i]) q[++top]=ff[i];
for (int i=top;i;i--) push_down(q[i]);
while(!isroot(x)) {
int y=ff[x],z=ff[y];
if (!isroot(y)) {
if ((ch[y][0]==x)^(ch[z][0]==y)) Rotate(x);
else Rotate(y);
}
Rotate(x);
}
}
void Access(int x) {
for (int y=0;x;y=x,x=ff[x]) {
Splay(x);ch[x][1]=y;push_up(x);
}
}
void Makeroot(int x) {
Access(x);Splay(x);
tag[x]^=1;
push_down(x);
}
int Findroot(int x) {
Access(x); Splay(x);
push_down(x);
while(ch[x][0]) {
push_down(x);x=ch[x][0];
}
Splay(x);
return x;
}
void Split(int x,int y) {
Makeroot(x);
Access(y); Splay(y);
}
void Link(int x,int y) {
Makeroot(x);
if (Findroot(y)==x) return;
ff[x]=y;
}
void Cut(int x,int y) {
Makeroot(x);
if (Findroot(y)!=x || ff[y]!=x || ch[y][0]) return;
ff[y]=ch[x][1]=0;
push_up(x);
}
int Query(int x,int y) {
Split(x,y);
return xr[y];
}
void Modify(int x,int y) {
val[x]=y; Makeroot(x);
}
}lct;
int n,m;
int main() {
cin>>n>>m;
for (int i=1;i<=n;i++) scanf("%d",&lct.val[i]),lct.xr[i]=lct.val[i];
for (int i=1;i<=m;i++) {
int opt,x,y;
scanf("%d%d%d",&opt,&x,&y);
switch(opt) {
case 0:printf("%d\n",lct.Query(x,y));break;
case 1:lct.Link(x,y);break;
case 2:lct.Cut(x,y);break;
case 3:lct.Modify(x,y);break;
}
}
return 0;
}
2-SAT
// 建图
n = read(), m = read();
for (int i = 0; i < m; ++i) {
// 笔者习惯对 x 点标号为 x,-x 标号为 x+n,当然也可以反过来。
int a = read(), va = read(), b = read(), vb = read();
if (va && vb) { // a, b 都真,-a -> b, -b -> a
g[a + n].push_back(b);
g[b + n].push_back(a);
} else if (!va && vb) { // a 假 b 真,a -> b, -b -> -a
g[a].push_back(b);
g[b + n].push_back(a + n);
} else if (va && !vb) { // a 真 b 假,-a -> -b, b -> a
g[a + n].push_back(b + n);
g[b].push_back(a);
} else if (!va && !vb) { // a, b 都假,a -> -b, b -> -a
g[a].push_back(b + n);
g[b].push_back(a + n);
}
}
// 找环
// 注意所有东西都要开两倍空间,因为每个变量存了两次
void tarjan(int u) {
low[u] = dfn[u] = ++dfsClock;
stk.push(u); ins[u] = true;
for (const auto &v : g[u]) {
if (!dfn[v]) tarjan(v), low[u] = std::min(low[u], low[v]);
else if (ins[v]) low[u] = std::min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
++sccCnt;
do {
color[u] = sccCnt;
u = stk.top(); stk.pop(); ins[u] = false;
} while (low[u] != dfn[u]);
}
}
// Tarjan 找环,得到的 color[x] 是 x 所在的 scc 的拓扑逆序。
for (int i = 1; i <= (n << 1); ++i) if (!dfn[i]) tarjan(i);
// 输出
for (int i = 1; i <= n; ++i)
if (color[i] == color[i + n]) { // x 与 -x 在同一强连通分量内,一定无解
puts("IMPOSSIBLE");
exit(0);
}
puts("POSSIBLE");
for (int i = 1; i <= n; ++i)
print((color[i] < color[i + n])), putchar(' '); // 如果不使用 Tarjan 找环,请改成大于号
puts("");
最大流Dinic
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=5e3+5;
//理论上界O(N^2*M)
//二分图最大匹配时间复杂度O(sqrt(N)*M)
struct Dinic{
struct Edge{
int to;LL v;int rev;
};
int n,s,t,dep[N],_cur[N];
bool used[N];
vector<Edge>G[N];
queue<int>L;
void init(int n_,int s_,int t_) {
n=n_,s=s_,t=t_;
for (int i=0;i<=n;i++) G[i].clear();
}
void addEdge(int x,int y,LL v) {
G[x].push_back({y,v,G[y].size()});
G[y].push_back({x,0,G[x].size()-1});
}
bool bfs() {
memset(used,0,sizeof(used));
dep[s]=1,used[s]=1;
L.push(s);
while(!L.empty()) {
int cur=L.front();
L.pop();
for (int i=0;i<G[cur].size();i++) {
Edge &e=G[cur][i];
if (!used[e.to] && e.v>0) {
dep[e.to]=dep[cur]+1;
used[e.to]=1;
L.push(e.to);
}
}
}
return used[t];
}
LL dfs(int c,LL flow) {
if (c==t ||flow==0) return flow;
LL ret=0,f;
for (int &i=_cur[c];i<G[c].size();i++) {
Edge &e=G[c][i];
if (dep[e.to]==dep[c]+1 && (f=dfs(e.to,min(flow,e.v)))>0) {
ret+=f,flow-=f,e.v-=f;
G[e.to][e.rev].v+=f;
if (!flow) break;
}
}
return ret;
}
LL go() {
LL ans=0;
while(bfs()) {
memset(_cur,0,sizeof(_cur));
ans+=dfs(s,1LL*100*INT_MAX);
}
return ans;
}
}dinic;
int n,m,e,s,t;
int main() {
cin>>n>>m>>s>>t;
dinic.init(n,s,t);
for (int i=1;i<=m;i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
dinic.addEdge(u,v,w);
}
printf("%lld\n",dinic.go());
return 0;
}
集合划分建图(最大流最小割)
void solve()
{
cin>>n;
s=0,t=n+n*n*2+1;
for (int i=s;i<=t;i++) G[i].clear();
sum=0;
for (int i=1;i<=n;i++)
{
scanf("%lf",&a);
ac=round(a*100);
addEdge(s,i,ac);
sum+=ac;
}
for (int i=1;i<=n;i++)
{
scanf("%lf",&a);
ac=round(a*100);
addEdge(i,t,ac);
sum+=ac;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
scanf("%lf",&b[i][j]);
ac=round((b[i][j])*100);
sum+=ac*2;
addEdge(s,n+((i-1)*n+j)*2-1,ac);
addEdge(n+((i-1)*n+j)*2,t,ac);
addEdge(n+((i-1)*n+j)*2-1,i,INF);
addEdge(n+((i-1)*n+j)*2-1,j,INF);
addEdge(i,n+((i-1)*n+j)*2,INF);
addEdge(j,n+((i-1)*n+j)*2,INF);
}
ll mincut=Dinic();
double ans=(sum-mincut)/100.0;
printf("%.2lf\n",ans);
}
int main()
{
cin>>T;
while(T--) solve();
return 0;
}
最小费用最大流(MCMF)
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+5;
const int INF=0x3f3f3f3f3f3f3f3f;
struct MCMF {
struct E{
int from,to;
LL flow,dis;
};
queue<int>Q;
vector<E>edge;
vector<int>G[N];
bool vis[N];
int n,m,s,t,pre[N],last[N];
LL dis[N],flow[N],maxflow,mincost;
void init(int _n,int _s,int _t) {
n=_n,s=_s,t=_t;
for (int i=0;i<=n;i++) G[i].clear();
edge.clear();m=0;
maxflow=mincost=0;
}
void addEdge(int from,int to,int flow,int cost) {
edge.push_back({from,to,flow,cost});
edge.push_back({to,from,0,-cost});
G[from].push_back(m++);
G[to].push_back(m++);
}
bool spfa(int s,int t) {
for (int i=0;i<=n;i++) {
dis[i]=INF;flow[i]=INF;vis[i]=0;
}
Q.push(s);dis[s]=0;vis[s]=1;pre[t]=-1;
while(!Q.empty()) {
int now=Q.front();
Q.pop();
vis[now]=0;
for (int i:G[now]) {
if (edge[i].flow && dis[edge[i].to]>dis[now]+edge[i].dis) {
dis[edge[i].to]=dis[now]+edge[i].dis;
pre[edge[i].to]=now;
last[edge[i].to]=i;
flow[edge[i].to]=min(flow[now],edge[i].flow);
if (!vis[edge[i].to]) {
vis[edge[i].to]=1;
Q.push(edge[i].to);
}
}
}
}
return pre[t]!=-1;
}
LL go() {
while(spfa(s,t)) {
int now=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(now!=s) {
edge[last[now]].flow-=flow[t];
edge[last[now]^1].flow+=flow[t];
now=pre[now];
}
}
return mincost;
}
}mcmf;
int n,m,s,t;
int main() {
cin>>n>>m>>s>>t;
mcmf.init(n,s,t);
for (int i=1;i<=m;i++) {
int u,v;LL w,f;
scanf("%d%d%lld%lld",&u,&v,&w,&f);
mcmf.addEdge(u,v,w,f);
}
mcmf.go();
printf("%lld %lld\n",mcmf.maxflow,mcmf.mincost);
return 0;
}
二分图最大匹配(匈牙利算法)(最小点覆盖=最大匹配)
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e2+5;
vector<int>G[MAXN];
int n,m,e,ans,u,v;
int vistime[MAXN],mch[MAXN];
bool dfs(int u,int tag) {
if (vistime[u]==tag) return 0;
vistime[u]=tag;
for (auto v:G[u])
if (mch[v]==0 || dfs(mch[v],tag)) {
mch[v]=u;
return 1;
}
return 0;
}
int main() {
cin>>n>>m>>e;
for (int i=1;i<=e;i++) {
scanf("%d%d",&u,&v);
G[u].push_back(v);
}
for (int i=1;i<=n;i++)
if (dfs(i,i)) ans++;
printf("%d\n",ans);
return 0;
}
一般图最大匹配(Edmonds带花树)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAX_N=1000+20;
const int INF=0x3f3f3f3f;
int N,M;
int n,m;
int d[MAX_N];
bool Graph[MAX_N][MAX_N];
int Match[MAX_N];
bool InQueue[MAX_N],InPath[MAX_N],InBlossom[MAX_N];
int Head,Tail;
int Queue[MAX_N];
int Start,Finish;
int NewBase;
int Father[MAX_N],Base[MAX_N];
int Count;
void CreatGraph(){//O(n^3)
int u,v;
vector<int>V[MAX_N];
memset(Graph,false,sizeof(Graph));
int tot=1;
for (int i=1;i<=n;i++)
{
scanf("%d",&d[i]);
for (int j=1;j<=d[i];j++) V[i].push_back(tot++);
}
for (int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
int x=tot++,y=tot++;
for (int j=0;j<d[u];j++) Graph[V[u][j]][x]=Graph[x][V[u][j]]=1;
for (int j=0;j<d[v];j++) Graph[V[v][j]][y]=Graph[y][V[v][j]]=1;
Graph[x][y]=Graph[y][x]=1;
}
N=tot-1;
}
void Push(int u){
Queue[Tail]=u;
Tail++;
InQueue[u]=true;
}
int Pop(){
int res=Queue[Head];
Head++;
return res;
}
int FindCommonAncestor(int u,int v){
memset(InPath,false,sizeof(InPath));
while(1){
u=Base[u];
InPath[u]=1;
if(u==Start)break;
u=Father[Match[u]];
}
while(1){
v=Base[v];
if(InPath[v])break;
v=Father[Match[v]];
}
return v;
}
void ResetTrace(int u){
int v;
while(Base[u]!=NewBase){
v=Match[u];
InBlossom[Base[u]]=InBlossom[Base[v]]=1;
u=Father[v];
if(Base[u]!=NewBase)Father[u]=v;
}
}
void BloosomContract(int u,int v){
NewBase=FindCommonAncestor(u,v);
memset(InBlossom,false,sizeof(InBlossom));
ResetTrace(u);
ResetTrace(v);
if(Base[u]!=NewBase)Father[u]=v;
if(Base[v]!=NewBase)Father[v]=u;
for(int tu=1;tu<=N;tu++){
if(InBlossom[Base[tu]]){
Base[tu]=NewBase;
if(!InQueue[tu])Push(tu);
}
}
}
void FindAugmentingPath(){
memset(InQueue,false,sizeof(InQueue));
memset(Father,0,sizeof(Father));
for(int i=1;i<=N;i++){
Base[i]=i;
}
Head=Tail=1;
Push(Start);
Finish=0;
while(Head<Tail){
int u=Pop();
for(int v=1;v<=N;v++){
if(Graph[u][v]&&(Base[u]!=Base[v])&&(Match[u]!=v)){
if((v==Start)||((Match[v]>0)&&Father[Match[v]]>0))BloosomContract(u,v);
else if(Father[v]==0){
Father[v]=u;
if(Match[v]>0)Push(Match[v]);
else{
Finish=v;
return;
}
}
}
}
}
}
void AugmentPath(){
int u,v,w;
u=Finish;
while(u>0){
v=Father[u];
w=Match[v];
Match[v]=u;
Match[u]=v;
u=w;
}
}
void Edmonds(){
memset(Match,0,sizeof(Match));
for(int u=1;u<=N;u++){
if(Match[u]==0){
Start=u;
FindAugmentingPath();
if(Finish>0)AugmentPath();
}
}
}
void PrintMatch(){
Count=0;
for(int u=1;u<=N;u++){
if(Match[u]>0)Count++;
}
if(Count==N)printf("Yes\n");
else printf("No\n");
}
int main(){
while(scanf("%d%d",&n,&m)==2){
CreatGraph();
Edmonds();
PrintMatch();
}
}
数据结构
树状数组
//第一个位置从1开始(0不可用)
struct BIT{
LL tr[N];
int lowbit(int x){ return x&(-x); }
void clr(int x){
while(x<SZ)
{
tr[x]=0;
x+=lowbit(x);
}
}
void add(int v,int x){
while(x<N)
{
tr[x]+=v;
x+=lowbit(x);
}
}
LL sum(int x){
LL sum=0;
while(x>0)
{
sum+=tr[x];
x-=lowbit(x);
}
return sum;
}
}bit;
//BIT求MEX
struct BIT {
int bucket[N],tr[N];
int lowbit(int x){
return x&(-x);
}
void add(int x,int v) {
if (x<SZ) {
bucket[x]+=v;
if (bucket[x]==1 && v==1|| bucket[x]==0 && v==-1)
while(x<SZ) {
tr[x]+=v;
x+=lowbit(x);
}
}
}
int sum(int x) {
int sum=0;
while(x>0) {
sum+=tr[x];
x-=lowbit(x);
}
return sum;
}
int MEX() {
int l=1,r=SZ-1;
while(l<r) {
int mid=(l+r)>>1;
if (sum(mid)<mid)
r=mid;
else l=mid+1;
}
return l;
}
}bit;
二维树状数组
struct BIT{
int C[N][N];
int lowbit(int x) {return x&(-x);}
void Modify(int i,int j,int delta){
for(int x=i;x<=a;x+=lowbit(x))
for(int y=j;y<=b;y+=lowbit(y)) {
C[x][y]+=delta;
}
}
int Sum(int i,int j)
{
int result=0;
for(int x=i;x>0;x-=lowbit(x)) {
for(int y=j;y>0;y-=lowbit(y)) {
result+=C[x][y];
}
}
return result;
}
}bit;
线段树
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+5;
struct Segtree {
LL t[N<<2],lz[N<<2];
void push_down(int o,int l,int r) {
if (lz[o]) {
int mid=(l+r)>>1;
t[o*2]+=lz[o]*(mid-l+1);
t[o*2+1]+=lz[o]*(r-mid);
lz[o*2]+=lz[o];
lz[o*2+1]+=lz[o];
lz[o]=0;
}
}
void push_up(int o,int l,int r) {
t[o]=t[o*2]+t[o*2+1];
}
void update(int o,int l,int r,LL v) {
t[o]+=v*(r-l+1);
lz[o]+=v;
}
void build(int o,int l,int r) {
lz[o]=0;
if (l==r) {
scanf("%lld",&t[o]);
return;
}
int mid=(l+r)>>1;
build(o*2,l,mid);
build(o*2+1,mid+1,r);
push_up(o,l,r);
}
void modify(int o,int l,int r,int tl,int tr,LL v) {
if (tl<=l && tr>=r) {
update(o,l,r,v);
return;
}
push_down(o,l,r);
int mid=(l+r)>>1;
if (tl<=mid) modify(o*2,l,mid,tl,tr,v);
if (tr>mid) modify(o*2+1,mid+1,r,tl,tr,v);
push_up(o,l,r);
}
LL query(int o,int l,int r,int tl,int tr) {
if (tl<=l && r<=tr) return t[o];
push_down(o,l,r);
int mid=(l+r)>>1;
if (tr<=mid) return query(o*2,l,mid,tl,tr);
if (tl>mid) return query(o*2+1,mid+1,r,tl,tr);
return query(o*2,l,mid,tl,tr)+query(o*2+1,mid+1,r,tl,tr);
}
}segtree;
int n,m,x,y,k,op;
int main() {
cin>>n>>m;
segtree.build(1,1,n);
for (int i=1;i<=m;i++) {
scanf("%d",&op);
switch (op){
case 1:
scanf("%d%d%d",&x,&y,&k);
segtree.modify(1,1,n,x,y,k);
break;
case 2:
scanf("%d%d",&x,&y);
printf("%lld\n",segtree.query(1,1,n,x,y));
}
}
return 0;
}
ST表
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N][21], n, m;
int query(int l, int r) {
int k = log2(r - l + 1);
return max(a[l][k], a[r-(1<<k)+1][k]);
}
int main() {
cin >> n >> m;
for (int i=1; i<=n; i++) scanf("%d", &a[i][0]);
for (int i=1; i<=21; i++) {
for (int j=1; j+(1<<i)-1<=n; j++) {
a[j][i] = max(a[j][i-1], a[j+(1<<(i-1))][i-1]);
}
}
for (int i=1, l, r; i<=m; i++) {
scanf("%d %d", &l, &r);
printf("%d\n", query(l, r));
}
return 0;
}
Splay
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,INF=0x3f3f3f3f;
struct Splay_tree{
int root=0,ch[N][2],ff[N],sz[N],cnt[N],val[N],points;
void push_up(int x) {
sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x];
}
void Rotate(int x) {
int y=ff[x],z=ff[y],k=ch[y][1]==x;
ch[z][ch[z][1]==y]=x,ff[x]=z;
ch[y][k]=ch[x][k^1],ff[ch[y][k]]=y;
ch[x][k^1]=y,ff[y]=x;
push_up(y);push_up(x);
}
void Splay(int x,int goal) {
while(ff[x]!=goal) {
int y=ff[x],z=ff[y];
if (z!=goal) {
if ((ch[z][1]==y) ^ (ch[y][1]==x)) Rotate(x);
else Rotate(y);
}
Rotate(x);
}
if (goal==0) root=x;
}
void Insert(int x) {
int u=root,f=0;
while(u && val[u]!=x) {
f=u;
u=ch[u][val[u]<x];
}
if (u) cnt[u]++;
else {
u=++points;
if (f) ch[f][x>val[f]]=u;
ch[u][0]=ch[u][1]=0;
ff[u]=f; val[u]=x;
sz[u]=1; cnt[u]=1;
}
Splay(u,0);
}
void Find(int x) {
int u=root;
if (!u) return;
while(ch[u][x>val[u]] && x!=val[u]) u=ch[u][x>val[u]];
Splay(u,0);
}
int Next(int x,int f) {
Find(x);
int u=root;
if ((val[u]>x && f) || (val[u]<x && !f)) return u;
u=ch[u][f];
while(ch[u][f^1]) u=ch[u][f^1];
return u;
}
void Delete(int x) {
int pre=Next(x,0),nxt=Next(x,1);
Splay(pre,0);Splay(nxt,pre);
int del=ch[nxt][0];
if (cnt[del]>1) {
cnt[del]--;
Splay(del,0);
}
else {
ch[nxt][0]=0;
//Splay(nxt,0);
}
}
int Rank(int x) {
Find(x);
return sz[ch[root][0]];
}
int K_th(int x) {
int u=root;
if (sz[u]<x) return -1;
while(true) {
if (sz[ch[u][0]]+cnt[u]<x) {
x-=sz[ch[u][0]]+cnt[u];
u=ch[u][1];
}
else if (sz[ch[u][0]]>=x) {
u=ch[u][0];
}
else return u;
}
}
}splay;
int n,opt,x;
int main() {
cin>>n;
splay.Insert(-INF);splay.Insert(INF);
while(n--) {
scanf("%d%d",&opt,&x);
switch(opt) {
case 1:splay.Insert(x);break;
case 2:splay.Delete(x);break;
case 3:printf("%d\n",splay.Rank(x));break;
case 4:printf("%d\n",splay.val[splay.K_th(x+1)]);break;
case 5:printf("%d\n",splay.val[splay.Next(x,0)]);break;
case 6:printf("%d\n",splay.val[splay.Next(x,1)]);break;
}
}
return 0;
}
DP
四边形不等式优化
若$dp[l][r]=min_{l<k<=r}\{dp[l][k]+dp[k+1][r]+w[l][r]\}$(min也可以改为max)
且$w[l][r]$满足四边形不等式,即:$w[l][r]+w[l+1][r+1]<=w[l+1][r]+w[l][r+1]$
且满足区间单调性,即:$w[l+1][r]<=w[l][r+1]$
则有两个定理(当成结论就好):
定理一:函数$dp$也满足四边形不等式,即:
定理二:若$dp$满足四边形不等式,定义$s[i][j]$为$dp[i][j]$对应的决策变量的最大值,即:
则$s[i][j]$单调,即:
发现状态转移方程中$k$的范围存在限制,于是愉快缩小了变量$k$的枚举范围:
#include <bits/stdc++.h>
using namespace std;
const int N = 3e3 + 5, M = 3e2 + 5, INF = 0x3f3f3f3f;
int dp[N][M], d[N][M], x[N], w[N][N];
int n, m;
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &x[i]);
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
if (i == j) w[i][j] = 0;
else w[i][j] = w[i][j - 1] + x[j] - x[(i + j) >> 1];
}
}
memset(dp, INF, sizeof(dp));
for (int i = 1; i <= m; i++) d[n + 1][i] = n;
for (int i = 0; i <= m; i++) dp[0][i] = 0;
for (int j = 1; j <= m; j++) {
for (int i = n; i >= 1; i--) {
int id;
for (int k = d[i][j - 1]; k <= d[i + 1][j]; k++) {
if (dp[k][j - 1] + w[k + 1][i] < dp[i][j]) {
dp[i][j] = dp[k][j - 1] + w[k + 1][i];
id = k;
}
}
d[i][j] = id;
}
}
printf("%d\n", dp[n][m]);
return 0;
}
斜率优化
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 5;
/** 若找最大值则维护上凸壳 要求K单减
* 若找最小值则维护下凸壳 要求K单增
* 若K无单调性 可以维护凸壳后二分查找
*/
int n, a, b, c;
ll dp[N], sum[N], Q[N], head, tail;
ll Y(int j){return dp[j] + sum[j] * sum[j] * a - sum[j] * b;}
ll X(int j){return sum[j];}
ll K(int i){return 2 * a * sum[i];}
ll B(int i, int j){return Y(j) - K(i) * X(j);}
double slope(int i, int j){return 1.0 * (Y(i) - Y(j)) / (X(i) - X(j));}
int main() {
cin >> n >> a >> b >> c;
for (int i = 1; i <= n; i++) {
int x; scanf("%d", &x);
sum[i] = sum[i - 1] + x;
}
Q[++tail] = 0; head = 1;
for (int i = 1; i <= n; i++) {
while(head < tail && slope(Q[head], Q[head + 1]) > K(i)) ++head;
dp[i] = B(i, Q[head]) + sum[i] * sum[i] * a + sum[i] * b + c;
while(head < tail && slope(Q[tail - 1], Q[tail]) < slope(Q[tail], i)) --tail;
Q[++tail] = i;
}
printf("%lld\n", dp[n]);
return 0;
}
数位dp
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int N=200,M=5000;
int digit[20];
int dp[N][M][2][2];
char n[200];
int dfs(int pos,int del,int eqb,int eqa) {
if (pos==-1) {
if (del>2500)
return 1;
else return 0;
}
if (dp[pos][del][eqb][eqa]!=-1) return dp[pos][del][eqb][eqa];
int upb=eqb?n[pos]:9;
int sum=0;
for (int b=0;b<=upb;b++) {
for (int a=0;a<=(eqa?b:9);a++) {
sum=(sum+dfs(pos-1,del+a-b,eqb&&(b==n[pos]),eqa&&(a==b)))%MOD;
}
}
dp[pos][del][eqb][eqa]=sum;
return sum;
}
int main() {
scanf("%s",n);
int len=strlen(n);
reverse(n,n+len);
for (int i=0;i<len;i++) n[i]-='0';
memset(dp,-1,sizeof(dp));
printf("%d",dfs(len-1,2500,1,1));
return 0;
}
数学
快速幂
int bin(int x,int p) {
int res=1;
for (;p;p>>=1,x=(1ll*x*x)%MOD)
if (p&1) res=(1ll*res*x)%MOD;
return res;
}
exgcd扩展欧几里得(求ax+by=0整数解)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=3e6+5;
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;
}
int n,p;
int main()
{
cin>>n>>p;
for (int i=1;i<=n;i++)
{
ll x, y;
Exgcd (i, p, x, y);
x = (x % p + p) % p;
printf ("%d\n", x); //x是a在mod p下的逆元
}
}
线性基
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n; ll p[N], tmp[N];
void Insert(ll x) {
for (int i = 55; i >= 0; i--) {
if (!(x >> i)) continue;
if (!p[i]) {
p[i] = x;
return;
}
x ^= p[i];
}
}
bool ask(ll x) { //查询x是否能被异或出来
for (int i = 55; i >= 0; i--)
if (x >> i) x ^= p[i];
return x == 0;
}
ll k_th(int k) { //查询第k小异或值
ll res = 0; int cnt = 0;
for (int i = 0; i <= 55; i++) {
for (int j = i - 1; j != -1; j--)
if (p[i] >> j) p[i] ^= p[j];
if (p[i]) tmp[cnt++] = p[i];
}
if (k >= (1ll <<cnt)) return -1;
for (int i = 0; i < cnt; i++)
if (k & (1ll << i)) res ^= tmp[i];
return res;
}
ll getMx() { //查询最大异或值
ll res = 0;
for (int i = 55; i >= 0; i--) {
res = max(res, res ^ p[i]);
}
return res;
}
ll getMn() { //查询最小异或值
for (int i = 0; i <= 55; i++)
if (p[i]) return p[i];
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
ll x; scanf("%lld", &x);
Insert(x);
}
cout << getMx() << endl;
return 0;
}
欧拉筛
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e7+5;
int vis[N],prime[N],countPrime;
void getPrime() {
for (int i=2;i<N;i++) {
if (!vis[i]) prime[++countPrime]=i;
for (int j=1;j<=countPrime;j++) {
if (i*prime[j]>=N) break;
vis[i*prime[j]]=1;
if (i%prime[j]==0) break;
}
}
}
int main() {
getPrime();
printf("%d",countPrime);
return 0;
}
Min_25筛
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5;
int vis[N], pr[N], countPr;
void getPrime() {
for (int i = 2; i < N; i++) {
if (!vis[i]) pr[++countPr] = i;
for (int j = 1; j <= countPr; j++) {
if (i * pr[j] >= N) break;
vis[i * pr[j]] = 1;
if (i % pr[j] == 0) break;
}
}
}
LL g[N], ans;
LL sum[N], n, w[N];
int id1[N], id2[N], m;
int sqr, K, T;
int bin(int x, int p) {
int res = 1;
for (int b = x; p; p >>= 1, b = 1ll * b * b % K)
if (p & 1) res = 1ll * res * b % K;
return res;
}
int main() {
cin >> T;
getPrime();
for (int i = 1; i <= countPr; i++) sum[i] = sum[i - 1] + pr[i];
while (T--) {
scanf("%lld %d", &n, &K);
n++;
sqr = sqrt(n);
m = 0;
int inv2 = bin(2, K - 2);
for (LL i = 1, j; i <= n; i = j + 1) {
j = n / (n / i); w[++m] = n / i;
if (w[m] <= sqr) id1[w[m]] = m;
else id2[n / w[m]] = m;
g[m] =(1ll * (1 + w[m]) % K * w[m] % K * inv2 - 1) % K;
}
int tot = 0;
while (1ll * pr[tot] * pr[tot] <= n) tot++;
tot--;
clock_t st = clock();
int cc = 0;
LL k;
for (int j = 1; j <= tot; ++j) {
for (int i = 1; i <= m && 1ll * pr[j] * pr[j] <= w[i]; ++i) {
cc++;
k = w[i] / pr[j];
if (k <= sqr) k = id1[k];
else k = id2[n / k];
g[i] = g[i] - 1ll * pr[j] * (g[k] - sum[j - 1]) ;
}
}
ans = (1ll * g[1] % K + ((n + 2) % K) * ((n - 1) % K) % K * inv2 % K - 4 + K) % K;
printf("%d\n",ans);
}
return 0;
}
线性求逆元
#include<bits/stdc++.h>
using namespace std;
const int N=3e6+5;
int inv[N],n,p;
void getInv(int n) {
inv[1] = 1;
for(int i=2 i<=n;i++)
inv[i]=1ll*(p-p/i)*inv[p%i]%p;
}
int main() {
cin>>n>>p;
getInv(n);
for (int i=1;i<=n;i++)
printf("%d\n",inv[i]);
}
O(n)预处理O(1)求组合数
int fac[N], ifac[N];
void init() {
fac[0] = ifac[0] = fac[1] = ifac[1] = 1;
for (int i = 2; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
for (int i = 2; i < N; i++) ifac[i] = 1ll * (MOD - MOD / i) * ifac[MOD % i] % MOD;
for (int i = 2; i < N; i++) ifac[i] = 1ll * ifac[i - 1] * ifac[i] % MOD;
}
int C(int n, int m) {
if (n < m) return 0;
return 1ll * fac[n] * ifac[m] % MOD * ifac[n - m] % MOD;
}
CRT中国剩余定理
ll gcd(ll a, ll b) {
if (b == 0) return a;
return gcd(b, a % b);
}
ll exgcd(ll a, ll b, ll &x, ll &y)
{
// ax + by = gcd(a,b)
if(b == 0)
{
x = 1;
y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x); // ax1 + by1 = bx2 + (a - b(a/b))y2
y -= (a / b) * x; // ax1 + by1 = ay2 + b(x2 - (a/b)y2)
return d;
}
long long crt(vector<ll> &m, vector<ll> &a)
{
// x = m mod a
ll m1 = m[0], a1 = a[0];
for (int i = 1; i < m.size(); i++)
{
// a1y1 - a2y2 = m1 - m2
ll m2 = m[i], a2 = a[i];
ll g = gcd(a1, a2);
if ((m2 - m1) % g)
return -1;
ll y1, y2;
exgcd(a1, a2, y1, y2);
y1 *= (m1 - m2) / g;
y1 = ((y1 % (a2 / g)) + a2 / g) % (a2 / g);
// x = (m1 + a1y10) + tLCM
m1 -= a1 * y1;
a1 = (a1 / gcd(a1, a2)) * a2;
}
m1 = ((m1 % a1) + a1) % a1; //最小正整数解
if(m1 <= 0)
m1 += a1;
return m1;
}
Miller-Rabin
long long mult_mod(long long a,long long b,long long c)
{
a%=c;
b%=c;
long long ret=0;
while(b)
{
if(b&1){ret+=a;ret%=c;}
a<<=1;
if(a>=c)a%=c;
b>>=1;
}
return ret;
}
//计算 x^n %c
long long pow_mod(long long x,long long n,long long mod)//x^n%c
{
if(n==1)return x%mod;
x%=mod;
long long tmp=x;
long long ret=1;
while(n)
{
if(n&1) ret=mult_mod(ret,tmp,mod);
tmp=mult_mod(tmp,tmp,mod);
n>>=1;
}
return ret;
}
//以a为基,n-1=x*2^t a^(n-1)=1(mod n) 验证n是不是合数
//一定是合数返回true,不一定返回false
bool check(long long a,long long n,long long x,long long t)
{
long long ret=pow_mod(a,x,n);
long long last=ret;
for(int i=1;i<=t;i++)
{
ret=mult_mod(ret,ret,n);
if(ret==1&&last!=1&&last!=n-1) return true;//合数
last=ret;
}
if(ret!=1) return true;
return false;
}
// Miller_Rabin()算法素数判定
//是素数返回true.(可能是伪素数,但概率极小)
//合数返回false;
bool Miller_Rabin(long long n)
{
if(n<2)return false;
if(n==2)return true;
if((n&1)==0) return false;//偶数
long long x=n-1;
long long t=0;
while((x&1)==0){x>>=1;t++;}
for(int i=0;i<S;i++)
{
long long a=rand()%(n-1)+1;//rand()需要stdlib.h头文件
if(check(a,n,x,t))
return false;//合数
}
return true;
}
二次剩余
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll w;
struct num{
ll x,y;
};
num mul(num a,num b,ll p)
{
num ans={0,0};
ans.x=((a.x*b.x%p+a.y*b.y%p*w%p)%p+p)%p;
ans.y=((a.x*b.y%p+a.y*b.x%p)%p+p)%p;
return ans;
}
ll powwR(ll a,ll b,ll p){
ll ans=1;
while(b){
if(b&1)ans=1ll*ans%p*a%p;
a=a%p*a%p;
b>>=1;
}
return ans%p;
}
ll powwi(num a,ll b,ll p){
num ans={1,0};
while(b){
if(b&1)ans=mul(ans,a,p);
a=mul(a,a,p);
b>>=1;
}
return ans.x%p;
}
ll solve(ll n,ll p)
{
n%=p;
if(p==2)return n;
if(powwR(n,(p-1)/2,p)==p-1)return -1;//不存在
ll a;
while(1)
{
a=rand()%p;
w=((a*a%p-n)%p+p)%p;
if(powwR(w,(p-1)/2,p)==p-1)break;
}
num x={a,1};
return powwi(x,(p+1)/2,p);
}
int main()
{
srand(time(0));
int t;
scanf("%d",&t);
while(t--)
{
ll n,p;
scanf("%lld%lld",&n,&p);
if(!n){
printf("0\n");continue;
}
ll ans1=solve(n,p),ans2;
if(ans1==-1)printf("Hola!\n");
else
{
ans2=p-ans1;
if(ans1>ans2)swap(ans1,ans2);
if(ans1==ans2)printf("%lld\n",ans1);
else printf("%lld %lld\n",ans1,ans2);
}
}
}
行列式计算/生成树计数
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int MOD=998244353,N=1e2+5;
LL a[N][N],ans;
int T,n,m,sign;
int M[N][N],M2[N][N];
struct Edge{
int u,v,w;
}e[10005];
LL detVal(int n){
sign=0;LL ans=1;
for(int i=1;i<=n;i++){
for(int j=i+1; j<=n; j++){//当前之后的每一行第一个数要转化成0
int x=i,y=j;
while(a[y][i]){//利用gcd的方法,不停地进行辗转相除
LL t=a[x][i]/a[y][i];
for(int k=i; k<n; k++)
a[x][k]=(a[x][k]-a[y][k]*t)%MOD;
swap(x, y);
}
if(x!=i){//奇数次交换,则D=-D'整行交换
for(int k=1;k<n;k++)
swap(a[i][k],a[x][k]);
sign^= 1;//行列式*-1
}
}
if(!a[i][i]){//斜对角中有一个0,则结果为0
return 0;
}
else ans=ans*a[i][i]%MOD;
}
if(sign!=0) ans*=-1;
if(ans<0) ans+=MOD;
return ans;
}
int main(){
cin>>T;
while(T--) {
scanf("%d%d",&n,&m);
memset(M,0,sizeof(M));
for (int i=1;i<=m;i++) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
e[i].u=u;e[i].v=v;e[i].w=w;
}
memset(a,0,sizeof(a));
for (int k=1;k<=m;k++) {
int i=e[k].u,j=e[k].v;
a[i][i]++;a[j][j]++;
a[i][j]--;a[j][i]--;
}
LL tot=detVal(n);
tot=bin(tot,MOD-2);
ans=0;
for (int i=0;i<=30;i++)
{
memset(M2,0,sizeof(M2));
memset(a,0,sizeof(a));
for (int p=1;p<=m;p++) {
if (e[p].w&(1<<i)) {
int j=e[p].u,k=e[p].v;
a[j][j]++;a[k][k]++;
a[j][k]--;a[k][j]--;
}
}
LL cur=detVal(n-1);
ans=(ans+(1ll*cur*tot)%M(1<<i))%MOD;
}
printf("%lld\n",ans);
}
return 0;
}
点和向量旋转
//任意点(x,y),绕一个坐标点(rx0,ry0)逆时针旋转a角度后的新的坐标设为(x0, y0),有公式:
// x0= (x-rx0)*cos(a)-(y-ry0)*sin(a)+rx0 ;
// y0= (x-rx0)*sin(a)+(y-ry0)*cos(a)+ry0 ;
//任意向量(x,y),绕一个坐标点(rx0,ry0)逆时针旋转a角度后的新的向量设为(x0, y0),有公式:
// x0= x*cos(a)-y*sin(a);
// y0= x*sin(a)+y*cos(a);
void Rotate(double &x,double &y,double &vx,double &vy,double x0,double y0,double a)
{
double x_,vx_,y_,vy_;
x_=(x-x0)*cos(a)-(y-y0)*sin(a)+x0;
vx_=vx*cos(a)-vy*sin(a);
y_=(x-x0)*sin(a)+(y-y0)*cos(a)+y0;
vy_=vx*sin(a)+vy*cos(a);
x=x_,y=y_,vx=vx_,vy=vy_;
}
求直线交点、中垂线
#include<bits/stdc++.h>
#define debug(x) cerr<<#x<<" : "<<x<<endl;
using namespace std;
const int MAXN=2e3+5;
const double eps=1e-6;
class Point {
public:
double x,y;
Point(double a=0,double b=0):x(a),y(b){}
};
class Line {
public:
bool ifk;
double k,b;
//求过两点的直线
void mkline (Point A,Point B) {
ifk=true;k=b=0;
if (A.x==B.x) {
ifk=0;
b=A.x;
} else {
if (A.y==B.y) {
k=0;
// b=(A.x+B.x)/2;
b=A.y;
} else {
k=(B.y-A.y)/(B.x-A.x);
b=A.y-k*A.x;
}
}
}
//求两点中垂线
void mkbisector (Point A,Point B) {
ifk=true;k=b=0;
if (A.x==B.x) {
k=0;
b=(A.y+B.y)/2;
} else if (A.y==B.y) {
ifk=0;
b=(A.x+B.x)/2;
} else {
k=-1/(B.y-A.y)*(B.x-A.x);
b=(A.y+B.y)/2-k*(A.x+B.x)/2;
}
}
bool operator == (Line &T) {
return (ifk==T.ifk) && fabs(k-T.k)<eps && fabs(b-T.b)<eps;
}
};
//求两直线交点
struct Intersection {
bool parallel,coincide;
double x,y;
bool operator < (const Intersection &T) const {
if (fabs(x-T.x)>eps) return x<T.x;
else if (fabs(y-T.y)>eps) return y<T.y;
else return 0;
}
};
Intersection getIntersection (Line A,Line B) {
Intersection ans;
ans.parallel=ans.coincide=0;
ans.x=ans.y=0;
if (A==B) {
ans.coincide=1;
return ans;
}
if (!A.ifk && !B.ifk || A.ifk && B.ifk && fabs(A.k-B.k)<eps) {
ans.parallel=1;
return ans;
}
if (!A.ifk && B.ifk) return getIntersection(B,A);
if (A.ifk && !B.ifk) {
ans.x=B.b;
ans.y=A.k*ans.x+A.b;
} else {
ans.x=(B.b-A.b)/(A.k-B.k);
ans.y=A.k*ans.x+A.b;
}
return ans;
}
Point p[MAXN];
Line l[MAXN];
int n,m,ans;
map<Intersection,int>CNT;
int main()
{
cin>>n;
Point ori(0,0);
for (int i=1;i<=n;i++)
{
cin>>p[i].x>>p[i].y;
l[i].mkbisector(ori,p[i]);
}
ans=1;
for (int i=1;i<=n-1;i++)
{
CNT.clear();
for (int j=i+1;j<=n;j++)
{
Intersection inter=getIntersection(l[i],l[j]);
if (!inter.coincide && !inter.parallel) CNT[inter]++;
}
for (auto it:CNT) ans=max(ans,it.second+1);
}
printf("%d\n",ans);
return 0;
}
算法
CDQ分治
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+5;
struct BIT{
int tr[N];
int lowbit(int x){ return x&(-x); }
void clr(int x){
while(x<N)
{
tr[x]=0;
x+=lowbit(x);
}
}
void add(int v,int x){
while(x<N)
{
tr[x]+=v;
x+=lowbit(x);
}
}
int sum(int x){
int sum=0;
while(x>0)
{
sum+=tr[x];
x-=lowbit(x);
}
return sum;
}
}bit;
struct node{
int id,x,y,z;
bool operator==(node &t){
return x==t.x && y==t.y && z==t.z;
}
}A[N],tmp[N];
int n,k,ans[N],cnt[N];
void CDQ(int l,int r)
{
if (l==r) return;
int mid=(l+r)>>1;
CDQ(l,mid);
CDQ(mid+1,r);
int p1=l,p2=mid+1,pt=l;
while(p1!=mid+1 || p2!=r+1)
{
if (p1==mid+1 || A[p2].y<A[p1].y && p2!=r+1)
{
ans[A[p2].id]+=bit.sum(A[p2].z);
tmp[pt++]=A[p2];
p2++;
}
else
{
bit.add(1,A[p1].z);
tmp[pt++]=A[p1];
p1++;
}
}
for (int i=l;i<=mid;i++) bit.clr(A[i].z);
for (int i=l;i<=r;i++) A[i]=tmp[i];
}
bool cmp(node &a,node &b){
if (a.x==b.x && a.y==b.y)
return a.z<b.z;
else if(a.x==b.x) return a.y<b.y;
else return a.x<b.x;
}
int main()
{
cin>>n>>k;
for (int i=1;i<=n;i++)
{
scanf("%d%d%d",&A[i].x,&A[i].y,&A[i].z);
A[i].id=i;
}
sort(A+1,A+1+n,cmp);
int same=0;
for (int i=n;i>=2;i--)
{
if (A[i]==A[i-1]) ans[A[i-1].id]+=++same;
else same=0;
}
CDQ(1,n);
for (int i=1;i<=n;i++) cnt[ans[i]]++;
for (int i=0;i<n;i++) printf("%d\n",cnt[i]);
return 0;
}
最长递增子序列(LIS)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000+5,INF=0x3f3f3f3f;
int T,n,m,A[N],dp[N],ans;
int check(int l,int r)
{
for (int i=l;i<r;i++)
{
int j=lower_bound(dp,dp+n,A[i])-dp;
dp[j]=A[i];
}
return n-(lower_bound(dp,dp+n,INF)-dp);
}
int main()
{
cin>>n;
for (int i=0;i<n;i++)
{
scanf("%d",&A[i]);
A[n+i]=A[i];
}
ans=INF;
for (int k=0;k<n;k++)
{
memset(dp,INF,sizeof(dp));
ans=min(ans,check(k,k+n));
}
cout<<ans;
return 0;
}
带修莫队
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 5, M = 1e6 + 5;
int n, m;
int a[N], cnt[M], cntq, cntp, pos[N], ans[N], now;
char op;
struct Query{
int l, r, t, id;
bool operator < (Query &Q) {
if (pos[l] != pos[Q.l]) return l < Q.l;
if (pos[r] != pos[Q.r]) return r < Q.r;
return t < Q.t;
}
}q[N];
struct Modify{
int pos, bef, aft;
}p[N];
void inc(int x) {
if (++cnt[x] == 1) now++;
}
void del(int x) {
if (!(--cnt[x])) now--;
}
int main() {
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1, u, v; i <= m; i++) {
op = getchar();
while(op != 'Q' && op != 'R') op = getchar();
scanf("%d%d", &u, &v);
if (op == 'Q') {
cntq++;
q[cntq].l = u;
q[cntq].r = v;
q[cntq].t = cntp;
q[cntq].id = cntq;
} else {
cntp++;
p[cntp].pos = u;
p[cntp].bef = a[u];
p[cntp].aft = v;
a[u] = v;
}
}
LL block = pow(1ll * n, 2.0 / 3.0);
for (int i = 1; i <= n; i++) pos[i] = i / block;
sort(q + 1, q + 1 + cntq);
int l = 1, r = 0, t = cntp;
for (int i = 1; i <= cntq; i++) {
int nl = q[i].l, nr = q[i].r, nt = q[i].t;
while(t < nt) {
t++;
int pos = p[t].pos, bef = p[t].bef, aft = p[t].aft;
if (pos >= l && pos <= r) {
cnt[bef]--;
if (cnt[bef] == 0) now--;
cnt[aft]++;
if (cnt[aft] == 1) now++;
}
a[pos] = aft;
}
while(t > nt) {
int pos = p[t].pos, bef = p[t].bef, aft = p[t].aft;
if (pos >= l && pos <= r) {
cnt[aft]--;
if (cnt[aft] == 0) now--;
cnt[bef]++;
if (cnt[bef] == 1) now++;
}
a[pos] = bef;
t--;
}
while(l < nl) del(a[l++]);
while(l > nl) inc(a[--l]);
while(r < nr) inc(a[++r]);
while(r > nr) del(a[r--]);
ans[q[i].id] = now;
}
for (int i = 1; i <= cntq; i++) printf("%d\n", ans[i]);
return 0;
}
威佐夫博弈
ak =[k(1+√5)/2],bk= ak + k (k=0,1,2,...,n 方括号表示取整函数)
树上莫队(带修)
#include <bits/stdc++.h>
#define debug(x) cerr << #x << " = " << x << " "
#define debugl(x) cerr << #x << " = " << x << endl
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n, M, Q, v[N], w[N], c[N];
int f[N], g[N], id[N << 1], pos[N << 1], IDX, TIME, CNT;
int dep[N], pre[N][25], pow_2[25], cnt[N];
bool vis[N];
ll ans, aans[N];
struct Modify{
int pre, aft, pos;
}m[N];
struct Query{
int x, y, t, id;
}q[N];
bool cmp(Query Q_, Query Q) {
if (pos[Q_.x] != pos[Q.x]) return pos[Q_.x] < pos[Q.x];
if (pos[Q_.y] != pos[Q.y]) return pos[Q_.y] < pos[Q.y];
return Q_.t < Q.t;
}
vector<int> G[N];
void dfs(int x, int ff) {
f[x] = ++IDX;
id[IDX] = x;
for (int to : G[x]) {
if (to == ff) continue;
dep[to] = dep[x] + 1;
pre[to][0] = x;
dfs(to, x);
}
g[x] = ++IDX;
id[IDX] = x;
}
int LCA(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
int gap = dep[x] - dep[y];
for (int i = 20; i >= 0; i--) {
if (gap >= pow_2[i]) {
gap -= pow_2[i];
x = pre[x][i];
}
}
if (x == y) return x;
for (int i = 20; i >= 0; i--) {
if (pre[x][i] != pre[y][i]) {
x = pre[x][i];
y = pre[y][i];
}
}
return pre[x][0];
}
void add(int pos) {
vis[pos] ^= 1;
if (vis[pos]) {
cnt[c[pos]]++;
ans = ans + 1ll * w[cnt[c[pos]]] * v[c[pos]];
}
else {
ans = ans - 1ll * w[cnt[c[pos]]] * v[c[pos]];
cnt[c[pos]]--;
}
}
int main() {
cin >> n >> M >> Q;
for (int i = 1; i <= M; i++) scanf("%d", &v[i]);
for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
for (int i = 1; i <= n - 1; i++) {
int a, b;
scanf("%d %d", &a, &b);
G[a].push_back(b);
G[b].push_back(a);
}
for (int i = 1; i <= n; i++) scanf("%d", &c[i]);
pow_2[0] = 1;
for (int i = 1; i <= 20; i++) pow_2[i] = pow_2[i - 1] * 2;
dep[1] = 1;
dfs(1, -1);
int block = pow(IDX, 2.0 / 3);
for (int i = 1; i <= IDX; i++) {
pos[i] = (i - 1) / block + 1;
}
for (int i = 1; i <= 20; i++) {
for (int j = 1; j <= n; j++) pre[j][i] = pre[pre[j][i - 1]][i - 1];
}
for (int i = 1; i <= Q; i++) {
int t, x, y;
scanf("%d %d %d", &t, &x, &y);
if (t == 0) {
m[++TIME] = {c[x], y, x};
c[x] = y;
} else {
if (f[x] > f[y]) swap(x, y);
q[++CNT] = {LCA(x, y) == x ? f[x] : g[x], f[y], TIME, CNT};
}
}
sort(q + 1, q + 1 + CNT, cmp);
int l = 0, r = 0, t = TIME;
for (int i = 1; i <= CNT; i++) {
while(t < q[i].t) {
t++;
if (vis[m[t].pos]) {
int pc = m[t].pre;
int ac = m[t].aft;
ans -= 1ll * v[pc] * w[cnt[pc]];
cnt[pc]--;
cnt[ac]++;
ans += 1ll * v[ac] * w[cnt[ac]];
}
c[m[t].pos] = m[t].aft;
}
while(t > q[i].t) {
if (vis[m[t].pos]) {
int pc = m[t].pre;
int ac = m[t].aft;
ans -= 1ll * v[ac] * w[cnt[ac]];
cnt[ac]--;
cnt[pc]++;
ans += 1ll * v[pc] * w[cnt[pc]];
}
c[m[t].pos] = m[t].pre;
t--;
}
while(r < q[i].y) r++, add(id[r]);
while(l > q[i].x) l--, add(id[l]);
while(r > q[i].y) add(id[r]), r--;
while(l < q[i].x) add(id[l]), l++;
int lca = LCA(id[l], id[r]);
if (lca != id[l] && lca != id[r]) {
add(lca);
aans[q[i].id] = ans;
add(lca);
} else aans[q[i].id] = ans;
}
for (int i = 1; i <= CNT; i++) printf("%lld\n", aans[i]);
return 0;
}
异或最小生成树
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
const int INF=0x3f3f3f3f;
int l[N<<5],r[N<<5];
struct Edge{
int t,w;
};
int root,num;
vector<Edge>G[N];
vector<int>P;
int u,v,w,n;
ll tot;
void dfs(int x,int fa,int now) {
P.push_back(now);
for (auto to:G[x]) {
if (to.t!=fa)
dfs(to.t,x,now^to.w);
}
}
void MakeTrie(int subroot,int check,int x) {
if (check==0) return;
if ((x&check)==0) {
if (l[subroot]==0) l[subroot]=++num;
MakeTrie(l[subroot],check>>1,x);
}
else {
if (r[subroot]==0) r[subroot]=++num;
MakeTrie(r[subroot],check>>1,x);
}
}
ll dfs3(int a,int b,int check) {
if (!a || !b) return INF;
if (check==0) return 0;
ll t1,t2;
t1=min(dfs3(l[a],l[b],check>>1),dfs3(r[a],r[b],check>>1));
if (t1==INF)
t2=check+min(dfs3(l[a],r[b],check>>1),dfs3(r[a],l[b],check>>1));
else t2=INF;
return min(t1,t2);
}
ll dfs2(int subroot,int check) {
ll ret=0;
if (l[subroot]) ret+=dfs2(l[subroot],check>>1);
if (r[subroot]) ret+=dfs2(r[subroot],check>>1);
if (l[subroot] && r[subroot]) {
int t1=l[subroot],t2=r[subroot];
ret+=check+dfs3(t1,t2,check>>1);
}
return ret;
}
int main() {
cin>>n;
for (int i=1;i<=n-1;i++) {
scanf("%d%d%d",&u,&v,&w);
G[u].push_back({v,w});
G[v].push_back({u,w});
}
dfs(0,-1,0);
sort(P.begin(),P.end());
P.erase(unique(P.begin(),P.end()),P.end());
for (auto x:P) {
MakeTrie(0,1<<30,x);
}
printf("%lld\n",dfs2(0,1<<30));
return 0;
}
字符串
KMP
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct KMP{
int nxt[N];
void getNext(string &t) {
nxt[0]=-1;
for (int i=1,j=-1;i<=t.length();i++) {
while(j!=-1 && t[i-1]!=t[j]) j=nxt[j];
nxt[i]=++j;
}
}
void getAns(string &s,string &t) {
int len=t.length();
for (int i=0,j=0;i<s.length();) {
if (j==-1 || s[i]==t[j]) i++,j++;
else j=nxt[j];
if (j==len) printf("%d\n",i-j+1),j=nxt[j];
}
}
}kmp;
string s,t;
int main() {
cin>>s;
cin>>t;
kmp.getNext(t);
kmp.getAns(s,t);
for (int i=1;i<=t.length();i++) printf("%d ",kmp.nxt[i]);
}
后缀数组SA
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e6+5;
int n;
char S[MAXN];
int s[MAXN];
int last[2];
//char s[MAXN];
struct SA {
int sa[MAXN], ra[MAXN], height[MAXN];
int t1[MAXN], t2[MAXN], c[MAXN];
void build(int *str, int n, int m) {
str[n] = 0;
n++;
int i, j, p, *x = t1, *y = t2;
for (i = 0; i < m; i++) c[i] = 0;
for (i = 0; i < n; i++) c[x[i] = str[i]]++;
for (i = 1; i < m; i++) c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
for (j = 1; j <= n; j <<= 1) {
p = 0;
for (i = n - j; i < n; i++) y[p++] = i;
for (i = 0; i < n; i++) if (sa[i] >= j) y[p++] = sa[i] - j;
for (i = 0; i < m; i++) c[i] = 0;
for (i = 0; i < n; i++) c[x[y[i]]]++;
for (i = 1; i < m; i++) c[i] += c[i - 1];
for (i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i];
swap(x, y);
p = 1;
x[sa[0]] = 0;
for (i = 1; i < n; i++)
x[sa[i]] = (y[sa[i - 1]] == y[sa[i]] && y[sa[i - 1] + j] == y[sa[i] + j]) ? p - 1 : p++;
if (p >= n) break;
m = p;
}
n--;
for (int i = 0; i <= n; i++) ra[sa[i]] = i;
for (int i = 0, j = 0, k = 0; i <= n; i++) {
if (k) k--;
j = sa[ra[i] - 1];
while (str[i + k] == str[j + k]) k++;
height[ra[i]] = k;
}
st_init(height, n);
}
int lg[MAXN], table[23][MAXN];
void st_init(int *arr, int n) {
if (!lg[0]) {
lg[0] = -1;
for (int i = 1; i < MAXN; i++)
lg[i] = lg[i / 2] + 1;
}
for (int i = 1; i <= n; ++i)
table[0][i] = arr[i];
for (int i = 1; i <= lg[n]; ++i)
for (int j = 1; j <= n; ++j)
if (j + (1 << i) - 1 <= n)
table[i][j] = min(table[i - 1][j], table[i - 1][j + (1 << (i - 1))]);
}
int lcp(int l, int r) {
l = ra[l], r = ra[r];
if (l > r) swap(l, r);
++l;
int t = lg[r - l + 1];
return min(table[t][l], table[t][r - (1 << t) + 1]);
}
} sa;
/*bool cmp(int i, int j) {
if (dis[i] != dis[j]) return dis[i] < dis[j];
if (i + dis[i] > n) return true;
if (j + dis[j] > n) return false;
int lcp = sa.lcp(i + dis[i], j + dis[j]);
return a[i + lcp + dis[i]] < a[j + lcp + dis[j]];
}*/
int main(){
while(scanf("%d",&n)!=-1)
{
scanf("%s",S);
last[0]=last[1]=0;
s[n]=n+1;
for (int i=n-1;i>=0;i--)
{
int now= S[i]=='a' ? 0 : 1;
if (last[now]==0) s[i]=n;
else s[i]=(last[now]-i);
last[now]=i;
}
n++;
sa.build(s,n,n+2);
for(int i = n-1;i >=1 ;i--)
printf("%d ",sa.sa[i]+1);
printf("\n");
}
return 0;
}