总目录详见https://blog.csdn.net/mrcrack/article/details/84471041
做题原则,找不到测评地址的题不做。2018-11-28
重走长征路---OI每周刷题记录---1月11日 2014
本周共计19题+1题
测评地址:
网络流
1.「网络流24题」搭配飞行员
2.「codevs1993」草地排水 //在线测评地址http://codevs.cn/problem/1993/
20.【模板】网络最大流 //在线测评地址https://www.luogu.org/problemnew/show/P3376
网络流/spfa
3.「bzoj1001」狼抓兔子 //在线测评地址https://www.luogu.org/problemnew/show/P4001
tarjan
4.「bzoj1051」受欢迎的牛
并查集
5.「bzoj1202」狡猾的商人 //在线测评地址https://www.luogu.org/problemnew/show/P2294
RMQ
6.「vijos1514」天才的记忆
二分图匹配
7.「bzoj1059」矩阵游戏 「bzoj1191」超级英雄Hero
单调栈
8.「bzoj3039」玉蟾宫
DP
9.「bzoj1270」雷涛的小猫
数学
10.「bzoj1303」中位数图
11.「bzoj1192」鬼谷子的钱袋 在线测评地址https://www.luogu.org/problemnew/show/P2320
递推+高精度
12.「bzoj1002」轮状病毒
spfa
13.「bzoj1003」物流运输trans
kruskal
14.「bzoj1601」灌水
快速幂
15.「codevs1851」越狱
贪心
16.「JoyOI1027」木瓜地
17.「JoyOI1099」超级书架
18.「JoyOI1090」母舰
重打 极大化思想
19.「vijos1055」奶牛浴场
题解:
网络流
1.「网络流24题」搭配飞行员
2.「codevs1993」草地排水
//1993 草地排水
//在线测评地址http://codevs.cn/problem/1993/
//网络流(最大流)
//采用Dinic算法,样例通过,提交AC。2019-2-7 20:13
#include <stdio.h>
#include <string.h>
#define INF 999999
int n,m,cnt=0,head[210],d[210],q[210],h,t;
struct node{
int to,w,next;
}e[210*2];
int min(int a,int b){
return a<b?a:b;
}
void add_edge(int u,int v,int w){
e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt,cnt++;//此处写成 e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],cnt++;
}
int bfs(int s){
int u,v,w,b;
memset(d,-1,sizeof(d));
h=t=1,q[t]=s,t++,d[s]=0;
while(h<t){
u=q[h];
b=head[u];
while(b!=-1){
v=e[b].to,w=e[b].w;
if(d[v]==-1&&w>0)d[v]=d[u]+1,q[t]=v,t++;//此处写成if(d[v]==-1&&w>0)d[v]=d[u]+1;
b=e[b].next;
}
h++;
}
return d[m]!=-1;
}
int dfs(int u,int exp){
int v,w,b,flow=0,tmp=0;
if(u==m)return exp;
b=head[u];
while(b!=-1){
v=e[b].to,w=e[b].w;
if(d[v]==d[u]+1&&w>0){
tmp=dfs(v,min(exp,w));
flow+=tmp;
exp-=tmp;
e[b].w-=tmp;
e[b^1].w+=tmp;
if(!exp)break;
}
b=e[b].next;
}
return flow;
}
int main(){
int u,v,w,ans=0;
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
while(n--){
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w),add_edge(v,u,0);
}
while(bfs(1))ans+=dfs(1,INF);
printf("%d\n",ans);
return 0;
}
20.【模板】网络最大流
//P3376 【模板】网络最大流
//在线测评地址https://www.luogu.org/problemnew/show/P3376
//题目图文并茂,很好理解。
//样例也能很快弄懂,但怎么编程实现。
//此文关于网络流(最大流)https://blog.csdn.net/lzoi_hmh/article/details/74940366写得真不赖。
//样例死循环,发现 v=e[b].to;//此处写成v=e[b].next;查了30分钟
//修改,还是死循环,阅读代码,发现
//scanf("%d%d%d%d",&N,&M,&S,&T);//此处写成scanf("%d%d",&N,&M,&S,&T);查了6分钟
//修改,样例数据输出20,
//memset(head,-1,sizeof(head));//此处写成 memset(head,0,sizeof(head)); 查了好久,因cnt=0开始使用,故需初始化为-1,查了60分钟,靠跟踪,才查出
//牵一发动全身,当一个熟悉的量改变后,势必,很多习惯也要跟着改变,之前习惯cnt=1开始使用,而此处cnt=0开始使用
//样例通过,提交AC。2019-2-7 19:21
#include <stdio.h>
#include <string.h>
#define INF 999999
int N,M,S,T,ans=0;
int head[10100],cnt=0,d[10100],q[10100],h,t;
struct node{
int to,next,w;
}e[100100*2];//正向边,反向边
int min(int a,int b){
return a<b?a:b;
}
void add_edge(int u,int v,int w){
e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt,cnt++;
}
int bfs(int s){//建立层次关系,用dfs建立,就乱套了。
int u,v,w,b;
memset(d,-1,sizeof(d));
h=t=1,q[t]=s,t++,d[s]=0;
while(h<t){
u=q[h];
b=head[u];
while(b!=-1){
v=e[b].to,w=e[b].w;//此处写成v=e[b].next;查了30分钟
if(d[v]==-1&&w>0)d[v]=d[u]+1,q[t]=v,t++;//未访问过,且可通过流量>0
b=e[b].next;
}
h++;
}
return d[T]!=-1;//终点是否能访问到,能,返回1,不能,返回0
}
int dfs(int u,int exp){//u点的流量exp
int flow=0,tmp=0,v,w,b;
if(u==T)return exp;//到达目的地
b=head[u];
while(b!=-1){
v=e[b].to,w=e[b].w;
if(d[v]==d[u]+1&&w>0){
tmp=dfs(v,min(exp,w));
flow+=tmp;
exp-=tmp;//exp还需运走的流量
e[b].w-=tmp;//正向边
e[b^1].w+=tmp;//反向边
if(!exp)break;//已无物可运,无需再搜
}
b=e[b].next;
}
return flow;
}
void dinic(int s){
while(bfs(s))ans+=dfs(s,INF);
}
int main(){
int u,v,w;
memset(head,-1,sizeof(head));//此处写成 memset(head,0,sizeof(head)); 查了好久,因cnt=0开始使用,故需初始化为-1,查了60分钟,靠跟踪,才查出
scanf("%d%d%d%d",&N,&M,&S,&T);//此处写成scanf("%d%d",&N,&M,&S,&T);查了6分钟
while(M--){
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w),add_edge(v,u,0);//正向边,反向边
}
dinic(S);
printf("%d\n",ans);
return 0;
}
网络流/spfa
3.「bzoj1001」狼抓兔子
//P4001 [ICPC-Beijing 2006]狼抓兔子
//在线测评地址https://www.luogu.org/problemnew/show/P4001
//网络流(最大流)
//该题难在图的读取,处理成节点的形式。
//输入数据,对应节点及边的关系如下
//有向边 边数 N*(M-1)+(N-1)*M+(N-1)*(M-1) 极限情况1000*1000+1000*1000+1000*1000=3*10^6
//无向边 边数=有向边 边数*2=6*10^6 故开到6001000
//点的数目N*M 极限情况1000*1000=10^6个 数组角标从1开始,1001*1001=1002001,故开到1003000
//内存占用(6001000*3+1003000+1003000+1003000)*4/1024/1024=80.1MB +代码空间 90MB应够了。
//编写完第一部分读取,马上测试,
//通过后,继续第二部分读取编写,之后马上测试,
//通过后,继续第三部分读取编写,之后马上测试,
//采用Dinic算法,
//样例一直无法通过,读题,发现,道路是无向的
//add_edge(u,v,w),add_edge(v,u,w);//此处写成add_edge(u,v,w),add_edge(v,u,0);
//修改,样例通过,提交70分。
//测试点7,9,10TLE。2019-2-7 22:44
//还算满意,以下为70分代码。
//如何优化,哪可以优化。
//翻看题解https://www.luogu.org/problemnew/solution/P4001
//作者: LiRewriter 更新时间: 2017-12-14 21:47说得太棒了
//我们知道,假定在一次dinic过程中,发现不能再进行增广了,那么就相当于向下的这条路是废的。
//因此,我们可以直接把这条路堵上,然后就可以过了。优化效果很明显
//确实,洛谷从TLE->1232ms,点石成金啊
//if(!tmp)d[v]=-1;//向下的这条路是废的。因此,我们可以直接把这条路堵上
//以下为AC代码。2019-2-7 23:08
#include <stdio.h>
#include <string.h>
#define INF 999999999
struct node{
int to,w,next;
}e[6001000];
int N;//点数
int head[1003000],d[1003000],cnt=0,q[1003000],h,t,T;
int min(int a,int b){
return a<b?a:b;
}
void add_edge(int u,int v,int w){
e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt,cnt++;
}
int bfs(int s){
int u,v,w,b;
memset(d,-1,sizeof(d));
h=t=1,q[t]=s,t++,d[s]=0;
while(h<t){
u=q[h];
b=head[u];
while(b!=-1){
v=e[b].to,w=e[b].w;
if(d[v]==-1&&w>0)d[v]=d[u]+1,q[t]=v,t++;
b=e[b].next;
}
h++;
}
return d[T]!=-1;
}
int dfs(int u,int exp){
int v,w,b,flow=0,tmp=0;
if(u==T)return exp;
b=head[u];
while(b!=-1){
v=e[b].to,w=e[b].w;
if(d[v]==d[u]+1&&w>0){
tmp=dfs(v,min(exp,w));
if(!tmp)d[v]=-1;//向下的这条路是废的。因此,我们可以直接把这条路堵上
flow+=tmp;
exp-=tmp;
e[b].w-=tmp;
e[b^1].w+=tmp;
if(!exp)break;
}
b=e[b].next;
}
return flow;
}
int main(){
int n,m,i,j,u,v,w,ans=0;//此处写成 ans=-1
memset(head,-1,sizeof(head));//初始化为-1还是很不顺手
scanf("%d%d",&n,&m);
N=n*m,T=N;//点数
for(i=1;i<=n;i++)//第一部分读取
for(j=1;j<m;j++){
scanf("%d",&w);
u=(i-1)*m+j,v=u+1;
add_edge(u,v,w),add_edge(v,u,w);//此处写成add_edge(u,v,w),add_edge(v,u,0);
}
for(i=1;i<=(n-1)*m;i++){//第二部分读取
scanf("%d",&w);
u=i,v=u+m;
add_edge(u,v,w),add_edge(v,u,w);//此处写成add_edge(u,v,w),add_edge(v,u,0);
}
for(i=1;i<n;i++)//第三部分读取
for(j=1;j<m;j++){
scanf("%d",&w);
u=(i-1)*m+j,v=u+m+1;
add_edge(u,v,w),add_edge(v,u,w);//此处写成add_edge(u,v,w),add_edge(v,u,0);
}
while(bfs(1))ans+=dfs(1,INF);
printf("%d\n",ans);
return 0;
}
//P4001 [ICPC-Beijing 2006]狼抓兔子
//在线测评地址https://www.luogu.org/problemnew/show/P4001
//网络流(最大流)
//该题难在图的读取,处理成节点的形式。
//输入数据,对应节点及边的关系如下
//有向边 边数 N*(M-1)+(N-1)*M+(N-1)*(M-1) 极限情况1000*1000+1000*1000+1000*1000=3*10^6
//无向边 边数=有向边 边数*2=6*10^6 故开到6001000
//点的数目N*M 极限情况1000*1000=10^6个 数组角标从1开始,1001*1001=1002001,故开到1003000
//内存占用(6001000*3+1003000+1003000+1003000)*4/1024/1024=80.1MB +代码空间 90MB应够了。
//编写完第一部分读取,马上测试,
//通过后,继续第二部分读取编写,之后马上测试,
//通过后,继续第三部分读取编写,之后马上测试,
//采用Dinic算法,
//样例一直无法通过,读题,发现,道路是无向的
//add_edge(u,v,w),add_edge(v,u,w);//此处写成add_edge(u,v,w),add_edge(v,u,0);
//修改,样例通过,提交70分。
//测试点7,9,10TLE。2019-2-7 22:44
//还算满意,以下为70分代码。
#include <stdio.h>
#include <string.h>
#define INF 999999999
struct node{
int to,w,next;
}e[6001000];
int N;//点数
int head[1003000],d[1003000],cnt=0,q[1003000],h,t,T;
int min(int a,int b){
return a<b?a:b;
}
void add_edge(int u,int v,int w){
e[cnt].to=v,e[cnt].w=w,e[cnt].next=head[u],head[u]=cnt,cnt++;
}
int bfs(int s){
int u,v,w,b;
memset(d,-1,sizeof(d));
h=t=1,q[t]=s,t++,d[s]=0;
while(h<t){
u=q[h];
b=head[u];
while(b!=-1){
v=e[b].to,w=e[b].w;
if(d[v]==-1&&w>0)d[v]=d[u]+1,q[t]=v,t++;
b=e[b].next;
}
h++;
}
return d[T]!=-1;
}
int dfs(int u,int exp){
int v,w,b,flow=0,tmp=0;
if(u==T)return exp;
b=head[u];
while(b!=-1){
v=e[b].to,w=e[b].w;
if(d[v]==d[u]+1&&w>0){
tmp=dfs(v,min(exp,w));
flow+=tmp;
exp-=tmp;
e[b].w-=tmp;
e[b^1].w+=tmp;
if(!exp)break;
}
b=e[b].next;
}
return flow;
}
int main(){
int n,m,i,j,u,v,w,ans=0;//此处写成 ans=-1
memset(head,-1,sizeof(head));//初始化为-1还是很不顺手
scanf("%d%d",&n,&m);
N=n*m,T=N;//点数
for(i=1;i<=n;i++)//第一部分读取
for(j=1;j<m;j++){
scanf("%d",&w);
u=(i-1)*m+j,v=u+1;
add_edge(u,v,w),add_edge(v,u,w);//此处写成add_edge(u,v,w),add_edge(v,u,0);
}
for(i=1;i<=(n-1)*m;i++){//第二部分读取
scanf("%d",&w);
u=i,v=u+m;
add_edge(u,v,w),add_edge(v,u,w);//此处写成add_edge(u,v,w),add_edge(v,u,0);
}
for(i=1;i<n;i++)//第三部分读取
for(j=1;j<m;j++){
scanf("%d",&w);
u=(i-1)*m+j,v=u+m+1;
add_edge(u,v,w),add_edge(v,u,w);//此处写成add_edge(u,v,w),add_edge(v,u,0);
}
while(bfs(1))ans+=dfs(1,INF);
printf("%d\n",ans);
return 0;
}
tarjan
4.「bzoj1051」受欢迎的牛
并查集
5.「bzoj1202」狡猾的商人
//P2294 [HNOI2005]狡猾的商人
//在线测评地址https://www.luogu.org/problemnew/show/P2294
//带权并查集,没有接触过,确实想不到。
//该题有个问题,
//解题过程中,充次着2个数组,这2个数组,互相牵制
//写代码的时候,2个数组,可以用一个数组表示
//x,f[x]=p,y,f[y]=q;s[x],s[y],s[p],s[q]是前缀和
//合并公式计算如下
//d[x]定义为x到根节点p经历的所有点的权重,x,p属同一区块。
//d[y]定义为y到根节点q经历的所有点的权重,y,q属同一区块。
//d[x]=s[x]-s[p],d[y]=s[y]-s[q];
//d[y]-d[x]=s[y]-s[x]+s[p]-s[q];
//s[q]-s[p]=s[y]-s[x]+d[x]-d[y];因s[y]-s[x]=v;
//故s[q]-s[p]=v+d[x]-d[y],按定义
//d[q]=s[q]-s[p]
//故d[q]=v+d[x]-d[y];
//样例通过,提交AC。2019-3-29
//虽然AC,还是要弄清难点
//合并部分,公式推导,还是容易弄懂
//难在用并查集维护前缀和的理解。
#include <stdio.h>
#include <string.h>
#define maxn 110
int f[maxn],d[maxn];
int getf(int u){
int t;
if(f[u]==u)return u;//找到爸爸
t=getf(f[u]);
d[u]+=d[f[u]];//维护前缀和
return f[u]=t;
}
int main(){
int w,n,m,x,y,v,i,p,q,flag;
scanf("%d",&w);
while(w--){
flag=1,memset(d,0,sizeof(d));
scanf("%d%d",&n,&m);
for(i=0;i<=n;i++)f[i]=i;//初始化,每个节点都是自己的爸爸
for(i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&v),x--;
p=getf(x),q=getf(y);
if(p!=q){//合并
f[q]=p,d[q]=v+d[x]-d[y];//公式推导详见代码之前题解
}else if(d[y]-d[x]!=v){//同一个爸爸
flag=0;
break;
}
if(flag==0)break;
}
if(flag==1)printf("true\n");
else printf("false\n");
}
return 0;
}
RMQ
6.「vijos1514」天才的记忆
二分图匹配
7.「bzoj1059」矩阵游戏 「bzoj1191」超级英雄Hero
单调栈
8.「bzoj3039」玉蟾宫
DP
9.「bzoj1270」雷涛的小猫
数学
10.「bzoj1303」中位数图
11.「bzoj1192」鬼谷子的钱袋
//P2320 [HNOI2006]鬼谷子的钱袋
//在线测评地址https://www.luogu.org/problemnew/show/P2320
//https://www.luogu.org/problemnew/solution/P2320?page=4此文思路不错 作者: a526955194 更新时间: 2016-02-05 19:44 在Ta的博客查看
//我们可以假象一下 若m=12 则需要求得组合方案有(1 2 3 4 ……12)
//我们可以把他们分成两份 (1 2 …… 6) (7 8 ……12)称左边的为L 右边的为R
//很容易得知R中的每种方案都可以由(12/2)+左边的组合得出
//再次分成两份(1 2 3)(4 5 6)
//同理 当m为奇数时 显而易见地 只需把 (m/2)改为(m/2+1) 即可
//该题考点,二分思想
//log2(10^9)=30
//样例通过,提交AC 2019-5-16
#include <stdio.h>
int a[32];
int main(){
int m,i;
scanf("%d",&m);
a[0]=0;
while(m){
if(m%2==0)a[++a[0]]=m/2;
else a[++a[0]]=m/2+1;
m/=2;
}
printf("%d\n",a[0]);
for(i=a[0];i>=1;i--)printf("%d ",a[i]);
return 0;
}
递推+高精度
12.「bzoj1002」轮状病毒
spfa
13.「bzoj1003」物流运输trans
kruskal
14.「bzoj1601」灌水
快速幂
15.「codevs1851」越狱
//P3197 [HNOI2008]越狱
//在线测评地址https://www.luogu.org/problemnew/show/P3197
//将同种信仰的两个犯人捆在一起,进行处理,有M种可能
//需占用n-1个位置中的一个,有m*(n-1)种可能
//剩下的n-2个位置,每个都有m种宗教可能,即m^(n-2)种可能
//总的情况数是m*(n-1)*m^(n-2)
//检验样例m=2,n=3
//2*(3-1)*2^(3-2)=2*2*2^1=8
//结果是6,8!=6,有重复计算
//需重新改算法
//准备采用容斥原理,所有可能-不冲突=冲突
//所有可能=m^n
//不冲突=m*(m-1)*(m-1)*...*(m-1)=m*(m-1)^(n-1)
//检验样例m=2,n=3
//2^3=8,2*(2-1)^(3-1)=2*1=2,冲突=8-2=6
//开始编码
//m^n,(m-1)^(n-1)可采用快速幂,减法过程需小心,可能会遇到负数
//因 模 100003,故int够用
//样例通过,提交10分,测试点2,7-9TLE,3-6,10WA。2019-4-18
//重新读题,发现1≤M≤10^8 1≤N≤10^12
//而2^31=2147483648=2.2*10^9,很明显,数据读入就已越界
//遇到快速幂,还是long long省时省力。
//提交10分,测试点2-10WA. 2018-4-18
//反复读题,感觉没有问题
//造了几组数据,
//发现100000000 1000000000000
//输出竟然是-51236 大跌眼镜
//排查,发现ans=(quick_pow(m%mod,n)-m%mod*quick_pow((m-1)%mod,n-1)%mod+mod)%mod;//此处写成 ans=(quick_pow(m%mod,n)-m%mod*quick_pow((m-1)%mod,n-1)+mod)%mod;
//修改,提交AC.2019-4-18
#include <stdio.h>
#define LL long long
#define mod 100003
LL m,n;
LL quick_pow(LL a,LL b){
LL ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int main(){
LL ans;
scanf("%lld%lld",&m,&n);
ans=(quick_pow(m%mod,n)-m%mod*quick_pow((m-1)%mod,n-1)%mod+mod)%mod;//此处写成 ans=(quick_pow(m%mod,n)-m%mod*quick_pow((m-1)%mod,n-1)+mod)%mod;
printf("%lld\n",ans);
return 0;
}
贪心
16.「JoyOI1027」木瓜地
17.「JoyOI1099」超级书架
18.「JoyOI1090」母舰
重打 极大化思想
19.「vijos1055」奶牛浴场