2011-03-29
我的线段树模板[静态版]

由于上次师大的邀请赛有一个线段树和并查集的综合题,可是我的线段树死活过不了,原来是我自己写错了一个地方,赋值错了,然后就一直悲剧了,现在把我的线段树模板放在这里,以后要用的时候就直接来取好了,不多说了,直接贴代码:

/
ID:qcx97811
LANG:C
TASK:H
/
#include
#include
#include
#include
/还有一个修改没加/
typedef struct//线段树的结构体
{//l表示左端点 r右端点 max表示这一段的最大值 min表示这一段的最小值
int l,r
int max,min
}Tree
Tree node[5000064];//4倍
void init_node(int idx,int _l,int _r)
{//初始化线段树
int mid
node[idx].l=_l//这里不管怎样都要赋值,确定边界
node[idx].r=_r
if(_l == _r)
{//如果到了底层了直接返回
return ;
}
mid=(_l+_r)>>1;
init_node(2idx,_l,mid);//初始化左子树
init_node(2idx+1,mid+1,_r);//初始化右子树
}
void insert(int idx,int _l,int _r,int val)
{//插入一个值
int mid
if(_l<=node[idx].l & _r>=node[idx].r)
{//如果区域覆盖这棵树就直接改变这棵树就行了 不改变子树的信息
node[idx].min=node[idx].max=val
return ;
}
if(_r<=node[2idx].r)
{//插入左子树
insert(2idx,_l,_r,val);
//更新该区域的最大最小值
if(node[2idx].max > node[idx].max)
node[idx].max=node[2idx].max
if(node[2idx].min < node[idx].min)
node[idx].min=node[2idx].min
}
else
{
if(_l>=node[2idx+1].l)
{//插入右子树
insert(2idx+1,_l,_r,val);
//更新该区域的最大最小值
if(node[2idx+1].max>node[idx].max)
node[idx].max=node[2idx+1].max
if(node[2idx+1].min<node[idx].min)
node[idx].min=node[2idx+1].min
}
else
{
//插入左子树和右子树
insert(2idx,_l,node[2idx].r,val);
insert(2idx+1,node[2idx+1].l,_r,val);
//更新该区域的最大最小值
if(node[2idx].max > node[idx].max)
node[idx].max=node[2idx].max
if(node[2idx].min < node[idx].min)
node[idx].min=node[2idx].min
if(node[2idx+1].max > node[idx].max)
node[idx].max=node[2idx+1].max
if(node[2idx+1].min < node[idx].min)
node[idx].min=node[2idx+1].min
}
}
}
int query_max(int idx,int _l,int _r)
{//求_l到_r区间的最大值
int max,max1
if(_l<=node[idx].l & _r>=node[idx].r)//如果覆盖这个区域 直接返回
return node[idx].max
if(_r<=node[2idx].r)
{//返回左子树的最大值
return query_max(2idx,_l,_r);
}
else
{
if(_l>=node[2idx+1].l)//返回右子树的最大值
return query_max(2idx+1,_l,_r);
//返回左子树和右子树中最大值的最大值
max=query_max(2idx,_l,node[2idx].r);
max1=query_max(2idx+1,node[2idx+1].l,_r);
if(max>max1)
return max
else
return max1
}
}
int query_min(int idx,int _l,int _r)
{//类似上面的query_max
int min,min1
if(_l<=node[idx].l & _r >=node[idx].r)
return node[idx].min
if(_r<=node[2idx].r)
return query_min(2idx,_l,_r);
else
{
if(_l>=node[2idx+1].l)
{
return query_min(2idx+1,_l,_r);
}
else
{
min=query_min(2idx,_l,node[2idx].r);
min1=query_min(2idx+1,node[2*idx+1].l,_r);
if(min<min1)
return min
else
return min1
}
}
}

2011-03-19
网络流24题-5 圆桌问题

提交地址

