-----------------------------------
点分治
一般步骤:
1.找到树的重心(避免当树退化成链时复杂度升高)
2.从重心出发分治统计路径
分治过程:
统计当前节点子树中的符合条件的路径数加到ans中
标记当前点避免重复
减去当前节点子树节点中的符合条件的路径数(这样剩下的就是经过当前节点的路径数,不会重复了)
在当前节点的子树中分别找到重心继续分治解决
bzoj1468
#include
#include
#include
#define INF 2e9
#define N 40005
using namespace std;
struct edge{int to,v;};
vectore[N];
int n,sum,root,k;
int son[N],f[N],vis[N],deep[N],c[N],ans;
void getroot(int x,int fa) //找重心
{
son[x]=1,f[x]=0;
for(int i=0;i //若以当前点为根那还要考虑以当前点的父节点为根的子树
if(f[x] //计算深度
{
++c[0];c[c[0]]=deep[x];
for(int i=0;i //统计答案
{
deep[x]=v;c[0]=0;
getdis(x,0);
sort(c+1,c+c[0]+1);
int l=1,r=c[0];
LL res=0;
while(l //加上x节点及其子树中所有的符合条件的路径数
for(int i=0;i //显然上面会加入一些非简单路径,就减去不经过x节点的那些会被统计到答案中的路径数
sum=son[t.to];
root=0;
getroot(t.to,0);
solve(root);
}
}
int main()
{
scanf("%d",&n);
for(int i=1,x,y,v;i
[点分治例题]bzoj2599
[点分治例题]bzoj2152
-----------------------------------
网络流(dinic)模板
#include
#include
#include
#include
#include
#define N 100005
#define INF 2e9
using namespace std;
struct edge{int to,v;};
vectore;
vectorG[N];
int n,m,ss,tt,d[N],cur[N<<1];
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
int a=0;char f=1,c=nc();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=nc();}
while(c>='0'&&c<='9'){a=a*10+c-'0';c=nc();}
return a*f;
}
inline void add(int x,int y,int v){
e.push_back((edge){y,v});
G[x].push_back(e.size()-1);
e.push_back((edge){x,0});
G[y].push_back(e.size()-1);
}
bool bfs(){
queueq;
memset(d,-1,sizeof(d));
q.push(ss),d[ss]=0;
while(!q.empty()){
int u=q.front();q.pop();
if(u==tt) return 1;
for(int i=0;i0){
e[t].v-=f;
e[t^1].v+=f;
flow+=f;
a-=f;
if(!a) break;
}
}
if(!flow) d[x]=-1;
return flow;
}
inline int dinic(){
int res=0;
while(bfs()){
memset(cur,0,sizeof(cur));
res+=dfs(ss,INF);
}
return res;
}
int main(){
n=read(),m=read(),ss=read(),tt=read();
for(int i=1;i<=m;++i){
int x=read(),y=read(),v=read();
add(x,y,v);
}
printf("%d\n",dinic());
return 0;
}
-----------------------------------
上下界网络流
神犇的总结 //看不懂我写的还是参考这一位的免得浪费时间,毕竟我语文水平有限而且本意也只是自己随便写点以后自己能看懂就够了
可以分为这么几类:
1.无源汇有上下界可行流
使所有边都先拥有等于下界的流量得到初始流,用一个a数组表示每一个点在初始流中的流入与流出的相对情况(a[i]>0则这个节点初始的 流入量>流出量)
建立超级源汇ss,tt
要给a[i]>0的点的流量找个来处(ss向i连容量a[i]的边),给a[i]<0的流量找个去处(i向tt连容量为-a[i]的边(ss,tt是为了和s,t区分,前者为超级源汇,后者为实际的源汇)
构建出了新的网络后跑ss到tt的最大流,一定要最大流等于新网络中ss可以流出的总流量才能满足流量守恒
每条边在可行流中的流量=容量下界+附加流中它的流量(即跑完dinic之后所加反向边的权值)
LOJ #115. 无源汇有上下界可行流
#include
#include
#include
#include
#define INF 2e9
#define N 205
#define M 10201
using namespace std;
int read()
{
int a=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}
return a*f;
}
struct edge{int to,v,next,num;}e[M*3];
int n,m,cnt=1,s,t,sum=0;
int head[N],a[N],d[N],low[M],cur[N],ans[M];
void add(int x,int y,int v,int num)
{
e[++cnt].next=head[x],e[cnt].to=y,e[cnt].v=v,e[cnt].num=num;
head[x]=cnt;
e[++cnt].next=head[y],e[cnt].to=x,e[cnt].v=0,e[cnt].num=num;
head[y]=cnt;
}
int bfs()
{
queueq;
memset(d,-1,sizeof(d));
q.push(s);d[s]=0;
while(!q.empty())
{
int x=q.front();q.pop();
if(x==t) return 1;
for(int i=head[x];i;i=e[i].next)
{
int y=e[i].to;
if(d[y]==-1&&e[i].v)
{
q.push(y);
d[y]=d[x]+1;
}
}
}
return 0;
}
int dfs(int x,int a)
{
if(x==t||!a) return a;
int flow=0,f;
for(int &i=cur[x];i;i=e[i].next)
if(d[e[i].to]==d[x]+1&&(f=dfs(e[i].to,min(a,e[i].v)))>0)
{
e[i].v-=f;
e[i^1].v+=f;
flow+=f;
a-=f;
if(!a) break;
}
if(!flow) d[x]=-1;
return flow;
}
int dinic()
{
int res=0;
while(bfs())
{
for(int i=s;i<=t;++i)
cur[i]=head[i];
res+=dfs(s,INF);
}
return res;
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=m;++i)
{
int x,y,h;
scanf("%d %d %d %d",&x,&y,&low[i],&h);
add(x,y,h-low[i],i);
a[x]-=low[i],a[y]+=low[i];
}
s=0,t=n+1;
for(int i=1;i<=n;++i)
if(a[i]>0)
sum+=a[i],add(s,i,a[i],0);
else
add(i,t,-a[i],0);
if(dinic()==sum)
{
printf("YES\n");
for(int i=1;i<=n;++i)
for(int j=head[i];j;j=e[j].next)
if(j%2&&e[j].num)
ans[e[j].num]=low[e[j].num]+e[j].v;
for(int i=1;i<=m;++i)
printf("%d\n",ans[i]);
}
else printf("NO\n");
return 0;
}
1的建图之后再从t向s连一条下界为0上界为正无穷的边,可行流即为t到s反向边中的流量
3.有源汇有上下界最大流
求出可行流,显然可行流不一定是最大流
在剩下的网络中再跑s-t的最大流
最终的最大流流量=可行流流量(即t到s的无穷边上跑出的流量)+新增广出的s-t流量
LOJ #116. 有源汇有上下界最大流
#include
#include
#include
#include
#define INF 2e9
#define N 205
#define M 10000
using namespace std;
int read()
{
int a=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}
return a*f;
}
struct edge{int next,to,v;}e[M*3];
int n,m,sum,s,t,cnt,ss,tt;
int a[N],head[N],cur[N],d[N];
void add(int x,int y,int v)
{
e[cnt].next=head[x],e[cnt].to=y,e[cnt].v=v;
head[x]=cnt++;
e[cnt].next=head[y],e[cnt].to=x,e[cnt].v=0;
head[y]=cnt++;
}
int bfs()
{
memset(d,-1,sizeof(d));
queueq;
q.push(ss),d[ss]=0;
while(!q.empty())
{
int x=q.front();q.pop();
if(x==tt) return 1;
for(int i=head[x];i!=-1;i=e[i].next)
{
int y=e[i].to;
if(d[y]==-1&&e[i].v)
{
q.push(y);
d[y]=d[x]+1;
}
}
}
return 0;
}
int dfs(int x,int a)
{
if(x==tt||!a) return a;
int flow=0,f;
for(int &i=cur[x];i!=-1;i=e[i].next)
if(d[e[i].to]==d[x]+1&&(f=dfs(e[i].to,min(a,e[i].v)))>0)
{
e[i].v-=f;
e[i^1].v+=f;
flow+=f;
a-=f;
if(!a) break;
}
if(!flow) d[x]=-1;
return flow;
}
int dinic()
{
int res=0;
while(bfs())
{
for(int i=0;i<=n+1;++i) cur[i]=head[i];
res+=dfs(ss,INF);
}
return res;
}
int main()
{
n=read(),m=read(),s=read(),t=read();
memset(head,-1,sizeof(head));
for(int i=1;i<=m;++i)
{
int x=read(),y=read(),l=read(),r=read();
add(x,y,r-l),
a[x]-=l,a[y]+=l;
}
ss=0,tt=n+1;
for(int i=1;i<=n;++i)
if(a[i]>0)
sum+=a[i],add(ss,i,a[i]);
else if(a[i]<0) add(i,tt,-a[i]);
add(t,s,INF);
sum-=dinic();
if(sum) printf("please go home to sleep\n");
else
{
sum=e[head[t]^1].v;
head[s]=e[head[s]].next,head[t]=e[head[t]].next;
ss=s,tt=t;
printf("%d\n",sum+dinic());
}
return 0;
}
与有源汇有上下界最大流类似
求出可行流后在残余网络中跑t到s的最大流
最终的最大流流量=可行流流量(即t到s的无穷边上跑出的流量)-新增广出的t-s的流量
#117. 有源汇有上下界最小流
#include
#include
#include
#include
#include
#define INF 2e9
#define N 50005
#define M 125005
using namespace std;
int read()
{
int a=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}
return a*f;
}
struct edge{int to,next,v;}e[M*3];
int n,m,cnt,sum,s,t,ss,tt;
int a[N],d[N],head[N],cur[N];
void add(int x,int y,int v)
{
e[cnt].next=head[x],e[cnt].to=y,e[cnt].v=v;
head[x]=cnt++;
e[cnt].next=head[y],e[cnt].to=x,e[cnt].v=0;
head[y]=cnt++;
}
int bfs()
{
queueq;
memset(d,-1,sizeof(d));
q.push(ss),d[ss]=0;
while(!q.empty())
{
int x=q.front();q.pop();
if(x==tt)
{
memcpy(cur,head,sizeof(cur));
return 1;
}
for(int i=head[x];i!=-1;i=e[i].next)
if(d[e[i].to]==-1&&e[i].v)
{
d[e[i].to]=d[x]+1;
q.push(e[i].to);
}
}
return 0;
}
int dfs(int x,int a)
{
if(x==tt||!a) return a;
int flow=0,f;
for(int &i=cur[x];i!=-1;i=e[i].next)
if(d[e[i].to]==d[x]+1&&(f=dfs(e[i].to,min(a,e[i].v)))>0)
{
e[i].v-=f;
e[i^1].v+=f;
flow+=f;
a-=f;
if(!a) break;
}
if(!flow) d[x]=-1;
return flow;
}
int dinic()
{
int res=0;
while(bfs())
{
// for(int i=0;i<=n+1;++i) cur[i]=head[i];
res+=dfs(ss,INF);
}
return res;
}
int main()
{
n=read(),m=read(),s=read(),t=read();
memset(head,-1,sizeof(head));
for(int i=1;i<=m;++i)
{
int x=read(),y=read(),l=read(),r=read();
a[x]-=l,a[y]+=l;
add(x,y,r-l);
}
ss=0,tt=n+1;
for(int i=1;i<=n;++i)
if(a[i]>0)
sum+=a[i],add(ss,i,a[i]);
else if(a[i]<0)
add(i,tt,-a[i]);
add(t,s,INF);
sum-=dinic();
if(sum) printf("please go home to sleep\n");
else
{
sum=e[head[t]^1].v;
head[s]=e[head[s]].next,head[t]=e[head[t]].next;
ss=t,tt=s;
printf("%d\n",sum-dinic());
}
return 0;
}
-----------------------------------
最小费用最大流
有源汇的最大流一定是一定的,即便流的分配方案不同。
将弧的费用看做长度做最短路,不断找路径长度小的增广路,当无法增广时,即是最大流且此时费用最小
LOJ #102. 最小费用流
#include
#include
#include
#include
#define N 405
#define M 15005
using namespace std;
int read()
{
int a=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}
return a*f;
}
struct edge{int next,to,v,c;}e[M*2];
int n,m,s,t,cnt;
int head[N],dis[N],from[N],pre[M*2],v[N];
void add(int x,int y,int v,int c)
{
e[cnt].next=head[x],e[cnt].to=y,e[cnt].v=v,e[cnt].c=c;
head[x]=cnt++;
e[cnt].next=head[y],e[cnt].to=x,e[cnt].v=0,e[cnt].c=-c;
head[y]=cnt++;
}
int spfa()
{
memset(from,-1,sizeof(from));
memset(dis,127,sizeof(dis));
queueq;
q.push(s),dis[s]=0;
while(!q.empty())
{
int x=q.front();q.pop();
v[x]=0;
for(int i=head[x];i!=-1;i=e[i].next)
{
if(e[i].v&&dis[e[i].to]>dis[x]+e[i].c)
{
dis[e[i].to]=dis[x]+e[i].c;
from[e[i].to]=x,pre[e[i].to]=i;
if(!v[e[i].to])
v[e[i].to]=1,q.push(e[i].to);
}
}
}
return from[t]!=-1;
}
int main()
{
n=read(),m=read();
s=1,t=n;
memset(head,-1,sizeof(head));
for(int i=1;i<=m;++i)
{
int x=read(),y=read(),v=read(),c=read();
add(x,y,v,c);
}
int flow=0,cost=0;
while(spfa())
{
int f=2e9;
for(int i=t;i!=s;i=from[i])
f=min(f,e[pre[i]].v);
flow+=f,cost+=f*dis[t];
for(int i=t;i!=s;i=from[i])
e[pre[i]].v-=f,e[pre[i]^1].v+=f;
}
printf("%d %d\n",flow,cost);
return 0;
}
AC自动机
P3808 【模板】AC自动机(简单版)
#include
#include
#include
#include
#define N 1000010
using namespace std;
struct node{
int fail,sum,lk[26];//有些地方赋值为0最好还是写一下,因为有些时候初始化指向根不为0
}AC[N]; //的话容易疏漏
int n,size;
char s[N];
inline void insert(char *s){ //就是tire树的插入
int len=strlen(s+1),nw=0;
for(int i=1,t;i<=len;++i){
t=s[i]-'a';
if(!AC[nw].lk[t]) AC[nw].lk[t]=++size;
nw=AC[nw].lk[t];
}
++AC[nw].sum;
return;
}
void build(){
queueq;
for(int i=0;i<26;++i)
if(AC[0].lk[i]) AC[AC[0].lk[i]].fail=0,q.push(AC[0].lk[i]);//第一层的fail指针都指向根,顺便进行第一次bfs的扩展
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<26;++i)
if(AC[x].lk[i]!=0){//存在这个子节点,直接把fail指针指向父节点fail指向的节点的这个子节点
AC[AC[x].lk[i]].fail=AC[AC[x].fail].lk[i];
q.push(AC[x].lk[i]);
}
else AC[x].lk[i]=AC[AC[x].fail].lk[i];//否则直接接过去,因为是bfs扩展,总会接到有这个子节点的那一个节点
}
return;
}
int ask(char *s){
int len=strlen(s+1);
int nw=0,ans=0;
for(int i=1;i<=len;++i){
nw=AC[nw].lk[s[i]-'a'];
int nxt=nw;
while(nxt&&AC[nxt].sum!=-1){ //按fail指针查找
ans+=AC[nxt].sum;
AC[nxt].sum=-1;
nxt=AC[nxt].fail;
}
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%s",s+1),insert(s);
AC[0].fail=0;
build();
scanf("%s",s+1);
printf("%d\n",ask(s));
return 0;
}