【2021省选前夕总结】
网络流
DinIc:
queue<int>QWQ;
ll bfs(){
flow = 0;
memset(minn,127,sizeof(minn));
memset(pre,0,sizeof(pre));
memset(vis,0,sizeof(vis));
QWQ.push(s);
while(!QWQ.empty()){
ll now = QWQ.front();
//std::cout<<now<<std::endl;
QWQ.pop();
if(now == t)
break;
for(int i = head[now];i;i = e[i].next){
ll vi = e[i].to;
//std::cout<<vi<<" ";
if(!vis[vi] && e[i].v && vi != pre[now]){
QWQ.push(vi);
pre[vi] = now;
minn[vi] = std::min(minn[now],e[i].v);
vis[vi] = 1;
}
}
//puts("");
}
while(!QWQ.empty())
QWQ.pop();
if(!vis[t])
return 0;
ll now = t,cut = minn[t];
// std::cout<<cut<<std::endl;
// puts("");
while(now != s){
for(int i = head[now];i;i = e[i].next){
if(e[i].to == pre[now]){
e[i].v += cut;
e[i ^ 1].v -= cut;
break;
}
}
now = pre[now];
}
return flow = cut;
}
EK费用流
bool spfa(){
std::queue<int>QWQ;
for(int i = 1;i <= n;++i){
minc[i] = 0x3f3f3f3f,vis[i] = 0;
}
QWQ.push(s);
vis[s] = 1;
minw[s] = 0x3f3f3f3f;
minc[s] = 0;
while(!QWQ.empty()){
int now = QWQ.front();
QWQ.pop();
vis[now] = 0;
for(int i = head[now];i;i = e[i].next){
if(e[i].v){
int v = e[i].to;
if(minc[v] > minc[now] + e[i].c){
minc[v] = minc[now] + e[i].c;
pre[v] = i;
minw[v] = std::min(minw[now],e[i].v);
if(!vis[v])vis[v] = 1,QWQ.push(v);
}
}
}
}
return minc[t] != 0x3f3f3f3f;
}
int answ,ansc;
void up(){
int now = t;
answ += minw[t];
ansc += minc[t] * minw[t];
while(now != s){
e[pre[now]].v -= minw[t];
e[pre[now] ^ 1].v += minw[t];
now = e[pre[now] ^ 1].to;
}
}
\(tarjan缩点\ 30min\)
每天一个抱铃小技巧:low[u] == std::min(low[u],dfn[v]);
这个居然不会报错
割点:仿照强连通分量,判断条件改为 low[v]>=dfn[u]
(v是搜索树上新根)
割边:仿照强连通分量,判断条件改为 low[v]>dfn[u]
无向图注意由于是维护的搜索树,不要搜到父亲去,割点时,如果搜索树根只是一个儿节点(搜索树上的),那么要强制把他设为非割点。
数据结构
胆大心细,暴力操作。
平衡树/线段树五问:
1.每个节点需要记录那些信息
2.需要那些标记
3.下传标记怎么做
4.区间整体修改怎么搞
5.如何合并区间
Fhq_treap
ll ch[A][2],val[A],cv[A],siz[A],cnt;
#define l(x) ch[x][0]
#define r(x) ch[x][1]
#define v(x) val[x]
#define c(x) cv[x]
#define s(x) siz[x]
void up(ll x){s(x) = 1 + s(l(x)) + s(r(x));}
ll randoom(){return rand() << 15 | rand();}
ll newcode(ll x){s(++cnt) = 1,v(cnt) = x,c(cnt) = randoom();return cnt;}
void split(ll now,ll k,ll &x,ll &y){
if(!now){x = y = 0;return;}
if(v(now) <= k) x = now,split(r(now),k,r(now),y);
else
y = now,split(l(now),k,x,l(now));
up(now);
}
ll merge(ll x,ll y){
if(!x || !y)return x + y;
if(c(x) < c(y)){
r(x) = merge(r(x),y);
up(x);return x;
}
else{
l(y) = merge(x,l(y));
up(y);return y;
}
}
ll root,x,y,z,cn;
void insert(ll a){
cn ++ ;
split(root,a,x,y);
root = merge(merge(x,newcode(a)),y);
}
void del(ll a){
cn -- ;
split(root,a,x,z);
split(x,a - 1,x,y);
y = merge(l(y),r(y));
root = merge(x,merge(y,z));
}
ll find(ll a){
split(root,a - 1,x,y);
ll ans = s(x) + 1;
root = merge(x,y);
return ans;
}
ll kth(ll now,ll k){
if(k <= s(l(now)))return kth(l(now),k);
else
if(k == s(l(now)) + 1)return now;
else
return kth(r(now),k - s(l(now)) - 1);
}
主席树:
ll build(ll l,ll r){
ll now = ++cnt;
if(l == r){
tree[now].va = 0;
return now;
}
tree[now].l = build(l,mid);
tree[now].r = build(mid + 1,r);
return now;
}
ll up(ll now){
tree[now].va = tree[tree[now].l].va + tree[tree[now].r].va;
}
ll update(ll pre,ll l,ll r,ll k){
ll now = ++cnt;
tree[now].l = tree[pre].l;
tree[now].r = tree[pre].r;
tree[now].va = tree[pre].va + 1;
if(l < r)
if(k <= mid)
tree[now].l = update(tree[pre].l,l,mid,k);
else
tree[now].r = update(tree[pre].r,mid + 1,r,k);
return now;
}
ll query(ll u,ll v,ll l,ll r,ll k){
if(l >= r) return l;
//std::cout<<u<<" "<<v<<" "<<tree[tree[v].l].va<<" "<<tree[tree[u].l].va<<std::endl;
ll x = tree[tree[v].l].va - tree[tree[u].l].va;
if(x >= k) return query(tree[u].l,tree[v].l,l,mid,k);
else return query(tree[u].r,tree[v].r,mid + 1,r,k - x);
} //静态区间kth
线段树合并:
int merge(int a,int b,int x,int y) {
if(!a) return b;if(!b) return a;
if(x==y) {d[a]+=d[b];t[a]=x;return a;}
int mid=x+y>>1;
l[a]=merge(l[a],l[b],x,mid),r[a]=merge(r[a],r[b],mid+1,y);
pushup(a);return a;
}//破坏结构,省空间,离线
int merge(int a,int b,int x,int y) {
if(!a) return b;if(!b) return a;
int root=++cnt;
if(x==y) {d[root]=d[a]+d[b];t[root]=x;return root;}
int mid=x+y>>1;
l[root]=merge(l[a],l[b],x,mid),r[root]=merge(r[a],r[b],mid+1,y);
pushup(root);return root;
}//类似于主席树,炸空间
cdq分治:
struct P{
ll x,y,z,id;
}e[N];
ll ans[N],unq[N],f[N];
ll t[N * 2];
ll n,k;
void add(ll x,ll p){
for(int i = x;i <= k;i += lowbit(i))
t[i] += p;
}
int get(ll x){
ll ans = 0;
for(int i = x;i;i -= lowbit(i))
ans += t[i];
return ans;
}
bool cmp(P t,P s){
if(t.x != s.x)
return t.x < s.x;
if(t.y != s.y)
return t.y < s.y;
return t.z < s.z;
}
bool cmp1(P t,P s){
if(t.y != s.y)
return t.y < s.y;
if(t.z != s.z)
return t.z < s.z;
return t.x < s.x;
}
void cdq(ll l,ll r){
if(l == r)
return;
int mid = (l + r) >> 1;
cdq(l,mid),cdq(mid + 1,r);
std::sort(e + l,e + r + 1,cmp1);
for(int i = l;i <= r;++i)
if(e[i].x <= mid)
add(e[i].z,1);
else
ans[e[i].id] += get(e[i].z);
for(int i = l;i <= r;++i){
if(e[i].x <= mid)
add(e[i].z,-1);
// printf("%lld %lld\n",e[i].x,ans[e[i].id]);
}
}
int main(){
scanf("%lld%lld",&n,&k);
for(int i = 1;i <= n;++i)
scanf("%lld%lld%lld",&e[i].x,&e[i].y,&e[i].z),e[i].id = i;
std::sort(e + 1,e + n + 1,cmp);
for(int i = 1,j;i <= n;){
j = i + 1;
while(j <= n && e[j].x == e[i].x && e[j].y == e[i].y && e[j].z == e[i].z)
j ++ ;
while(i < j)
unq[e[i].id] = e[j - 1].id,i ++ ;
}
for(int i = 1;i <= n;++i)
e[i].x = i;
cdq(1,n);
for(int i = 1;i <= n;++i)
f[ans[unq[e[i].id]]] ++ ;
for(int i = 0;i < n;++i)
printf("%lld\n",f[i]);
}
01tire什么的就不写的,到时候复习一点笔记好了
数论
exgcd
LL exgcd(LL a,LL b,LL &x,LL &y){
LL d=a; if(b==0) x=1,y=0; else{
d=exgcd(b,a%b,y,x),y-=a/b*x;
}
return d;
}
筛法:
mu[1] = 1;
for(int i = 2;i <= 10000000;++i){
if(!flag[i]) prime[++cnt] = i,mu[i] = -1,phi[i] = i - 1;
for(int j = 1;j <= cnt && i * prime[j] <= 10000000;++j){
flag[i * prime[j]] = 1;
if(i % prime[j] == 0){phi[i * prime[j]] = phi[i] * prime[j];break;}
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
mu[i * prime[j]] = -mu[i];
}
}
杜教筛
到时候看博客
字符串
马拉车
s[0] = '~';
a = getchar();
while(a <= 'z' && a >= 'a'){
s[++len] = '|';
s[++len] = a;
a = getchar();
}
s[++len] = '|';
for(int i = 1;i <= len;++i){
if(m + ans[m] >= i)
ans[i] = std::min(m + ans[m] - i,ans[m * 2 - i]);
while(s[i - ans[i]] == s[i + ans[i]]) ans[i] ++ ;
if(ans[i] + i > m + ans[m])m = i;
if(fans < ans[i])
fans = ans[i];
}
std::cout<<fans - 1;
最小表示法
scanf("%lld",&n);
for(int i = 1;i <= n;++i)
scanf("%lld",&a[i]);
for(int i = n + 1;i <= 2 * n;++i)
a[i] = a[i - n];
int j = 1,i = 2,k,tmp;
while(i <= n){
k = 0;
while(a[i + k] == a[j + k])
k += 1;
if(a[i + k] < a[j + k])
tmp = i,i = std::max(i + 1,j + k + 1),j = tmp;
else
i = i + k + 1;
}
for(int l = 1;l <= n;++l)
std::cout<<a[j + l - 1]<<" ";
AC自动机
ll n,cnt;
int to[1000005][50],end[1000005],fail[1000005];
char a[1000005];
void insert(){
ll now = 0;
ll n = strlen(a + 1);
for(int i = 1;i <= n;++i){
if(!to[now][a[i] - 'a'])
to[now][a[i] - 'a'] = ++ cnt ;
now = to[now][a[i] - 'a'];
}
end[now] ++ ;
}
std::queue<int> QWQ ;
void get(){
for(int i = 0;i < 26;++i)
if(to[0][i]) fail[to[0][i]] = 0,QWQ.push(to[0][i]);
while(! QWQ.empty()){
ll u = QWQ.front();QWQ.pop();
for(int i = 0;i < 26;++i)
if(to[u][i]) fail[to[u][i]] = to[fail[u]][i],QWQ.push(to[u][i]);
else to[u][i] = to[fail[u]][i];
}
}
int query(){
ll len = strlen(a + 1),now = 0,ans = 0;
for(int i = 1;i <= len;++i){
now = to[now][a[i] - 'a'];
for(int t = now;t && end[t] != -1;t = fail[t]) ans += end[t],end[t] = -1;
}
return ans;
}
SA:
const int maxn = 1e6 + 5;
int rk[maxn], RK[maxn];
int sa[maxn], SA[maxn];
int bac[maxn], n, h[maxn], height[maxn];
int rmq[maxn][18], bin[1 << 18];
char str[maxn];
void getsa(char *str, int n, int alp = 256) {
for (int i = 0; i <= alp; i++) bac[i] = 0;
for (int i = 1; i <= n; i++) ++bac[str[i]];
for (int i = 1; i <= alp; i++) bac[i] += bac[i - 1];
for (int i = 1; i <= n; i++) sa[bac[str[i]]--] = i;
for (int i = 1; i <= n; i++) rk[sa[i]] = rk[sa[i - 1]] + (str[sa[i]] != str[sa[i - 1]]);
for (int p = 1; p <= n; p <<= 1)
{
for (int i = 1; i <= n; i++) bac[rk[sa[i]]] = i;
for (int i = n; i >= 1; i--) if (sa[i] > p) SA[bac[rk[sa[i] - p]]--] = sa[i] - p;
for (int i = n; i > n - p; i--) SA[bac[rk[i]]--] = i;
#define comp(x, y) (rk[x] != rk[y] || rk[x + p] != rk[y + p])
for (int i = 1; i <= n; i++) RK[SA[i]] = RK[SA[i - 1]] + comp(SA[i], SA[i - 1]);
for (int i = 1; i <= n; i++) sa[i] = SA[i], rk[i] = RK[i];
if (rk[sa[n]] >= n) return ;
}
}
void geth(char *str, int n) {
for (int i = 1; i <= n; i++)
{
int j = sa[rk[i] - 1], k = max(0, h[i - 1] - 1);
while (str[i + k] == str[j + k] && str[i + k]) ++k;
h[i] = height[rk[i]] = k;
}
for (int j = 0; j < 18; j++)
for (int i = 1 << j; i < (1 << j + 1); i++) bin[i] = j;
for (int i = 1; i <= n; i++) rmq[i][0] = height[i];
for (int j = 1; j < 18; j++)
for (int i = 1 << j; i <= n; i++)
rmq[i][j] = min(rmq[i - (1 << j - 1)][j - 1], rmq[i][j - 1]);
}
int lcp(int x, int y) {
int u = rk[x], v = rk[y];
if (u > v) swap(u, v);
int j = bin[v - u];
return min(rmq[v][j], rmq[u + (1 << j)][j]);
}
树上问题
树链剖分
ll n,m,root,p;
ll v[200000],head[200000];
ll cnt = 0;
void add(ll x,ll y){
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
int dep[200000],fa[200000],siz[200000],gson[200000];
int dfncnt,dfn[200000],top[200000],num[200000];
void dfs2(int u,int t){
top[u] = t;
dfn[u] = ++dfncnt;
num[dfncnt] = v[u];
if(!gson[u])return;
dfs2(gson[u],t);
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa[u] || v == gson[u])
continue;
dfs2(v,v);
}
}
void dfs(ll u,ll f){
fa[u] = f;
dep[u] = dep[f] + 1;
siz[u] = 1;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == f)
continue;
dfs(v,u);
siz[u] += siz[v];
if(siz[v] > siz[gson[u]])
gson[u] = v;
}
}
struct Seg{
struct E{
ll l,r,v,m;
}t[400000];
#define l(x) t[x].l
#define r(x) t[x].r
#define v(x) t[x].v
#define m(x) t[x].m
#define mid ((l + r) >> 1)
void up(ll now){v(now) = (v(l(now)) + v(r(now))) % p;}
void push(ll now,ll l,ll r){
m(l(now)) += m(now);
m(r(now)) += m(now);
v(l(now)) += m(now) * (mid - l + 1);
v(r(now)) += m(now) * (r - mid);
v(l(now)) %= p,m(l(now)) %= p;
v(r(now)) %= p,m(r(now)) %= p;
m(now) = 0;}
ll build(ll l,ll r){
ll now = ++cnt;
m(now) = 0;
if(l == r){
v(now) = num[l];
return cnt;
}
l(now) = build(l,mid);
r(now) = build(mid + 1,r);
up(now);
return now;
}
void change(ll now,ll l,ll r,ll nl,ll nr,ll s){
push(now,l,r);
if(nl <= l && r <= nr){
v(now) += (r - l + 1) * s;
m(now) += s;
v(now) %= p;
m(now) %= p;
return ;
}
if(nl <= mid)
change(l(now),l,mid,nl,nr,s);
if(nr > mid)
change(r(now),mid + 1,r,nl,nr,s);
up(now);
}
ll q(ll now,ll l,ll r,ll nl,ll nr){
push(now,l,r);
ll ans = 0;
if(nl <= l && r <= nr)
return v(now) % p;
if(nl <= mid)
ans = (ans + q(l(now),l,mid,nl,nr)) % p;
if(nr > mid)
ans = (ans + q(r(now),mid + 1,r,nl,nr)) % p;
up(now);
return ans;
}
}Q;
int main(){
scanf("%lld%lld%lld%lld",&n,&m,&root,&p);
for(int i = 1;i <= n;++i){
scanf("%lld",&v[i]);
}
for(int i = 1;i <= n - 1;++i){
ll x,y;
scanf("%lld%lld",&x,&y);
add(x,y);
add(y,x);
}
dfs(root,0);
dfs2(root,root);
cnt = 0;
int z = Q.build(1,n);
// for(int i = 1;i <= n;++i){
// std::cout<<fa[i]<<" "<<dep[i]<<" "<<top[i]<<" "<<dfn[i]<<" "<<siz[i]<<" "<<gson[i]<<std::endl;
// }
for(int i = 1;i <= m;++i){
ll opt,x,y,z;
scanf("%lld",&opt);
if(opt == 1){
scanf("%lld%lld%lld",&x,&y,&z);
z %= p;
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])std::swap(x,y);
Q.change(1,1,n,dfn[top[x]],dfn[x],z);
x = fa[top[x]];
}
if(dep[x] > dep[y]) std::swap(x,y);
Q.change(1,1,n,dfn[x],dfn[y],z);
}
if(opt == 2){
scanf("%lld%lld",&x,&y);
ll ans = 0;
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])std::swap(x,y);
ans = (ans + Q.q(1,1,n,dfn[top[x]],dfn[x])) % p;
x = fa[top[x]];
}
if(dep[x] > dep[y])
std::swap(x,y);
ans = (ans + Q.q(1,1,n,dfn[x],dfn[y])) % p;
std::cout<<ans % p<<std::endl;
}
if(opt == 3){
scanf("%lld%lld",&x,&z);
Q.change(1,1,n,dfn[x],dfn[x] + siz[x] - 1,z);
}
if(opt == 4){
scanf("%lld",&x);
std::cout<<Q.q(1,1,n,dfn[x],dfn[x] + siz[x] - 1) % p<<std::endl;
}
}
}//一个普通的维护路径权和的代码,还是写的不够快
虚树:
struct P{
ll to,next,v;
}e[M],e2[M];
ll cnt,head[N],dfn[N],idf[N],top[N],Minn[N],dep[N],fa[N],siz[N],son[N];
ll dcnt;
ll cnt2,head2[N],DP[N];
void add(ll x,ll y,ll v){
e[++cnt].to = y;
e[cnt].v = v;
e[cnt].next = head[x];
head[x] = cnt;
}
void add2(ll x,ll y){
e2[++cnt2].to = y;
e2[cnt2].next = head2[x];
head2[x] = cnt2;
}
void dfs1(ll now,ll f){
fa[now] = f;
dep[now] = dep[f] + 1;
siz[now] = 1;
for(int i = head[now];i;i = e[i].next){
ll v = e[i].to;
if(v != f){
Minn[v] = std::min(Minn[now],e[i].v);
dfs1(v,now);
siz[now] += siz[v];
if(siz[v] > siz[son[now]])
son[now] = v;
}
}
}
void dfs2(ll now,ll t){
top[now] = t;
dfn[now] = ++dcnt;
idf[dfn[now]] = now;
if(!son[now])
return;
dfs2(son[now],t);
for(int i = head[now];i;i = e[i].next){
ll v = e[i].to;
if(v == fa[now] || v == son[now])
continue;
dfs2(v,v);
}
}
ll lca(ll x,ll y){
while(top[x] != top[y]){
if(dep[top[x]] < dep[top[y]])
std::swap(x,y);
x = fa[top[x]];
}
if(dep[x] > dep[y])
std::swap(x,y);
return x;
}
ll stack[N];
void build(){
cnt2 = 0;
std::sort(stack + 1,stack + stack[0] + 1);
ll s = stack[0];
for(int i = 2;i <= s;++i)
stack[++stack[0]] = dfn[lca(idf[stack[i]],idf[stack[i - 1]])];
std::sort(stack + 1,stack + stack[0] + 1);
s = std::unique(stack + 1,stack + stack[0] + 1) - stack - 1;
for(int i = 2;i <= s;++i)
add2(lca(idf[stack[i]],idf[stack[i - 1]]),idf[stack[i]]);
}
bool qr[N];
void dfs(ll now){
ll s = 0;
DP[now] = Minn[now];
for(int i = head2[now];i;i = e2[i].next){
ll v = e2[i].to;
dfs(v);
s += DP[v];
}
if(s && !qr[now])
DP[now] = std::min(s,DP[now]);
else
qr[now] = 0;
head2[now] = 0;
}
淀粉质:
struct P{
int to,next,v;
}e[M];
int cnt,head[N],dis[N],siz[N],dp[N],rev[N];
bool vis[N];
void add(int x,int y,int v){
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].v = v;
head[x] = cnt;
}
ll n,k,rt,sum;
void getrt(ll u,ll fa){
siz[u] = 1,dp[u] = 0;
for(int i = head[u];i;i = e[i].next){
ll v = e[i].to;
if(v == fa || vis[v])
continue;
getrt(v,u);
siz[u] += siz[v];
dp[u] = std::max(dp[u],siz[v]);
}
dp[u] = std::max((ll)dp[u],sum - siz[u]);
if(dp[u] < dp[rt])
rt = u;
}
ll ans = 0;
ll tot = 0;
void getdis(ll u,ll fa){
rev[++tot] = dis[u];
for(int i = head[u];i;i = e[i].next){
ll v = e[i].to;
if(v == fa || vis[v])
continue;
dis[v] = dis[u] + e[i].v;
getdis(v,u);
}
}
ll doit(ll u,ll s){
tot = 0,dis[u] = s,getdis(u,0);
std::sort(rev + 1,rev + tot + 1);
ll l = 1,r = tot;
ll ans = 0;
while(l <= r)
if(rev[l] + rev[r] <= k)
ans += r - l,++l;
else
--r;
return ans;
}
void solve(ll u){
vis[u] = 1,ans += doit(u,0);
for(int i = head[u];i;i = e[i].next){
ll v = e[i].to;
if(vis[v])continue;
ans -= doit(v,e[i].v);
sum = siz[v],dp[0] = n,rt = 0;
getrt(v,u),solve(rt);
}
}
树上启发式合并
struct P{
ll to,next;
}e[M];
int cnt,C[N],head[N],c[N],siz[N],gson[N];
ll ans[N],fans[N];
void add(int x,int y){
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
ll n;
void dfs(int u,int fa){
siz[u] = 1;
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa)
continue;
dfs(v,u);
siz[u] += siz[v];
if(siz[v] > siz[gson[u]])
gson[u] = v;
}
}
std::queue<int>QWQ;
ll maxx;
void clear(){
maxx = 0;
while(!QWQ.empty()){
C[QWQ.front()] = 0;
QWQ.pop();
}
}
ll t;
void init(int u,int fa){
QWQ.push(c[u]);
C[c[u]] ++ ;
if(C[c[u]] > maxx)
fans[t] = c[u],maxx = C[c[u]];
else
if(C[c[u]] == maxx)
fans[t] += c[u];
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa || v == gson[t])
continue;
init(v,u);
}
}
void solve(int u,int fa){
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(v == fa || v == gson[u])
continue;
solve(v,u);
clear();
}
if(gson[u])
solve(gson[u],u);
t = u;
fans[u] = fans[gson[u]];
init(u,fa);
}