把单位放在一边,餐桌放在一边,然后增加一个超级源点,从这个点到每个单位有一条边,容量是这个单位的人数,增加一个超级汇点,从每个餐桌到这个点有一条边,容量是餐桌能够容纳的人数,然后每个单位到每个餐桌连一条边,容量为1,表示所有的餐桌最多只能有同一个单位的1个人,然后跑一次最大流,如果为满流,也就是所有的人都有相应的餐桌,那么就存在一种方案满足题意.另外这题可以贪心,每次选人数最多的单位分配餐桌,对同一个单位,每个餐桌分配一个人,然后下次分配的时候,首先选那些剩下座位多的餐桌,依次下去,就可以判定是不是存在一种方案了.本题就不贴代码了.

2011-03-19
网络流24题-3 最小路径覆盖

提交地址
首先是这题有提示,不然还真是很难想的啊.有了提示之后,只需要想清楚为什么这样的构图是正确的,正确性的证明在有了图之后还是很好证明的,可以形成一条一条的链,从左到右再到左,依此循环,知道某个点”没有出度”为止.这样之后就很容易看出最小路径覆盖的条数就是总点数-最大流.接下来就是输出路径了,我用的也是链一样的形式,从左边开始,在右边找一个点[这个点满足一开始两点之间有流量,现在流量为0]然后一直枚举左边没输出过的点就OK了.这里附上我的代码:这个sap代码有了基本的要注意的地方.

/
ID:qcx97811
LANG:C
TASK:24-3/NKU_2123
/
#include
#include
#include
#include
#include

int n,m
int graph[360][360],c[360][360];
int d[360],gap[360],maxflow
void sap()
{
int u,v
int q[360],low[360];//这里的数组大小是顶点的个数+i
int tmp,front,rear
maxflow=0
//bfs
q[rear=0]=n2+1//这里是汇点
front=-1
memset(d,-1,sizeof(d));
memset(gap,0,sizeof(gap));
d[2n+1]=0//这里是汇点的层次度为0
while(front < rear)
{
u=q[++front];
for(v=0v<=2n+1;v++)//这里注意 上界是顶点的最大下标 下界是最小下标
{
if((-1==d[v])&(graph[v][u]>0))
{//这里是graph[v][u]不是graph[u][v]!!!
q[++rear]=v
d[v] = d[u]+1
gap[d[v]]++
}
}
}
//sap
q[tmp=0]=0//这里是起点
low[0]=INT_MAX//这里记得赋值 无穷大
while(d[0]<2(1+n))//这里的2(n+1)是如果所有的点是一条链的时候源点的标号+1
{//这里是起点的层次信息
u=q[tmp];
for(v=0v<=2n+1;v++)//这里的n2+1是顶点的最大标号
{
if(graph[u][v]>0 & d[v]+1==d[u])
break
}
if(2(n+1)==v)//这里v==上面for循环的上界+1
{//此点出度为0
gap[d[u]]
if(gap[d[u]] == 0)
break
d[u]=2n+2//这里应该赋值为一个无穷大
for(v=0v<=2n+1;v++)//这里的n2+1是顶点的最大标号
{
if(graph[u][v] > 0 & d[v] >=0 && d[v]+1<d[u])
d[u]=d[v]+1
}
gap[d[u]]++
if(tmp>0)
tmp
}
else
{
q[++tmp]=v
low[tmp]=graph[u][v];
if(low[tmp-1]<low[tmp])
low[tmp]=low[tmp-1];
if(v==2n+1)//这里的n2+1是顶点的最大标号
{//找到一条增广路
maxflow+=low[tmp];
for(u=0u<tmp;u++)
{//改变残留网络
front = q[u];
rear = q[u+1];
graph[front][rear] -= low[tmp];
graph[rear][front] += low[tmp];
}
tmp=0
}
}
}
}
void output()
{
char used[360];
int i,j,now
memset(used,0,sizeof(used));
for(i=1i<=n;i++)
{
if(0==used[i])
{
printf(“%d”,i);
now=i
while(1)
{
used[now]=1
for(j=1j<=c[now][0];j++)
{
if(0==used[c[now][j]]&0==graph[now][c[now][j]])
{
printf(“ %d”,c[now][j]-n);
used[c[now][j]]=1
break
}
}
if(c[now][0]+1==j)
break
else
now=c[now][j]-n;
if(1==used[now])
break
}
printf(\n);
}
}
printf(“%d\n,n-maxflow);
}
int main(void)
{
freopen(“24-3.in”,“r”,stdin);
freopen(“24-3.out”,“w”,stdout);
int i,s,e,j
scanf(“%d%d”,n,&m);
memset(graph,0,sizeof(graph));
memset(c,0,sizeof(c));
for(i=0i<m;i++)
{
scanf(“%d%d”,s,&e);
graph[s][n+e]=1
c[s][++c[s][0]]=n+e
}
for(i=1i<=n;i++)
{
graph[0][i]=1
graph[n+i][2n+1]=1
}
#if 0
for(i=0;i<2*(n+1);i++)< span="">
{
for(j=0;j<2*(n+1);j++)< span="">
printf(“(%d,%d:%d) “,i,j,graph[i][j]);
printf(“\n”);
}
#endif
#if 1
sap();
output();
#endif
return 0
}

2011-03-03
网络流24题-2最小割模型

对于第一次做最小割模型的我 是看了黑书上的讲解才会的 - -下面附上黑书上的讲解[由于pdf文件不能复制,所以截图传上来]

那么收益的问题就解决了,输出那些器材和实验的时候,可以先从源点开始搜索求出S集,然后不再S集里面的就都要输出了[为什么?]求网络流的算法我用的是前面介绍的sap+gap

2011-02-27
网络流sap算法

对于学习了dinic算法之后,再来看sap算法,其实就没什么难的了,dinic是多次bfs然后dfs找增广路,sap是一次bfs然后多次重标号.当然sap还可以用gap优化.也就是如果断层了的话,那么就可以直接退出了.具体的是先进行一次bfs构建层次图和初始化gap数组,然后当dist[source]<N的时候(source表示源点,N表示顶点的个数dist是层次图的信息)一直循环,如果找到一条增广路径的话,就改变残留网络和最大流,如果当前点的出度为0(包括层次图的信息)那么就退栈,并且改变层次图的信息和gap数组,改变gap数组之后如果发现已经断层了,那么就直接退出了,下面给出USACO4.2.1的sap版代码:

/
ID:qcx97811
LANG:C
TASK:ditch
/
#include
#include
#include
#include
int graph[206][206],d[206],gap[206];
int N,M,maxflow

void bfs()
{
int i,j
int q[206];
int front,rear
front = -1
q[rear=0]=M
memset(d,-1,sizeof(d));
d[M]=0
memset(gap,0,sizeof(gap));
gap[0]=1
while(front < rear)
{
i = q[++front];
for(j = 1j <= M;j++)
{
if(-1 == d[j] & graph[j][i] > 0)
{
d[j] = d[i]+1
q[++rear] = j
gap[d[j]]++
}
}
}
}
void sap()
{
int i,j
int top,q[206],low[206];
bfs();
#if 0
printf(“%d %d %d %d\n”,d[1],d[2],d[3],d[4]);
printf(“——–after bfs\n”);
#endif
top=0
low[0]=INT_MAX
q[top]=1
while(d[1] < M)
{
for(i = 1i <= M;i++)
{
if(graph[q[top]][i] > 0 & d[i]+1==d[q[top]])
{
q[++top]=i
low[top]=graph[q[top-1]][i];
if(low[top] > low[top-1])//到当前点的增广流的最大值
low[top]=low[top-1];
break
}
}
if(i != (M+1))//注意这里是M+1和上面的for循环的出口对应 应该是for循环的最大值+1
{
if(M == q[top])//如果已经找到一条增广路
{
for(i = 0i < top;i++)
{//更新残留网络
graph[q[i]][q[i+1]] -= low[top];
graph[q[i+1]][q[i]] += low[top];
}
maxflow += low[top];//更改结果
top = 0//重新从源点开始找
}
}
else
{
j = q[top];
gap[d[j]]//这个点对应的层次的点的个数-1
if(gap[d[j]] == 0)//如果-1之后==0那么就出现了断层,可以直接退出了,至于证明这个应该不难吧.
{
break
}
d[j] = M+1
for(i = 1i <= M;i++)
{
if(graph[j][i] > 0 & d[i]+1 < d[j] && d[i] >= 0)
{//注意这里的d[i] >= 0因为并不是所有的点都在层次图中
d[j]=d[i]+1
}
}
gap[d[j]]++//对应的点的层次的点的个数+1
if(top>0)//如果不是源点就退到前一个顶点
top
}
}
return ;
}

int main(void)
{
freopen(ditch.in,r,stdin);
freopen(ditch.out,w,stdout);
int i,s,e,c
scanf(%d%d,N,&M);
memset(graph,0,sizeof(graph));
for(i = 0i < N;i++)
{
scanf(%d%d%d,s,&e,&c);
graph[s][e] += c
}
sap();
printf(%d\n,maxflow);
return 0
}

2011-02-10
网络流24题之一最大匹配

此处可提交

我用的还是最原始的EK算法,建图也比较简单,只要某两个人能配合好就给一个权值为1的边,然后增加一个超级源点和汇点,剩下的就是最大流了.不过最后输出时注意下(输出配对关系的时候),比如下面这组数据

2 4

1 3

1 4

2 3

-1 -1

代码如下:

/
LANG:C
TASK:
ID:qcx97811
/
#include
#include
#include

int graph[106][106],pre[106];
int N,M,maxflow

int bfs()
{
int i,j
int q[206];
int front,rear
memset(pre,-1,sizeof(pre));
front = rear = -1
q[++rear] = 0
while(front < rear)
{
i = q[++front];
for(j = 0j <= (N+1);j++)
{
if(-1 == pre[j] & graph[i][j] > 0)
{
pre[j] = i
rear++
q[rear] = j
if(j == (N+1))
return 1
}
}
}
return 0
}
void max_flow()
{
int i,j,add
maxflow = 0
while(0 != (add = bfs()))
{
add = 10000006
for(i = (N+1);i != 0i = pre[i])
{
j = pre[i];
if(graph[j][i] < add)
add = graph[j][i];
}
for(i = (N+1);i != 0i = pre[i])
{
j = pre[i];
graph[j][i] -= add
graph[i][j] += add
}
maxflow += add
}
}
int main(void)
{
freopen(“24-1.in”,“r”,stdin);
freopen(“24-1.out”,“w”,stdout);
int i,j,s,e
scanf(“%d%d”,M,&N);
memset(graph,0,sizeof(graph));
while(1)
{
scanf(“%d%d”,s,&e);
if((-1 == s) & (-1 == e))
break
if(s > e)
{//确定是从一个方向流往另外一个方向
i = s;
s = e
e = i
}
graph[s][e] = 1
}
for(i = 1i <= M;i++)//超级源点
graph[0][i] = 1
for(i = M+1i <= N;i++)//超级汇点
graph[i][N+1] = 1
max_flow();
if(0 == maxflow)
{
printf(“No Solution\n);
}
else
{
printf(“%d\n,maxflow);
for(i = 1i <= M;i++)
{
for(j = (M+1);j <= N;j++)
{
if(graph[j][i] == 1)
{
printf(“%d %d\n,i,j);
//这里的输出注意下 附数据如下
//2 4
//1 3
//1 4
//2 3
//-1 -1
}
}
}
}
return 0
}