高一下二月中旬日记

2.11

闲话

  • 上午 7:3012:00 打 accoders NOI 的模拟赛。
  • 中午去食堂吃饭的路上看见不少来学校参观的人。
  • 下午讲完 T1 后( huge 把多校完整讲题会议录制发在群里了)就放假了,这次让从 FY 门口出来,白把行李从宿舍带到机房了。

做题纪要

luogu P4123 [CQOI2016] 不同的最小割

  • 最小割树。

    点击查看代码
    const int inf=0x3f3f3f3f;
    set<int>s;
    struct MinCutTree
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[40010];
    	int head[910],dis[910],vis[910],cur[910],id[910],tmp[910],ans[910][910],cnt=1;
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)  id[i]=i;
    		memset(ans,0x3f,sizeof(ans));
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(tmp==0)  dis[e[i].to]=0;
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		for(int i=2;i<=cnt;i++)  e[i].flow=0;
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    	void build(int l,int r)
    	{
    		if(l>=r)  return;
    		int s=id[l],t=id[l+1],flow=Dinic(s,t),x=l-1,y=r+1;
    		for(int i=l;i<=r;i++)
    		{
    			if(vis[id[i]]==1)
    			{
    				x++;  tmp[x]=id[i];
    			}
    			else
    			{
    				y--;  tmp[y]=id[i];
    			}
    		}
    		for(int i=l;i<=r;i++)  id[i]=tmp[i];
    		build(l,x);  build(y,r);
    		for(int i=l;i<=x;i++)
    		{
    			for(int j=y;j<=r;j++)  ans[id[i]][id[j]]=ans[id[j]][id[i]]=min({ans[id[i]][s],ans[id[j]][t],flow});
    		}
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,u,v,w,i,j;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;
    		C.add(u,v,w);  C.add(v,u,w);
    	}
    	C.init(n);  C.build(1,n);
    	for(i=1;i<=n;i++)
    	{
    		for(j=i+1;j<=n;j++)  s.insert(C.ans[i][j]);
    	}
    	cout<<s.size()<<endl;
    	return 0;
    }
    

luogu P3329 [ZJOI2011] 最小割

  • 没办法做前缀和就暴力扫一遍即可。

    点击查看代码
    const int inf=0x3f3f3f3f;
    struct MinCutTree
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[15010];
    	int head[210],dis[210],vis[210],cur[210],id[210],tmp[210],ans[210][210],cnt=1;
    	void clear()
    	{
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		cnt=1;	
    	}
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)  id[i]=i;
    		memset(ans,0x3f,sizeof(ans));
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(tmp==0)  dis[e[i].to]=0;
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		for(int i=2;i<=cnt;i++)  e[i].flow=0;
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    	void build(int l,int r)
    	{
    		if(l>=r)  return;
    		int s=id[l],t=id[l+1],flow=Dinic(s,t),x=l-1,y=r+1;
    		for(int i=l;i<=r;i++)
    		{
    			if(vis[id[i]]==1)
    			{
    				x++;  tmp[x]=id[i];
    			}
    			else
    			{
    				y--;  tmp[y]=id[i];
    			}
    		}
    		for(int i=l;i<=r;i++)  id[i]=tmp[i];
    		build(l,x);  build(y,r);
    		for(int i=l;i<=x;i++)
    		{
    			for(int j=y;j<=r;j++)  ans[id[i]][id[j]]=ans[id[j]][id[i]]=min({ans[s][id[i]],ans[id[j]][t],flow});
    		}
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int t,n,m,q,u,v,w,x,ans=0,i,j,k;
    	cin>>t;
    	for(;t>=1;t--)
    	{
    		C.clear();
    		cin>>n>>m;
    		for(i=1;i<=m;i++)
    		{
    			cin>>u>>v>>w;
    			C.add(u,v,w);  C.add(v,u,w);
    		}
    		C.init(n);  C.build(1,n);
    		cin>>q;
    		for(k=1;k<=q;k++)
    		{
    			cin>>x;  ans=0;
    			for(i=1;i<=n;i++)
    			{
    				for(j=i+1;j<=n;j++)  ans+=(C.ans[i][j]<=x);
    			}
    			cout<<ans<<endl;
    		}
    		cout<<endl;
    	}
    	return 0;
    }
    

luogu P4214 [CERC2015] Juice Junctions

  • 最小割树。

    点击查看代码
    const int inf=0x3f3f3f3f;
    struct MinCutTree
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[20010];
    	int head[3010],dis[3010],vis[3010],cur[3010],id[3010],tmp[3010],ans[3010][3010],cnt=1;
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)  id[i]=i;
    		memset(ans,0x3f,sizeof(ans));
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(tmp==0)  dis[e[i].to]=0;
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		for(int i=2;i<=cnt;i++)  e[i].flow=0;
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    	void build(int l,int r)
    	{
    		if(l>=r)  return;
    		int s=id[l],t=id[l+1],flow=Dinic(s,t),x=l-1,y=r+1;
    		for(int i=l;i<=r;i++)
    		{
    			if(vis[id[i]]==1)
    			{
    				x++;  tmp[x]=id[i];
    			}
    			else
    			{
    				y--;  tmp[y]=id[i];
    			}
    		}
    		for(int i=l;i<=r;i++)  id[i]=tmp[i];
    		build(l,x);  build(y,r);
    		for(int i=l;i<=x;i++)
    		{
    			for(int j=y;j<=r;j++)  ans[id[i]][id[j]]=ans[id[j]][id[i]]=min({ans[s][id[i]],ans[id[j]][t],flow});
    		}
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,u,v,ans=0,i,j;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v;
    		C.add(u,v,1);  C.add(v,u,1);
    	}
    	C.init(n);  C.build(1,n);
    	for(i=1;i<=n;i++)
    	{
    		for(j=i+1;j<=n;j++)  ans+=C.ans[i][j];
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

luogu P10940 舞动的夜晚

  • 二分图最大匹配的不可行边,考虑转化成可行边的补集。

    • 在最大匹配中,不存在从 st 的增广路。而如果我们想反转匹配边 (u,v) 的状态,需要保证存在一条 vu 的增广路。
    • 必须边的限制条件为流量为 1 且端点在残量网络上属于两个不同的强连通分量。
    • 可行边的限制条件为流量为 1 或端点在残量网络上属于一个强连通分量。
    点击查看代码
    const int inf=0x3f3f3f3f;
    struct MaxFlow
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[300010];
    	int head[20010],dis[20010],vis[20010],cur[20010],dfn[20010],low[20010],ins[20010],col[20010],cnt=1,tot=0,scc_cnt=0;
    	stack<int>s;
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(tmp==0)  dis[e[i].to]=0;
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    	void tarjan(int x)
    	{
    		tot++;  dfn[x]=low[x]=tot;
    		s.push(x);  ins[x]=1;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].cap==e[i].flow)  continue;
    			if(dfn[e[i].to]==0)
    			{
    				tarjan(e[i].to);
    				low[x]=min(low[x],low[e[i].to]);
    			}
    			else  if(ins[e[i].to]==1)  low[x]=min(low[x],dfn[e[i].to]);
    		}
    		if(dfn[x]==low[x])
    		{
    			int tmp=0;
    			scc_cnt++;
    			while(tmp!=x)
    			{
    				tmp=s.top();  s.pop();  
    				ins[tmp]=0;  col[tmp]=scc_cnt;
    			}
    		}
    	}
    	void print(int n,int m,int q)
    	{
    		for(int i=1;i<=n+m;i++)  if(dfn[i]==0)  tarjan(i);
    		int ans=0;
    		for(int i=2;i<=2*q+1;i+=2)  ans+=(!(e[i].flow==1||col[e[i].to]==col[e[i^1].to]));
    		cout<<ans<<endl;
    		for(int i=2;i<=2*q+1;i+=2)  if(!(e[i].flow==1||col[e[i].to]==col[e[i^1].to]))  cout<<i/2<<" ";
    	}
    }F;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,q,s,t,u,v,i;
    	cin>>n>>m>>q;  s=n+m+1;  t=n+m+2;
    	for(i=1;i<=q;i++)
    	{
    		cin>>u>>v;
    		F.add(u,v+n,1);
    	}
    	for(i=1;i<=n;i++)  F.add(s,i,1);
    	for(i=1;i<=m;i++)  F.add(i+n,t,1);
    	F.Dinic(s,t);  F.print(n,m,q);
    	return 0;
    }
    

2.12

闲话

  • 颓。
  • 把牛客网络流的课听完了,没什么收获。

做题纪要

2.13

闲话

  • 上午要求 10:2011:20 进校,因担心堵车遂 8:50 就从家里出发了。
  • 下午 14:0018:24 打 accoders NOI 的模拟赛。
  • feifei 非说晚上 18:25 吃饭,但我们记得之前都是 18:20 吃饭的。
  • 晚上 feifei 说了下后续训练安排,周一、二、四、六打模拟赛,其余时间自由刷专题,好题分享以高二参加为主,没安排到的以后还有机会进行分享;然后讲题。

做题纪要

luogu P2764 最小路径覆盖问题

  • 多倍经验: UVA1184 Air Raid

  • 一个点至多被一个前驱和一个后继一起进行覆盖。

  • 将一个点拆成入点和出点后点数减最大流(点之间的匹配数)即为最小点路径覆盖数。

  • 输出路径时记录下后继节点即可。

    点击查看代码
    const int inf=0x3f3f3f3f;
    int n;
    struct MaxFlow
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[200010];
    	int head[510],dis[510],vis[510],cur[510],nxt[510],ins[510],cnt=1;
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow,int s)
    	{
    		if(x==t)  return flow;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum),s);
    				if(tmp==0)  dis[e[i].to]=0;
    				else
    				{
    					nxt[x]=e[i].to-n;
    					if(x!=s)  ins[e[i].to-n]=1;
    				}
    				sum+=tmp; 
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf,s);
    		return flow;
    	}
    }F;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int m,u,v,s,t,ans,i,j;
    	cin>>n>>m;  s=2*n+1;  t=2*n+2;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v;
    		F.add(u,v+n,1);
    	}
    	for(i=1;i<=n;i++)  
    	{
    		F.add(s,i,1);  F.add(i+n,t,1);
    	}
    	ans=n-F.Dinic(s,t);
    	for(i=1;i<=n;i++)
    	{
    		if(F.ins[i]==0)
    		{
    			for(j=i;j!=0&&j!=t;j=F.nxt[j])  cout<<j<<" ";
    			cout<<endl;
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

luogu P4043 [AHOI2014/JSOI2014] 支线剧情

  • 有源汇有上下界最小费用可行流。

    • 将寻找增广路的过程换成 SPFA 实现,注意算上一开始默认流的 down×w 的贡献。
    点击查看代码
    const int inf=0x3f3f3f3f;
    struct UpDownFlowMinCost
    {
    	struct node
    	{
    		int nxt,to,w,cap,flow;
    	}e[20010];
    	int head[310],dis[310],vis[310],cur[310],f[310],cnt=1,cost;
    	void add(int u,int v,int w,int _w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,_w,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,-_w,0,0};  head[v]=cnt;
    	}
    	void _add(int u,int v,int up,int down,int _w)
    	{
    		add(u,v,up-down,_w);  cost+=down*_w;
    		f[u]-=down;  f[v]+=down;
    	}
    	bool spfa(int s,int t)
    	{
    		memset(dis,0x3f,sizeof(dis));
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=0;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			vis[x]=0;
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;  cur[e[i].to]=head[e[i].to];
    					if(vis[e[i].to]==0)
    					{
    						q.push(e[i].to);  vis[e[i].to]=1;
    					}
    				}
    			}
    		}
    		return dis[t]<inf;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		vis[x]=1;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    				cost+=tmp*e[i].w;
    			}
    		}
    		vis[x]=0;
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		while(spfa(s,t)==true)  while(dfs(s,t,inf));
    		return cost;
    	}
    	int query(int n,int s,int t)
    	{
    		int _s=n+1,_t=n+2;
    		_add(t,s,inf,0,0);
    		for(int i=1;i<=n;i++)
    		{
    			if(f[i]>0)  add(_s,i,f[i],0);
    			if(f[i]<0)  add(i,_t,-f[i],0);
    		}
    		return Dinic(_s,_t);
    	}
    }F;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,v,w,s,t,k,i,j;
    	cin>>n;  s=1;  t=n+1;
    	for(i=1;i<=n;i++)
    	{
    		cin>>k;
    		for(j=1;j<=k;j++)
    		{
    			cin>>v>>w;
    			F._add(i,v,inf,1,w);
    		}
    		if(i!=1)  F._add(i,t,inf,0,0);
    	}
    	cout<<F.query(n+1,s,t)<<endl;
    	return 0;
    }
    

luogu P4126 [AHOI2009] 最小割

  • 最小割的可行边和必须边。

    • 最小割上的边一定满流,否则不满足“最小”的性质。
    • 可行边需满足端点在残量网络上属于两个不同的强连通分量,否则额外需要割掉一条环上的边,显然不优。
    • 必须边需满足端点恰好各属于 s,t 所属的强连通分量。
    点击查看代码
    const ll inf=0x3f3f3f3f3f3f3f3f;
    struct MinCut
    {
    	struct node
    	{
    		ll nxt,to,cap,flow;
    	}e[120010];
    	ll head[4010],dis[4010],vis[4010],cur[4010],dfn[4010],ins[4010],low[4010],col[4010],cnt=1,tot=0,scc_cnt=0;
    	stack<ll>s;
    	void add(ll u,ll v,ll w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	bool bfs(ll s,ll t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<ll>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			ll x=q.front();  q.pop();
    			for(ll i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	ll dfs(ll x,ll t,ll flow)
    	{
    		if(x==t)  return flow;
    		ll sum=0,tmp;
    		for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(tmp==0)  dis[e[i].to]=0;
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	ll Dinic(ll s,ll t)
    	{
    		ll flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    	void tarjan(ll x)
    	{	
    		tot++;  dfn[x]=low[x]=tot;
    		s.push(x);  ins[x]=1;
    		for(ll i=head[x];i!=0;i=e[i].nxt)
    		{
    			if(e[i].cap==e[i].flow)  continue;
    			if(dfn[e[i].to]==0)
    			{
    				tarjan(e[i].to);
    				low[x]=min(low[x],low[e[i].to]);
    			}
    			else  if(ins[e[i].to]==1)  low[x]=min(low[x],dfn[e[i].to]);
    		}
    		if(dfn[x]==low[x])
    		{
    			int tmp=0;
    			scc_cnt++;
    			while(tmp!=x)
    			{
    				tmp=s.top();  s.pop();
    				ins[tmp]=0;  col[tmp]=scc_cnt;
    			}
    		}
    	}
    	void print(ll n,ll s,ll t)
    	{
    		for(ll i=1;i<=n;i++)  if(dfn[i]==0)   tarjan(i);
    		for(ll i=2;i<=cnt;i+=2)
    		{
    			cout<<(e[i].cap==e[i].flow&&col[e[i].to]!=col[e[i^1].to])<<" "
    			<<(e[i].cap==e[i].flow&&col[e[i].to]==col[t]&&col[e[i^1].to]==col[s])<<endl;
    		}
    	}
    }F;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,m,s,t,u,v,w,i;
    	cin>>n>>m>>s>>t;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;  F.add(u,v,w);
    	}
    	F.Dinic(s,t);  F.print(n,s,t);
    	return 0;
    }
    

2.14

闲话

做题纪要

CF204E Little Elephant and Strings

P940. 划分

CF343E Pumping Stations

  • 对当前最小割树上边权最小的边只走一次一定更优。

  • 按照这条边不断将树划分开递归处理即可。

    点击查看代码
    const int inf=0x3f3f3f3f;
    int ans=0;
    vector<int>p;
    struct MinCutTree
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[4010];
    	int head[210],dis[210],vis[210],cur[210],id[210],tmp[210],broke[210][210],u,v,minn,cnt=1;
    	vector<pair<int,int> >g[210];
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	void _add(int u,int v,int w)
    	{
    		g[u].push_back(make_pair(v,w));
    	}
    	void init(int n)
    	{
    		for(int i=1;i<=n;i++)  id[i]=i;
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		int ans=0,tmp;
    		for(int i=cur[x];i!=0&&ans<flow;i=e[i].nxt)
    		{
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-ans));
    				if(tmp==0)  dis[e[i].to]=0;
    				ans+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return ans;
    	}
    	int Dinic(int s,int t)
    	{
    		for(int i=2;i<=cnt;i++)  e[i].flow=0;
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    	void build(int l,int r)
    	{
    		if(l>=r)  return;
    		int s=id[l],t=id[l+1],flow=Dinic(s,t),x=l-1,y=r+1;
    		_add(s,t,flow);  _add(t,s,flow);
    		for(int i=l;i<=r;i++)
    		{
    			if(vis[id[i]]==1)
    			{
    				x++;  tmp[x]=id[i];
    			}
    			else
    			{
    				y--;  tmp[y]=id[i];
    			}
    		}
    		for(int i=l;i<=r;i++)  id[i]=tmp[i];
    		build(l,x);  build(y,r);
    	}
    	bool get_minn(int x,int fa)
    	{
    		bool flag=0;
    		for(int i=0;i<g[x].size();i++)
    		{
    			if(g[x][i].first!=fa&&broke[x][g[x][i].first]==0)
    			{
    				if(g[x][i].second<minn)
    				{
    					u=x;  v=g[x][i].first;  minn=g[x][i].second;
    				}
    				get_minn(g[x][i].first,x);
    				flag=1;
    			}
    		}
    		return flag;
    	}
    	void divide(int x)
    	{
    		u=v=0;  minn=inf;
    		if(get_minn(x,0)==true)
    		{
    			broke[u][v]=broke[v][u]=1;
    			ans+=minn;
    			int _u=u,_v=v;
    			divide(_u);  divide(_v);
    		}
    		else  p.push_back(x);
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,u,v,w,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;
    		C.add(u,v,w);  C.add(v,u,w);
    	}
    	C.init(n);  C.build(1,n);  C.divide(1);
    	cout<<ans<<endl;
    	for(i=0;i<p.size();i++)  cout<<p[i]<<" ";
    	return 0;
    }
    

CF708D Incorrect Flow

luogu P2805 [NOI2009] 植物大战僵尸

  • 最大权闭合子图。

  • 正点权植物向源点连边,负点权植物向汇点连边,将原图中的保护(包括从右往左攻击)看作连边(被攻击向保护连一条容量为 的边)。正点权之和减去最小割即为所求。

  • 通过拓扑排序仅保留非环上的边用于建图。

    点击查看代码
    const int inf=0x3f3f3f3f;
    int a[610],din[610],vis[610];
    vector<int>e[610];
    void top_sort(int n)
    {
    	queue<int>q;
    	for(int i=1;i<=n;i++)
    	{
    		if(din[i]==0)
    		{
    			q.push(i);  vis[i]=1;
    		}
    	}
    	while(q.empty()==0)
    	{
    		int x=q.front();  q.pop();
    		for(int i=0;i<e[x].size();i++)
    		{
    			din[e[x][i]]--;
    			if(din[e[x][i]]==0)
    			{
    				q.push(e[x][i]);  vis[e[x][i]]=1;
    			}
    		}
    	}
    }
    struct MinCut
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[1440010];
    	int head[610],dis[610],vis[610],cur[610],cnt=1;
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(tmp==0)  dis[e[i].to]=0;
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,u,v,s,t,c,sum=0,i,j,k;
    	cin>>n>>m;  s=n*m+1;  t=n*m+2;
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m;j++)
    		{
    			cin>>a[(i-1)*m+j]>>c;
    			for(k=1;k<=c;k++)
    			{
    				cin>>u>>v;  u++;  v++;
    				e[(i-1)*m+j].push_back((u-1)*m+v);
    				din[(u-1)*m+v]++;
    			}
    			if(j-1>=1)
    			{
    				e[(i-1)*m+j].push_back((i-1)*m+j-1);
    				din[(i-1)*m+j-1]++;
    			}
    		}
    	}
    	top_sort(n*m);
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m;j++)
    		{
    			if(vis[(i-1)*m+j]==0)  continue;
    			if(a[(i-1)*m+j]>=0)  
    			{
    				C.add(s,(i-1)*m+j,a[(i-1)*m+j]);
    				sum+=a[(i-1)*m+j];
    			}
    			else  C.add((i-1)*m+j,t,-a[(i-1)*m+j]);
    			for(k=0;k<e[(i-1)*m+j].size();k++)
    			{
    				if(vis[e[(i-1)*m+j][k]]==1)  C.add(e[(i-1)*m+j][k],(i-1)*m+j,inf);
    			}
    		}
    	}
    	cout<<sum-C.Dinic(s,t)<<endl;
    	return 0;
    }
    

luogu P3749 [六省联考 2017] 寿司餐厅

  • 最大权闭合子图。

  • 区间包含关系构成了一个 DAG ;将 cx 的贡献挂在 di,i 上方便处理。

  • ai 建立虚拟节点与 di,i 相连。

    点击查看代码
    const int inf=0x3f3f3f3f;
    int a[110],d[110][110],id[110][110],tot;
    struct MinCut
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[80010];
    	int head[12010],dis[12010],vis[12010],cur[12010],cnt=1;
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(tmp==0)  dis[e[i].to]=0;
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif  
    	int n,m,s,t,sum=0,i,j;
    	cin>>n>>m;  s=1;  tot=t=2;
    	for(i=1;i<=n;i++)  cin>>a[i];
    	for(i=1;i<=n;i++)  
    	{
    		for(j=i;j<=n;j++)  
    		{
    			cin>>d[i][j];
    			tot++;  id[i][j]=tot;
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		for(j=i;j<=n;j++)
    		{
    			if(i==j)
    			{
    				C.add(id[i][j],tot+a[i],inf);
    				if(d[i][j]-a[i]>=0)  
    				{
    					C.add(s,id[i][j],d[i][j]-a[i]);  sum+=d[i][j]-a[i];
    				}
    				else  C.add(id[i][j],t,-(d[i][j]-a[i]));
    			}
    			else
    			{
    				C.add(id[i][j],id[i+1][j],inf);  C.add(id[i][j],id[i][j-1],inf);
    				if(d[i][j]>=0)  
    				{
    					C.add(s,id[i][j],d[i][j]);  sum+=d[i][j];
    				}
    				else  C.add(id[i][j],t,-d[i][j]);
    			}
    		}
    	}
    	for(i=1;i<=1000;i++)  C.add(tot+i,t,m*i*i);
    	cout<<sum-C.Dinic(s,t)<<endl;
    	return 0;
    }   
    

QOJ 1359. Setting Maps

AT_abc388_f [ABC388F] Dangerous Sugoroku

  • fi 表示是否能走到 i ,状态转移方程为 fi=orj=ibiafj

  • bitset 优化矩阵快速幂转移即可。

    点击查看代码
    struct Matrix
    {
    	bitset<25>ma[25];
    	Matrix operator * (const Matrix &another) const
    	{
    		Matrix tmp;
    		for(ll i=0;i<=19;i++)
    		{
    			for(ll h=0;h<=19;h++)  
    			{
    				if(ma[i][h]==1)  tmp.ma[i]|=another.ma[h];
    			}
    		}
    		return tmp;
    	}
    }f,base;
    Matrix qpow(Matrix a,ll b)
    {
    	Matrix ans;
    	for(ll i=0;i<=19;i++)  ans.ma[i][i]=1;
    	while(b)
    	{
    		if(b&1)  ans=ans*a;
    		b>>=1;
    		a=a*a;
    	}
    	return ans;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,m,a,b,l,r,last=1,i,j;
    	cin>>n>>m>>a>>b;
    	f.ma[0][19]=1;
    	for(i=0;i<=18;i++)  base.ma[i+1][i]=1;
    	for(i=a;i<=b;i++)  base.ma[19-i+1][19]=1;
    	for(i=1;i<=m;i++)
    	{
    		cin>>l>>r;
    		f=f*qpow(base,r-last);
    		for(j=max(l,r-19);j<=r;j++)  f.ma[0][19-(r-j)]=0;
    		last=r;
    	}
    	f=f*qpow(base,n-last);
    	cout<<(f.ma[0][19]==1?"Yes":"No")<<endl;
    	return 0;
    }
    

luogu P4553 80人环游世界

  • 拆成入点和出点后新建一个点用来保证可以花费 0 的代价直接到达某一个城市的限制。

    点击查看代码
    const ll inf=0x3f3f3f3f3f3f3f3f;
    ll a[2010];
    struct MaxFlowMinCost
    {
    	struct node
    	{
    		ll nxt,to,w,cap,flow;
    	}e[48010];
    	ll head[4010],dis[4010],cur[4010],vis[4010],cnt=1,cost;
    	void add(ll u,ll v,ll w,ll _w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,_w,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,-_w,0,0};  head[v]=cnt;
    	}
    	bool spfa(ll s,ll t)
    	{
    		memset(dis,0x3f,sizeof(dis));
    		memset(vis,0,sizeof(vis));
    		queue<ll>q;
    		dis[s]=0;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			ll x=q.front();  q.pop();
    			vis[x]=0;
    			for(ll i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;  cur[e[i].to]=head[e[i].to];
    					if(vis[e[i].to]==0)
    					{
    						q.push(e[i].to);  vis[e[i].to]=1;
    					}
    				}
    			}
    		}
    		return dis[t]<inf;
    	}
    	ll dfs(ll x,ll t,ll flow)
    	{
    		if(x==t)  return flow;
    		vis[x]=1;
    		ll sum=0,tmp;
    		for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    				cost+=tmp*e[i].w;
    			}
    		}
    		vis[x]=0;
    		return sum;
    	}
    	ll Dinic(ll s,ll t)
    	{
    		cost=0;
    		while(spfa(s,t)==true)  while(dfs(s,t,inf)!=0);
    		return cost;
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,m,s,t,_s,x,i,j;
    	cin>>n>>m;  s=2*n+1;  t=2*n+2;  _s=2*n+3;
    	C.add(s,_s,m,0);
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];  C.add(_s,i,inf,0);
    		C.add(s,i+n,a[i],0);  C.add(i,t,a[i],0);
    	}
    	for(i=1;i<=n;i++)
    	{
    		for(j=i+1;j<=n;j++)
    		{
    			cin>>x;
    			if(x!=-1)  C.add(i+n,j,inf,x);
    		}
    	}
    	cout<<C.Dinic(s,t)<<endl;
    	return 0;
    }
    
    

luogu P2469 [SDOI2010] 星际竞速

  • 可以花费 ai 的代价直接到达一个星球。

    点击查看代码
    const ll inf=0x3f3f3f3f3f3f3f3f;
    ll a[2010];
    struct MaxFlowMinCost
    {
    	struct node
    	{
    		ll nxt,to,w,cap,flow;
    	}e[48010];
    	ll head[4010],dis[4010],cur[4010],vis[4010],cnt=1,cost;
    	void add(ll u,ll v,ll w,ll _w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,_w,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,-_w,0,0};  head[v]=cnt;
    	}
    	bool spfa(ll s,ll t)
    	{
    		memset(dis,0x3f,sizeof(dis));
    		memset(vis,0,sizeof(vis));
    		queue<ll>q;
    		dis[s]=0;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			ll x=q.front();  q.pop();
    			vis[x]=0;
    			for(ll i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;  cur[e[i].to]=head[e[i].to];
    					if(vis[e[i].to]==0)
    					{
    						q.push(e[i].to);  vis[e[i].to]=1;
    					}
    				}
    			}
    		}
    		return dis[t]<inf;
    	}
    	ll dfs(ll x,ll t,ll flow)
    	{
    		if(x==t)  return flow;
    		vis[x]=1;
    		ll sum=0,tmp;
    		for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    				cost+=tmp*e[i].w;
    			}
    		}
    		vis[x]=0;
    		return sum;
    	}
    	ll Dinic(ll s,ll t)
    	{
    		cost=0;
    		while(spfa(s,t)==true)  while(dfs(s,t,inf)!=0);
    		return cost;
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,m,s,t,u,v,w,i;
    	cin>>n>>m;  s=2*n+1;  t=2*n+2;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];  C.add(s,i,inf,a[i]);
    		C.add(s,i+n,1,0);  C.add(i,t,1,0);
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;
    		if(u>v) swap(u,v);
    		C.add(u+n,v,inf,w);
    	}
    	cout<<C.Dinic(s,t)<<endl;
    	return 0;
    }
    
    

luogu B4142 [语言月赛 202502] 蛋挞烤制

  • 顺序结构。

    点击查看代码
    ll v1,v2,v3,e,m,t;
    cin>>v1>>v2>>v3>>e>>m>>t;
    cout<<ceil(ceil(1.0*(e*v1+m*v2)/v3)/t);
    

luogu B4143 [语言月赛 202502] 地铁环线

  • 分支结构。

    点击查看代码
    ll n,x,y,sum;
    cin>>n>>x>>y;  sum=abs(x-y);
    if(sum<n-sum)  cout<<((y>x)?"Clockwise Loop":"Counter-clockwise Loop")<<endl;
    if(sum>n-sum)  cout<<((y<x)?"Clockwise Loop":"Counter-clockwise Loop")<<endl;
    if(sum==n-sum)  cout<<"\"Wonderful\""<<endl;
    

luogu B4144 [语言月赛 202502] 随机数

  • 贴着上界放,至多调整一遍。

    点击查看代码
    ll a[1000010];
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,l,r,sum=0,i;
    	cin>>n>>l>>r;
    	for(i=1;i<=n;i++)
    	{
    		a[i]=r/n;
    		sum+=a[i];
    	}  
    	if(sum>r)
    	{
    		for(i=1;i<=n&&sum>r;i++)
    		{
    			a[i]--;
    			sum--;
    		}
    	}
    	else  if(sum<l)
    	{
    		for(i=1;i<=n&&sum<l;i++)
    		{
    			a[i]++;
    			sum++;
    		}
    	}
    	for(i=1;i<=n;i++)  cout<<a[i]<<" ";
    	return 0;
    }
    

luogu B4145 [语言月赛 202502] 披萨订单

  • 循环结构,不要用 map

    点击查看代码
    int cnt[10000010];
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int x,y,k,maxx=0,i,j,h;
    	cin>>x>>y>>k;
    	for(i=0;i<=x;i++)
    	{
    		for(j=1;j<=y;j++)
    		{
    			for(h=0;h<=k;h++)
    			{
    				maxx=max(maxx,(i+j)^h);
    				cnt[(i+j)^h]++;
    			}
    		}
    	}
    	cout<<maxx<<endl;
    	cout<<cnt[maxx]<<endl;
    	return 0;
    }
    

luogu B4146 [语言月赛 202502] 本俗妙手不如举手

  • 循环结构。

    点击查看代码
    int a[5010];
    int check(int n,int l,int k)
    {
    	for(int i=l;i<=l+k-1;i++)  a[i]-=2;
    	int sum=0;
    	for(int i=1;i<=n;i++)  sum+=(99-a[i]>a[i]);
    	for(int i=l;i<=l+k-1;i++)  a[i]+=2;
    	return sum>n-sum;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,k,ans=0,i;
    	cin>>n>>k;
    	for(i=1;i<=n;i++)  cin>>a[i];
    	for(i=1;i+k-1<=n;i++)  ans+=check(n,i,k);
    	cout<<ans<<endl;
    	return 0;
    }
    

luogu B4149 [语言月赛 202502] 积木重合

  • [ABC361B] Intersection of Cuboids ,三维分开算最后再乘起来。 O(V) 可以接受,直接冲过去。

    点击查看代码
    ll cnt[1000010];
    ll f(ll l1,ll r1,ll l2,ll r2)
    {
    	memset(cnt,0,sizeof(cnt));
    	ll sum=0;
    	for(ll i=l1;i<=r1;i++)  cnt[i]++;
    	for(ll i=l2;i<=r2;i++)  cnt[i]++;
    	for(ll i=1;i<=1000000;i++)  sum+=(cnt[i]==2);
    	return sum;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll x1,x2,y1,y2,z1,z2;
    	ll _x1,_x2,_y1,_y2,_z1,_z2;
    	cin>>x1>>y1>>z1>>x2>>y2>>z2;
    	cin>>_x1>>_y1>>_z1>>_x2>>_y2>>_z2;
    	cout<<f(x1,x2,_x1,_x2)*f(y1,y2,_y1,_y2)*f(z1,z2,_z1,_z2)<<endl;
    	return 0;
    }
    

luogu B4148 [语言月赛 202502] IPv6

  • 模拟。

    点击查看代码
    char s[1200];
    vector<int>c;
    int val(char x)
    {
    	if('0'<=x&&x<='9')  return x-'0';
    	return x-'A'+10; 
    }
    void print(int x)
    {
    	c.clear();
    	while(x)
    	{
    		c.push_back(x%2);
    		x/=2;
    	}
    	while(c.size()<16)  c.push_back(0);
    	reverse(c.begin(),c.end());
    	for(int i=0;i<c.size();i++)  cout<<c[i];
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int last=0,n,cnt=0,id=0,num=0,i;
    	cin>>(s+1); n=strlen(s+1);
    	for(i=1;i<=n;i++)
    	{
    		cnt+=(s[i]==':'&&s[i+1]!=':');
    		if(s[i]==':'&&s[i+1]==':')  id=i;
    	}
    	for(i=1;i<=id;i++)
    	{
    		if(s[i]==':')
    		{
    			print(num);
    			num=0;
    		}
    		else  num=num*16+val(s[i]);
    	}
    	for(i=1;i<=6-cnt;i++)  print(0);
    	num=0;  s[n+1]=':';
    	for(i=id+1;i<=n+1;i++)
    	{
    		if(s[i]==':')
    		{
    			print(num);
    			num=0;
    		}
    		else  num=num*16+val(s[i]);
    	}
    	return 0;
    }
    

luogu B4147 [语言月赛 202502] 沿轴求和

  • mapvector

    点击查看代码
    ll d[20],a[1000010],p[20],w[1000010];
    map<vector<ll>,ll>c;
    vector<ll>s,tmp;
    void dfs(ll pos,ll n,ll x)
    {
    	if(pos==n+1)
    	{
    		for(ll i=0;i<s.size();i++)  cout<<s[i]<<" ";
    		ll sum=0;
    		for(ll i=0;i<=d[x]-1;i++)
    		{
    			tmp.clear();
    			for(ll j=1;j<=x-1;j++)  tmp.push_back(s[j-1]);
    			tmp.push_back(i);
    			for(ll j=x+1;j<=n;j++)  tmp.push_back(s[j-2]);
    			sum+=c[tmp];
    		}
    		cout<<sum<<endl;
    	}
    	else
    	{
    		if(pos==x)  dfs(pos+1,n,x);
    		else
    		{
    			for(ll i=0;i<=d[pos]-1;i++)  
    			{
    				s.push_back(i);
    				dfs(pos+1,n,x);
    				s.pop_back();
    			}
    		}
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,x,m=1,id,i,j;
    	cin>>n>>x;  x++;
    	for(i=1;i<=n;i++) 
    	{
    		cin>>d[i];
    		m*=d[i];
    	}
    	for(i=1;i<=m;i++)
    	{
    		id=1;
    		s.clear();
    		for(j=1;j<=n;j++)
    		{
    			cin>>p[j];
    			s.push_back(p[j]);
    		}
    		cin>>c[s];
    	}
    	s.clear();
    	dfs(1,n,x);
    	return 0;
    }
    

2.15

闲话

  • 上午 7:3012:00 打 accoders NOI 的模拟赛。
  • 下午讲题。 miaomiao@wkh2008 桌子上的蛇拿起来疑惑地看了看,还挤压下了头部。
  • 没有体活。

做题纪要

luogu P6054 [RC-02] 开门大吉

  • (i,x)(j,min(m+1,x+k)) 连一条容量为 的有向边来限制。

  • 求最大流时发现无解时直接退出。

    点击查看代码
    const double eps=1e-8,inf=0x3f3f3f3f;
    int a[110],id[110][110],tot;
    double c[110],f[110],sum[110][110];
    bool cmp(double a,double b)
    {
    	return a-b>eps;
    }
    struct MinCut
    {
    	struct node
    	{
    		int nxt,to;
    		double cap,flow;
    	}e[300010];
    	int head[6510],cur[6510],dis[6510],vis[6510],cnt=1;
    	void clear()
    	{
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		cnt=1;
    	}
    	void add(int u,int v,double w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&cmp(e[i].cap,e[i].flow))
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	double dfs(int x,int t,double flow)
    	{
    		if(x==t)  return flow;
    		double sum=0,tmp;
    		for(int i=cur[x];i!=0&&cmp(flow,sum);i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(dis[e[i].to]==dis[x]+1&&cmp(e[i].cap,e[i].flow))
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(abs(tmp)<=eps)  dis[e[i].to]=0;
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	double Dinic(int s,int t)
    	{
    		double flow=0;
    		while(bfs(s,t)==true)  
    		{
    			flow+=dfs(s,t,inf);
    			if(flow>=inf)  return -1;
    		}
    		return flow;
    	}
    }C;	
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int testcase,n,m,k,q,s,t,u,v,w,i,j,h;
    	double ans;
    	scanf("%d",&testcase);
    	for(;testcase>=1;testcase--)
    	{
    		C.clear();
    		scanf("%d%d%d%d",&n,&m,&k,&q);  s=1;  tot=t=2;
    		for(i=1;i<=k;i++)
    		{
    			scanf("%d",&a[i]);  a[i]+=a[i-1];
    		}
    		for(i=1;i<=m+1;i++)
    		{
    			for(j=1;j<=n;j++)
    			{
    				tot++;  id[i][j]=tot;
    				sum[i][j]=c[k+1]=0;  f[0]=1;
    				if(i<=m)
    				{
    					for(h=1;h<=k;h++)
    					{
    						scanf("%lf",&c[h]);  f[h]=f[h-1]*c[h];
    					}
    					for(h=1;h<=k;h++)  sum[i][j]+=f[h]*(1-c[h+1])*a[h];
    				}
    				else
    				{
    					C.add(s,id[1][j],inf);  C.add(id[m+1][j],t,inf);
    				}
    				if(i>=2)C.add(id[i-1][j],id[i][j],sum[i-1][j]);
    			}
    		}
    		for(i=1;i<=q;i++)
    		{
    			scanf("%d%d%d",&u,&v,&w);
    			for(j=max(1,1-w);j<=m+1;j++)  C.add(id[j][v],id[min(j+w,m+1)][u],inf);
    		}
    		ans=C.Dinic(s,t);
    		if(ans!=-1)  printf("%.6lf\n",ans);
    		else  printf("-1\n");
    	}
    	return 0;
    }
    

2025多校冲刺省选模拟赛13 B. 网格图

luogu P10217 [省选联考 2024] 季风

  • 将限制条件合并一下,得到 |xi=1mxi|+|yi=1myi|mk ,将绝对值拆开并移项后得到 {i=1mxi+yi+mkx+yi=1mxiyi+mkxyi=1mxi+yi+mkx+yi=1mxiyi+mkxy ,合法的 m 必须四个条件都会满足。

  • 然后就和 CF1141E Superhero Battle 一样了,设 m=qn+r

  • si=j=1ixj+yj以第一个式子为例有 qsn+srx+y ,枚举 r 后按照 sn 的正负性可知 q 的上下界。

  • 特判 x=y=0 时输出 0

    点击查看代码
    ll x[1000010],y[1000010],sum[1000010],l[1000010],r[1000010];
    void solve(ll n,ll k,ll k1,ll k2,ll a,ll b)
    {
    	ll c=k1*a+k2*b;
    	for(ll i=1;i<=n;i++)  sum[i]=sum[i-1]+k1*x[i]+k2*y[i]+k;
    	if(sum[n]==0)
    	{
    		for(ll i=1;i<=n;i++)  if(sum[i]<c)  r[i]=-1;
    	}
    	if(sum[n]>0)
    	{
    		for(ll i=1;i<=n;i++)  if(sum[i]<c)  l[i]=max(l[i],(ll)ceil(1.0*(c-sum[i])/sum[n]));
    	}
    	if(sum[n]<0)
    	{
    		for(ll i=1;i<=n;i++)  r[i]=((sum[i]<c)?-1:min(r[i],(c-sum[i])/sum[n]));
    	}
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll t,n,k,a,b,ans,i,j;
    	scanf("%lld",&t);
    	for(j=1;j<=t;j++)
    	{
    		ans=0x3f3f3f3f3f3f3f3f;
    		cin>>n>>k>>a>>b;
    		for(i=1;i<=n;i++)
    		{
    			scanf("%lld%lld",&x[i],&y[i]);
    			l[i]=0;  r[i]=0x3f3f3f3f3f3f3f3f;
    		}
    		if(a==0&&b==0)  printf("0\n");
    		else
    		{
    			solve(n,k,1,1,a,b);  solve(n,k,1,-1,a,b); 
    			solve(n,k,-1,1,a,b);  solve(n,k,-1,-1,a,b);
    			for(i=1;i<=n;i++)
    			{
    				if(l[i]<=r[i])  ans=min(ans,i+l[i]*n);
    			}
    			printf("%lld\n",((ans==0x3f3f3f3f3f3f3f3f)?-1:ans));
    		}
    	}
    	return 0;
    }
    

luogu P5972 [PA 2019] Desant

LibreOJ 6039. 「雅礼集训 2017 Day5」珠宝 /「NAIPC2016」Jewel Thief

HZTG2487. 选拔

luogu P11615 【模板】哈希表

  • 拉链法可以理解成 Hash(x)x 连一条有向边。

    点击查看代码
    struct sx_hash_table
    {
    	const ull mod=10000019;
    	struct node
    	{
    		ull nxt,key,val;
    	}e[12000010];
    	ull head[12000010],cnt=0;
    	ull sx_hash(ull x)
    	{
    		return (x%mod+mod)%mod;
    	}
    	void update(ull x,ull y)
    	{
    		ull o=sx_hash(x);
    		for(ull i=head[o];i!=0;i=e[i].nxt)  
    		{
    			if(e[i].key==x) 
    			{
    				e[i].val=y;
    				return;
    			}
    		}
    		cnt++;  e[cnt]=(node){head[o],x,y};  head[o]=cnt;
    	}
    	ull query(ull x)
    	{
    		ull o=sx_hash(x);
    		for(ull i=head[o];i!=0;i=e[i].nxt)  
    		{
    			if(e[i].key==x)   return e[i].val;
    		}
    		return 0;
    	}
    }H;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ull n,x,y,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>x>>y;  ans+=H.query(x)*i;
    		H.update(x,y);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    
  • 开放寻址法基本思想是从 Hash(x) 不断向后寻找空余位置放 x

    点击查看代码
    struct sx_hash_table
    {
    	const ull mod=10000019;
    	ull vis[12000010],key[12000010],val[12000010];
    	ull sx_hash(ull x)
    	{
    		return (x%mod+mod)%mod;
    	}
    	void insert(ull x,ull y)
    	{
    		ull o=sx_hash(x);
    		for(;vis[o]==1&&key[o]!=x;o=(o+1)%mod);
    		vis[o]=1;  key[o]=x;  val[o]=y;
    	}
    	ull query(ull x)
    	{
    		ull o=sx_hash(x);
    		for(;vis[o]==1;o=(o+1)%mod)  if(key[o]==x)  return val[o];
    		return 0;
    	}
    }H;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ull n,x,y,ans=0,i;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		cin>>x>>y;  ans+=H.query(x)*i;
    		H.insert(x,y);
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

2.16

闲话

  • 早上有体活。
  • @GGrun 分享了 luogu P6578 [Ynoi2019] 魔法少女网站@int_R 分享了 luogu P11714 [清华集训 2014] 主旋律 | luogu P11736 [集训队互测 2015] 胡策的小树
  • 下午 huge 来了后我们哪吒 2 都看了吗,他觉得有几个番外挺有意思的,遂从 B 站找了个长达 19min 的番外解说视频给我们看(只看了约 5,6min 的样子)。
  • 晚上讲完题后 huge 说以后我们分享题目尽量选择难度适中、贴近省选的题目,不要因为做分享的题目把一整天的时间都花进去导致没时间去做自己的事情了,不要把分享题目变成一件很浪费时间的事情,可以把侧重点放到没怎么见过的 Trick 和套路上,一些比较恶心的题目等到省选后再去研究也挺有意思的。前几届这个时候基本上是没有自己的时间的,一周也就只有一天或两天留出来整理下题目,一周下来反倒还有五六道题还没改,学长也反映过这样效率也不一定高。现在讲题也是为了锻炼我们以后给多校、去机构兼职、打 ACM 时给别人讲题的能力,让我们在讲题前把要用的东西准备好,尽量把所有问题都搞懂,预测下听课的人可能会问什么问题以至于讲的时候不会被问出的问题卡壳。后面的同学可以吸取下前面同学分享的经历,看看自己准备分享的题目是否有瑕疵,临时和别人要分享的题目冲突了也没什么大的问题。

做题纪要

luogu P7173 【模板】有负圈的费用流

  • 对于负权边,先让其强制满流,然后考虑退流。

  • 具体地,从 uv 连一条容量上下界均为 f 、费用为 w 的边,然后从 vu 连一条容量上界为 f 、下界为 0 、费用为 w 的边。

  • 有源汇上下界最小费用最大流。费用等于可行流的费用加上最大流的费用。

    点击查看代码
    const int inf=0x3f3f3f3f;
    struct UpDownFlowMinCost
    {
    	struct node
    	{
    		int nxt,to,w,cap,flow;
    	}e[60010];
    	int head[210],dis[210],vis[210],cur[210],f[210],cnt=1,limit,val,flow,cost;
    	void add(int u,int v,int w,int _w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,_w,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,-_w,0,0};  head[v]=cnt;
    	}
    	void _add(int u,int v,int up,int down,int _w)
    	{
    		add(u,v,up-down,_w);  cost+=down*_w;
    		f[u]-=down;  f[v]+=down;
    	}
    	bool spfa(int s,int t)
    	{
    		memset(dis,0x3f,sizeof(dis));
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=0;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			vis[x]=0;
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow&&i<=limit)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;  cur[e[i].to]=head[e[i].to];
    					if(vis[e[i].to]==0)
    					{
    						q.push(e[i].to);  vis[e[i].to]=1;
    					}
    				}
    			}
    		}
    		return dis[t]<inf;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		vis[x]=1;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow&&i<=limit)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    				cost+=tmp*e[i].w;
    			}
    		}
    		vis[x]=0;
    		return sum;
    	}
    	void Dinic(int s,int t)
    	{
    		while(spfa(s,t)==true)
    		{
    			for(int x=dfs(s,t,inf);x!=0;x=dfs(s,t,inf))  flow+=x;
    		}
    	}
    	void query(int n,int s,int t)
    	{
    		int _s=n+1,_t=n+2,tmp;
    		_add(t,s,inf,0,0);  tmp=head[t];
    		for(int i=1;i<=n;i++)
    		{
    			if(f[i]>0)  add(_s,i,f[i],0);
    			if(f[i]<0)  add(i,_t,-f[i],0);
    		}
    		limit=inf;  Dinic(_s,_t);
    		limit=tmp;  flow=e[limit].flow;
    		e[limit].cap=e[limit].flow=e[limit^1].cap=e[limit^1].flow=0;
    		Dinic(s,t);
    	}
    }F;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,s,t,u,v,w,_w,i;
    	cin>>n>>m>>s>>t;
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w>>_w;
    		if(_w>=0)  F.add(u,v,w,_w);
    		else  
    		{
    			F._add(u,v,w,w,_w);  F.add(v,u,w,-_w);
    		}
    	}
    	F.query(n,s,t);
    	cout<<F.flow<<" "<<F.cost<<endl;
    	return 0;
    }
    

luogu P1402 酒店之王

  • 多倍经验: luogu P2891 [USACO07OPEN] Dining G

  • 三分图最大匹配。

  • 左、中、右部点分别为房间、人、菜品,需要将人拆成入点和出点来限制 1 的流量。

    点击查看代码
    const int inf=0x3f3f3f3f;
    struct MaxFlow
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[80010];
    	int head[510],dis[510],vis[510],cur[510],cnt=1;
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(tmp==0)  dis[e[i].to]=0;
    				sum+=tmp; 
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    }F;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,_m,s,t,x,i,j;
    	cin>>n>>m>>_m;  s=2*n+m+_m+1;  t=2*n+m+_m+2;
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m;j++)
    		{
    			cin>>x;
    			if(x==1)  F.add(j,m+i,1);
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=_m;j++)
    		{
    			cin>>x;
    			if(x==1)  F.add(n+m+i,2*n+m+j,1);
    		}
    	}
    	for(i=1;i<=m;i++)  F.add(s,i,1);
    	for(i=1;i<=n;i++)  F.add(m+i,n+m+i,1);
    	for(i=1;i<=_m;i++)  F.add(2*n+m+i,t,1);
    	cout<<F.Dinic(s,t)<<endl;
    	return 0;
    }
    

luogu P3965 [TJOI2013] 循环格

  • 拆成出点和入点后跑最小费用最大流。

  • 合法状态在最大流(二分图最大匹配)时取到,证明考虑在出点和入点之间加一条虚边后最大匹配下的二分图能被分解成有限个环。

    点击查看代码
    const int inf=0x3f3f3f3f,dx[4]={1,0,-1,0},dy[4]={0,-1,0,1};
    int val(char x)
    {
    	if(x=='R')  return 3;
    	if(x=='D')  return 0;
    	if(x=='L')  return 1;
    	return 2;
    }
    struct MaxFlowMinCost
    {
    	struct node
    	{
    		int nxt,to,w,cap,flow;
    	}e[3010];
    	int head[510],dis[510],vis[510],cur[510],cnt=1,cost;
    	void add(int u,int v,int w,int _w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,_w,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,-_w,0,0};  head[v]=cnt;
    	}
    	bool spfa(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		memset(dis,0x3f,sizeof(dis));
    		queue<int>q;
    		dis[s]=0;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			vis[x]=0;
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;  cur[e[i].to]=head[e[i].to];
    					if(vis[e[i].to]==0)
    					{
    						q.push(e[i].to);  vis[e[i].to]=1;
    					}
    				}
    			}
    		}
    		return dis[t]<inf;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		vis[x]=1;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    				cost+=tmp*e[i].w;
    			}
    		}
    		vis[x]=0;
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		cost=0;
    		while(spfa(s,t)==true)  while(dfs(s,t,inf)!=0);
    		return cost;
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,s,t,x,y,i,j,k;
    	char c;
    	cin>>n>>m;  s=2*n*m+1;  t=2*n*m+2;
    	for(i=0;i<n;i++)
    	{
    		for(j=0;j<m;j++)
    		{
    			cin>>c;
    			C.add(s,i*m+j+1,1,0);  C.add(n*m+i*m+j+1,t,1,0);  
    			for(k=0;k<=3;k++)
    			{
    				x=(i+dx[k]+n)%n;  y=(j+dy[k]+m)%m;
    				C.add(i*m+j+1,n*m+x*m+y+1,1,(k!=val(c)));
    			}
    		}
    	}
    	cout<<C.Dinic(s,t)<<endl;
    	return 0;
    }
    

luogu P3358 最长k可重区间集问题

  • 不交的区间可以同时选,不妨把相交的区间分开考虑。

  • sli 连一条 c=1,w=0 的边, liri 连一条 c=1,w=rl 的边, rit 连一条 c=1,w=0 的边;若 (li,ri),(lj,rj) 不相交则 rilj 连一条 c=1,w=0 的边。其中 s 流量至多为 k

  • 需要对点进行离散化。注意不相交区间之间的边不能重复连。

    点击查看代码
    const int inf=0x3f3f3f3f;
    int l[510],r[510],d[1010];
    pair<int,int>c[510];
    struct MaxFlowMaxCost
    {
    	struct node
    	{
    		int nxt,to,w,cap,flow;
    	}e[750010];
    	int head[1010],dis[1010],vis[1010],cur[1010],cnt=1,cost;
    	void add(int u,int v,int w,int _w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,_w,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,-_w,0,0};  head[v]=cnt;
    	}
    	bool spfa(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		memset(dis,-0x3f,sizeof(dis));
    		queue<int>q;
    		dis[s]=0;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			vis[x]=0;
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]<dis[x]+e[i].w&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;  cur[e[i].to]=head[e[i].to];
    					if(vis[e[i].to]==0)
    					{
    						q.push(e[i].to);  vis[e[i].to]=1;
    					}
    				}
    			}
    		}
    		return dis[t]>0;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		vis[x]=1;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    				cost+=tmp*e[i].w;
    			}
    		}
    		vis[x]=0;
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		cost=0;
    		while(spfa(s,t)==true)  while(dfs(s,t,inf)!=0);
    		return cost;
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,k,_s,s,t,i,j;
    	cin>>n>>k;  _s=2*n+1;  s=2*n+2;  t=2*n+3;
    	C.add(_s,s,k,0);
    	for(i=1;i<=n;i++)
    	{
    		cin>>c[i].first>>c[i].second;
    		d[0]++;  d[d[0]]=c[i].first;
    		d[0]++;  d[d[0]]=c[i].second;
    	}
    	sort(d+1,d+1+d[0]);  d[0]=unique(d+1,d+1+d[0])-(d+1);
    	sort(c+1,c+1+n);
    	for(i=1;i<=n;i++)
    	{
    		l[i]=lower_bound(d+1,d+1+d[0],c[i].first)-d;
    		r[i]=lower_bound(d+1,d+1+d[0],c[i].second)-d;
    		C.add(s,l[i],1,0);  C.add(r[i],t,1,0);
    		C.add(l[i],r[i],1,c[i].second-c[i].first);
    	}
    	for(i=1;i<=n;i++)
    	{
    		for(j=i+1;j<=n;j++)
    		{
    			if(c[i].second<=c[j].first)  C.add(r[i],l[j],1,0);
    		}
    	}
    	cout<<C.Dinic(_s,t)<<endl;
    	return 0;
    }	
    

luogu P4194 矩阵

  • 考虑二分答案,设当前二分出的答案为 mid

  • 以上面的式子为例,有 i=1nbi,j[i=1nai,jmid,i=1nai,j+mid]

  • 对行和列分别建虚点形成三分图后跑有源汇上下界可行流即可。

    点击查看代码
    const int inf=0x3f3f3f3f;
    int a[210][210],sum[2][210];
    struct UpDownFlow
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[162010];
    	int head[42010],dis[42010],vis[42010],f[42010],cur[42010],cnt=1;
    	void clear()
    	{
    		memset(e,0,sizeof(e));
    		memset(head,0,sizeof(head));
    		memset(f,0,sizeof(f));
    		cnt=1;
    	}
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	void _add(int u,int v,int up,int down)
    	{
    		add(u,v,up-down);
    		f[u]-=down;  f[v]+=down;
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(tmp==0)  dis[e[i].to]=0;
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    	bool check(int n,int s,int t)
    	{
    		int _s=n+1,_t=n+2,sum=0;
    		_add(t,s,inf,0);
    		for(int i=1;i<=n;i++)
    		{
    			if(f[i]>0)
    			{
    				sum+=f[i];
    				add(_s,i,f[i]);
    			}
    			if(f[i]<0)  add(i,_t,-f[i]);
    		}
    		return Dinic(_s,_t)>=sum;
    	}
    }F;
    bool check(int mid,int n,int m,int up,int down)
    {
    	F.clear();
    	int s=n*m+n+m+1,t=n*m+n+m+2;
    	for(int i=1;i<=n;i++)  F._add(s,n*m+i,sum[0][i]+mid,sum[0][i]-mid);
    	for(int i=1;i<=m;i++)  F._add(n*m+n+i,t,sum[1][i]+mid,sum[1][i]-mid);
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=m;j++)
    		{
    			F._add(n*m+i,(i-1)*m+j,up,down);
    			F._add((i-1)*m+j,n*m+n+j,up,down);
    		}
    	}
    	return F.check(n*m+n+m+2,s,t);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,up,down,l=0,r=40000000,mid,ans=0,i,j;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m;j++)
    		{
    			cin>>a[i][j];
    			sum[0][i]+=a[i][j];  sum[1][j]+=a[i][j];
    		}
    	}
    	cin>>down>>up;
    	while(l<=r)
    	{
    		mid=(l+r)/2;
    		if(check(mid,n,m,up,down)==true)
    		{
    			ans=mid;
    			r=mid-1;
    		}
    		else
    		{
    			l=mid+1;
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    } 
    

luogu P3980 [NOI2008] 志愿者招募

  • 一面对多面。

  • siti 连一条 c=,w=ci 的边;从 ii+1 连一条 c=ai,w=0 的边。此时总流量 0 的最小费用即为所求。

  • 新建虚拟节点 s1 流一条流量下界为 0 的边, n+1 向虚拟节点 t 流一条流量下界为 0 的边后跑有源汇上下界最小费用最小流。

  • 具体实现时真的去写有源汇上下界最小费用最小流没必要,将 c=ai 改成 c=ai ,虚拟节点连的边 cdown=0 改成 cup= 后跑最小费用最大流(一定是 )即可。

    点击查看代码
    const ll inf=0x3f3f3f3f3f3f3f3f;
    ll a[1010];
    struct MinCost
    {
    	struct node
    	{
    		ll nxt,to,w,cap,flow;
    	}e[40010];
    	ll head[1010],dis[1010],vis[1010],cur[1010],cnt=1,cost;
    	void add(ll u,ll v,ll w,ll _w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,_w,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,-_w,0,0};  head[v]=cnt;
    	}
    	bool spfa(ll s,ll t)
    	{
    		memset(vis,0,sizeof(vis));
    		memset(dis,0x3f,sizeof(dis));
    		queue<ll>q;
    		dis[s]=0;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			ll x=q.front();  q.pop();
    			vis[x]=0;
    			for(ll i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;  cur[e[i].to]=head[e[i].to];
    					if(vis[e[i].to]==0)
    					{
    						q.push(e[i].to);  vis[e[i].to]=1;
    					}
    				}
    			}
    		}
    		return dis[t]<inf;
    	}
    	ll dfs(ll x,ll t,ll flow)
    	{
    		if(x==t)  return flow;
    		vis[x]=1;
    		ll sum=0,tmp;
    		for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    				cost+=tmp*e[i].w;
    			}
    		}
    		vis[x]=0;
    		return sum;
    	}
    	ll Dinic(ll s,ll t)
    	{
    		cost=0;
    		while(spfa(s,t)==true)  while(dfs(s,t,inf)!=0);
    		return cost;
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,m,s,t,u,v,w,i;
    	cin>>n>>m;  s=n+2;  t=n+3;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];  C.add(i,i+1,inf-a[i],0);
    	}
    	C.add(s,1,inf,0);  C.add(n+1,t,inf,0);
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;  C.add(u,v+1,inf,w);
    	}
    	cout<<C.Dinic(s,t)<<endl;
    	return 0;
    }
    

luogu P4001 [ICPC-Beijing 2006] 狼抓兔子

  • 最大流最小割定理。

    点击查看代码
    const int inf=0x3f3f3f3f;
    struct MinCut
    {
    	struct node
    	{
    		int nxt,to,cap,flow;
    	}e[12000010];
    	int head[1000010],dis[1000010],vis[1000010],cur[1000010],cnt=1;
    	void add(int u,int v,int w)
    	{
    		cnt++;  e[cnt]=(node){head[u],v,w,0};  head[u]=cnt;
    		cnt++;  e[cnt]=(node){head[v],u,0,0};  head[v]=cnt;
    	}
    	bool bfs(int s,int t)
    	{
    		memset(vis,0,sizeof(vis));
    		queue<int>q;
    		dis[s]=1;  cur[s]=head[s];
    		q.push(s);  vis[s]=1;
    		while(q.empty()==0)
    		{
    			int x=q.front();  q.pop();
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(vis[e[i].to]==0&&e[i].cap>e[i].flow)
    				{
    					dis[e[i].to]=dis[x]+1;  cur[e[i].to]=head[e[i].to];
    					q.push(e[i].to);  vis[e[i].to]=1;
    					if(e[i].to==t)  return true;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int x,int t,int flow)
    	{
    		if(x==t)  return flow;
    		int sum=0,tmp;
    		for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
    		{
    			cur[x]=i;
    			if(dis[e[i].to]==dis[x]+1&&e[i].cap>e[i].flow)
    			{
    				tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
    				if(tmp==0)  dis[e[i].to]=0;
    				sum+=tmp;
    				e[i].flow+=tmp;  e[i^1].flow-=tmp;
    			}
    		}
    		return sum;
    	}
    	int Dinic(int s,int t)
    	{
    		int flow=0;
    		while(bfs(s,t)==true)  flow+=dfs(s,t,inf);
    		return flow;
    	}
    }C;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,s,t,x,i,j;
    	scanf("%d%d",&n,&m);  s=1;  t=n*m;
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m-1;j++)
    		{
    			scanf("%d",&x);
    			C.add((i-1)*m+j,(i-1)*m+j+1,x);  C.add((i-1)*m+j+1,(i-1)*m+j,x);
    		}
    	}
    	for(i=1;i<=n-1;i++)
    	{
    		for(j=1;j<=m;j++)
    		{
    			scanf("%d",&x);
    			C.add((i-1)*m+j,i*m+j,x);  C.add(i*m+j,(i-1)*m+j,x);
    		}
    	}
    	for(i=1;i<=n-1;i++)
    	{
    		for(j=1;j<=m-1;j++)
    		{
    			scanf("%d",&x);
    			C.add((i-1)*m+j,i*m+j+1,x);  C.add(i*m+j+1,(i-1)*m+j,x);
    		}
    	}
    	printf("%d\n",C.Dinic(s,t));
    	return 0;
    }
    

luogu P5304 [GXOI/GZOI2019] 旅行者

  • 考虑按位二进制分组。

  • 现在需要解决多源、汇 Dijkstra ,新建虚点 s 指向所有源点,虚点 t 被所有汇点指向。

    点击查看代码
    struct node
    {
    	ll nxt,to,w;
    }e[600010];
    ll head[100010],dis[100010],vis[100010],a[100010],u[500010],v[500010],w[500010],cnt=0;
    void add(ll u,ll v,ll w)
    {
    	cnt++;  e[cnt]=(node){head[u],v,w};  head[u]=cnt;
    }
    ll dijkstra(ll s,ll t)
    {
    	memset(dis,0x3f,sizeof(dis));
    	memset(vis,0,sizeof(vis));
    	priority_queue<pair<ll,ll> >q;
    	dis[s]=0;  q.push(make_pair(-dis[s],s));
    	while(q.empty()==0)
    	{
    		ll x=q.top().second;  q.pop();
    		if(vis[x]==0)
    		{
    			vis[x]=1;
    			for(ll i=head[x];i!=0;i=e[i].nxt)
    			{
    				if(dis[e[i].to]>dis[x]+e[i].w)
    				{
    					dis[e[i].to]=dis[x]+e[i].w;
    					q.push(make_pair(-dis[e[i].to],e[i].to));
    				}
    			}
    		}
    	}
    	return dis[t];
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll testcase,n,m,q,s,t,ans,i,j,k;
    	scanf("%lld",&testcase);
    	for(k=1;k<=testcase;k++)
    	{
    		scanf("%lld%lld%lld",&n,&m,&q);
    		s=n+1;  t=n+2;  ans=0x7f7f7f7f7f7f7f7f;
    		for(i=1;i<=m;i++)  scanf("%lld%lld%lld",&u[i],&v[i],&w[i]);
    		for(i=1;i<=q;i++)  scanf("%lld",&a[i]);
    		for(j=0;j<=18;j++)
    		{
    			fill(e+1,e+1+cnt,(node){0,0,0});
    			fill(head+1,head+1+n+2,0);
    			cnt=0;
    			for(i=1;i<=m;i++)  add(u[i],v[i],w[i]);
    			for(i=1;i<=q;i++)
    			{
    				if((a[i]>>j)&1)  add(s,a[i],0);
    				else  add(a[i],t,0);
    			}
    			ans=min(ans,dijkstra(s,t));
    			fill(e+1,e+1+cnt,(node){0,0,0});
    			fill(head+1,head+1+n+2,0);
    			cnt=0;
    			for(i=1;i<=m;i++)  add(u[i],v[i],w[i]);
    			for(i=1;i<=q;i++)
    			{
    				if((a[i]>>j)&1)  add(a[i],t,0);
    				else  add(s,a[i],0);
    			}
    			ans=min(ans,dijkstra(s,t));
    		}
    		printf("%lld\n",ans);
    	}
    	return 0;
    }
    

luogu P6578 [Ynoi2019] 魔法少女网站

  • 考虑将 x 的数看做 1 ,其余的数看做 0 。那么每个极长连续 1 段对答案的贡献为 len(len+1)2

  • 如果没有单点修改的话,将询问按照 x 排序后顺次修改随便即可。具体地,对于每个极长连续 1 段在开头记录结尾位置,在结尾记录开头位置。合并时分讨新加入一段,合并相邻的一段,合并相邻的两段。

  • 观察到每次修改至多有 O(1) 个点的信息会发生变化。考虑对序列进行分块,仅记录块内极长连续 1 段的信息和整块答案。散块暴力扫;整块补上开头和结尾极长连续 1 段的贡献后用总答案减去其贡献得到其他段的贡献。

  • 不妨对于每次询问枚举一遍修改操作,然后再撤销回去,再套个操作序列分块即可。具体实现时,可以先把修改的数扣出来在询问时在加回去。

  • 略带卡常。

    • vector 改成手写链式前向星。
    • 手写栈,撤销时减少一次拷贝。
    • 修改时减少分支判断。
    • 适当使用 bitsetbool
    • 稍调值域块和操作块的块长,两者大概为 n,3.65n
    点击查看代码
    struct change
    {
    	int tim,pos,val;
    }c[300010];
    struct ask
    {
    	int tim,l,r,val;
    	bool operator < (const ask &another) const
    	{
    		return val<another.val;
    	}
    }q[300010];
    struct node
    {
    	int nxt,to;
    }e[300010];
    struct quality
    {	
    	int x,id[2],pt[2],ans;
    	bool alone;
    }s[300010];
    int head[300010],a[300010],L[300010],R[300010],pos[300010],pt[300010],sum[300010],klen,ksum,c_cnt,q_cnt,cnt=0,top=0;
    bitset<300010>broke,vis,val;
    ll b[300010],ans[300010];
    void add(int u,int v)
    {
    	cnt++;  e[cnt]=(node){head[u],v};  head[u]=cnt;
    }
    void init(int n)
    {
    	klen=560;  ksum=n/klen;
    	for(int i=1;i<=ksum;i++)
    	{
    		L[i]=R[i-1]+1;  R[i]=R[i-1]+klen;
    	}
    	if(R[ksum]<n)
    	{
    		ksum++;
    		L[ksum]=R[ksum-1]+1;  R[ksum]=n;
    	}
    	for(int i=1;i<=ksum;i++)
    	{
    		for(int j=L[i];j<=R[i];j++)  pos[j]=i;
    	}
    }
    void update(int x,bool flag)
    {
    	if(val[x]==1)  return;// 无需修改时直接返回减小常数
    	int pre=x-1,nxt=x+1;
    	bool flag1=(val[pre]==1&&L[pos[x]]!=x),flag2=(val[nxt]==1&&R[pos[x]]!=x);
    	quality tmp;  tmp.x=x;  tmp.id[0]=tmp.pt[0]=tmp.id[1]=tmp.pt[1]=tmp.alone=0;
    	pt[x]=x;  val[x]=1;
    	if(flag1==1)// 只需要进行两次分支判断
    	{
    		if(flag2==1)
    		{
    			tmp.ans=(pre-pt[pre]+2)*(pt[nxt]-nxt+2);
    			tmp.id[0]=pt[pre];  tmp.pt[0]=pt[pt[pre]];  pt[pt[pre]]=pt[nxt];
    			tmp.id[1]=pt[nxt];  tmp.pt[1]=pt[pt[nxt]];  pt[pt[nxt]]=tmp.id[0];
    		}
    		else
    		{
    			tmp.ans=pre-pt[pre]+2;
    			tmp.id[0]=tmp.pt[0]=x;  pt[x]=pt[pre];
    			tmp.id[1]=pt[pre];  tmp.pt[1]=pt[pt[pre]];  pt[pt[pre]]=x;
    		}
    	}
    	else
    	{
    		if(flag2==0)  tmp.ans=tmp.alone=1;
    		else
    		{
    			tmp.ans=pt[nxt]-nxt+2;
    			tmp.id[0]=tmp.pt[0]=x;  pt[x]=pt[nxt];
    			tmp.id[1]=pt[nxt];  tmp.pt[1]=pt[pt[nxt]];  pt[pt[nxt]]=x;
    		}
    	}
    	sum[pos[x]]+=tmp.ans;
    	if(flag==1)  s[++top]=tmp;
    }
    ll query(int l,int r)
    {
    	ll ans=0;
    	int c1=0,c2=0;
    	if(pos[l]==pos[r])
    	{
    		for(int i=l;i<=r;i++)
    		{
    			if(val[i]==1)  c1++;
    			else  
    			{
    				ans+=b[c1];  c1=0;
    			}
    		}
    	}
    	else
    	{
    		for(int i=l;i<=R[pos[l]];i++)
    		{
    			if(val[i]==1)  c1++;
    			else
    			{
    				ans+=b[c1];  c1=0;
    			}
    		}
    		for(int i=r;i>=L[pos[r]];i--)
    		{
    			if(val[i]==1)  c2++;
    			else
    			{
    				ans+=b[c2];  c2=0;
    			}
    		}
    		for(int i=pos[l]+1;i<=pos[r]-1;i++)
    		{
    			if(pt[L[i]]==R[i])  c1+=R[i]-L[i]+1;
    			else
    			{
    				if(val[L[i]]==1)
    				{
    					c1+=pt[L[i]]-L[i]+1;  ans-=b[pt[L[i]]-L[i]+1];
    				}
    				ans+=b[c1]+sum[i];  c1=0;
    				if(val[R[i]]==1)
    				{
    					c1+=R[i]-pt[R[i]]+1;  ans-=b[R[i]-pt[R[i]]+1];
    				}
    			}
    		}
    	}
    	return ans+b[c1+c2];
    }
    void split()
    {
    	for(;top>=1;top--)
    	{// 减少一次拷贝
    		sum[pos[s[top].x]]-=s[top].ans;  val[s[top].x]=0;
    		if(s[top].alone==0)	
    		{
    			pt[s[top].id[0]]=s[top].pt[0];  pt[s[top].id[1]]=s[top].pt[1];
    		}
    	}
    }
    void solve(int n)
    {
    	val.reset();  broke.reset();
    	memset(pt,0,sizeof(pt));
    	memset(sum,0,sizeof(sum));
    	memset(e,0,sizeof(e));  cnt=0;
    	memset(head,0,sizeof(head));
    	sort(q+1,q+1+q_cnt);
    	for(int i=1;i<=c_cnt;i++)  broke[c[i].pos]=1;
    	for(int i=1;i<=n;i++)  if(broke[i]==0)  add(a[i],i);
    	for(int i=1,j=1;i<=q_cnt;i++)
    	{
    		for(;j<=q[i].val;j++)
    		{
    			for(int k=head[j];k!=0;k=e[k].nxt)  update(e[k].to,0);
    		}
    		for(int k=c_cnt;k>=1;k--)
    		{
    			if(c[k].tim<q[i].tim&&vis[c[k].pos]==0)
    			{
    				vis[c[k].pos]=1;
    				if(c[k].val<=q[i].val)  update(c[k].pos,1);
    			}
    		}
    		for(int k=1;k<=c_cnt;k++)
    		{
    			if(vis[c[k].pos]==0)
    			{
    				vis[c[k].pos]=1;
    				if(a[c[k].pos]<=q[i].val)  update(c[k].pos,1);
    			}
    		}
    		ans[q[i].tim]=query(q[i].l,q[i].r);
    		split();
    		for(int k=1;k<=c_cnt;k++)  vis[c[k].pos]=0;
    	}
    	for(int i=1;i<=c_cnt;i++)  a[c[i].pos]=c[i].val;
    	c_cnt=q_cnt=0;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,pd,limit=2000,i;	
    	scanf("%d%d",&n,&m);  init(n);
    	for(i=1;i<=n;i++)  
    	{
    		scanf("%d",&a[i]);  b[i]=1ll*i*(i+1)/2;
    	}
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d",&pd);
    		if(pd==1)
    		{
    			ans[i]=-1;
    			c_cnt++;  c[c_cnt].tim=i;
    			scanf("%d%d",&c[c_cnt].pos,&c[c_cnt].val);
    		}
    		else
    		{
    			q_cnt++;  q[q_cnt].tim=i;
    			scanf("%d%d%d",&q[q_cnt].l,&q[q_cnt].r,&q[q_cnt].val);
    		}
    		if(i%limit==0)  solve(n);
    	}
    	if(q_cnt!=0)  solve(n);
    	for(i=1;i<=m;i++)  if(ans[i]!=-1)  printf("%lld\n",ans[i]);
    	return 0;
    }
    

UVA1306 The K-League

2.17

闲话

  • 侯操时德育主任提到课表有所调整,新添加了若干科目;然后年级主任叭叭了一堆东西,于是就不用跑操了。
    • 人都是逼出来的。当你看见别人学什么东西特别轻松时,你有知道他在背后付出了多少努力吗······(后面忘了)
    • 一调中 1,2 部共 6 个理科实验班中 2 部排名分别为 1,4,6 ,但是我们要有一定的危机意识,大部分因为集训寒假作业做得少,甚至还导致了不能参加考试,特别是普通班作业写得就更少了,说明假期期间在家学习极其不自律。
    • 这学期会过得很快:四周后考三调,然后是期中,五月份学考,六月份就期末了。我们要拼搏,要努力,主动结交性格坚毅的朋友,带动自己更加坚定。上课不走神,做到老师讲某个知识点时自己能立刻反应出下句话可能要说什么······(后面忘了)
  • 上午 7:3012:00 打学校 OJ 的模拟赛。
  • 下午到机房后继续放昨天的番外解说视频。然后讲题。
  • 临吃晚饭时, @GGrunfeifei 说晚饭吃饭时间能不能调整到原来的 18:20 ,现在 18:25 去吃饭食堂里人太多了,然后 feifei 就让我们下去吃饭了。貌似 feifei 自始至终都不知道晚上到底几点吃饭。

做题纪要

luogu P4066 [SHOI2003] 吃豆豆

CF838D Airplane Arrangements

luogu P3511 [POI 2010] MOS-Bridges

luogu P3396 哈希冲突

  • 多倍经验: CF1207F Remainder Problem

  • 根号分治。>n 的暴力跳,否则预处理。

    点击查看代码
    int a[150010],sum[410][410];
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,klen,x,y,ans,i,j;
    	char pd;
    	cin>>n>>m;  klen=sqrt(n)+1;
    	for(i=1;i<=n;i++)  
    	{
    		cin>>a[i];
    		for(j=1;j<=klen;j++)  sum[j][i%j]+=a[i];
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd>>x>>y;
    		if(pd=='A')
    		{
    			if(x<=klen)  ans=sum[x][y];
    			else 
    			{
    				ans=0;
    				for(j=y;j<=n;j+=x)  ans+=a[j];
    			}
    			cout<<ans<<endl;
    		}
    		else
    		{
    			for(j=1;j<=klen;j++)  sum[j][x%j]+=y-a[x];
    			a[x]=y;
    		}
    	}
    	return 0;
    }
    

luogu P5309 [Ynoi2011] 初始化

  • 单点修改直接根号分治即可。

  • 对于区间修改,考虑套上一个序列分块,这样的话 n 的部分就不能简单地修改了。

  • 考虑分别统计 [l,r]n 的部分,不妨按照 x 将原序列划分成若干块,维护块内前缀和即可。

  • 可以使用 long long 来减少取模次数。

    点击查看代码
    const int p=1000000007;
    int pos[200010],L[200010],R[200010],klen,ksum;
    ll a[200010],sum[200010],pre[450][450];
    void init(int n)
    {
    	klen=sqrt(n)+1;  ksum=n/klen;
    	for(int i=1;i<=ksum;i++)
    	{
    		L[i]=R[i-1]+1;  R[i]=R[i-1]+klen;
    	}
    	if(R[ksum]<n)
    	{
    		ksum++;
    		L[ksum]=R[ksum-1]+1;  R[ksum]=n;
    	}
    	for(int i=1;i<=ksum;i++)
    	{
    		for(int j=L[i];j<=R[i];j++)
    		{
    			pos[j]=i;  sum[i]+=a[j];
    		}
    	}
    }
    void update(int x,int y,int z,int n)
    {	
    	if(x<=klen)
    	{
    		for(int i=y;i<=x;i++)  pre[x][i]+=z;
    	}
    	else
    	{
    		for(int i=y;i<=n;i+=x)
    		{
    			a[i]+=z;  sum[pos[i]]+=z;
    		}
    	}
    }
    int query(int l,int r)
    {
    	ll ans=0;
    	if(pos[l]==pos[r])
    	{
    		for(int i=l;i<=r;i++)  ans+=a[i];
    	}
    	else
    	{
    		for(int i=l;i<=R[pos[l]];i++)  ans+=a[i];
    		for(int i=pos[l]+1;i<=pos[r]-1;i++)  ans+=sum[i];
    		for(int i=L[pos[r]];i<=r;i++)  ans+=a[i];
    	}
    	for(int i=1;i<=klen;i++)
    		ans+=((r-1)/i-(l-1)/i)*pre[i][i]+pre[i][(r-1)%i+1]-pre[i][(l-1)%i];
    	return ans%p;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,m,pd,x,y,z,i;
    	scanf("%d%d",&n,&m);
    	for(i=1;i<=n;i++)  
    	{
    		scanf("%lld",&a[i]);  a[i]%=p;
    	}
    	init(n);
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&pd,&x,&y);
    		if(pd==1)
    		{
    			scanf("%d",&z);  z%=p;
    			update(x,y,z,n);
    		}
    		else  printf("%d\n",query(x,y));
    	}
    	return 0;
    }
    

HZTG5882. 饺子

CF1881G Anya and the Mysterious String

  • 只统计回文子串长度为 2,3 的即可。

    点击查看代码
    char s[200010];
    struct SMT
    {
    	struct node
    	{
    		int ans,lc,_lc,rc,_rc;
    		node()
    		{
    			ans=0;
    			lc=_lc=rc=_rc=-1;
    		}
    		node operator + (const node &another) const 
    		{
    			node tmp;
    			tmp.lc=lc;
    			tmp._lc=(_lc!=-1)?_lc:another.lc;
    			tmp.rc=another.rc;
    			tmp._rc=(another._rc!=-1)?another._rc:rc;
    			tmp.ans=ans|another.ans|(rc==another.lc)|(_rc!=-1&&_rc==another.lc)|(another._lc!=-1&&rc==another._lc);
    			return tmp;
    		}
    		node operator + (const int &another) const
    		{
    			node tmp;
    			tmp.ans=ans;
    			tmp.lc=(lc+another)%26;
    			tmp.rc=(rc+another)%26;
    			if(_lc!=-1)  tmp._lc=(_lc+another)%26;
    			if(_rc!=-1)  tmp._rc=(_rc+another)%26;
    			return tmp;
    		}
    	};
    	struct SegmentTree
    	{
    		node info;	
    		int lazy;
    	}tree[800010];
    	#define lson(rt) (rt<<1)
    	#define rson(rt) (rt<<1|1)
    	void pushdown(int rt)
    	{
    		tree[lson(rt)].info=tree[lson(rt)].info+tree[rt].lazy;
    		tree[lson(rt)].lazy=(tree[lson(rt)].lazy+tree[rt].lazy)%26;
    		tree[rson(rt)].info=tree[rson(rt)].info+tree[rt].lazy;
    		tree[rson(rt)].lazy=(tree[rson(rt)].lazy+tree[rt].lazy)%26;
    		tree[rt].lazy=0;
    	}
    	void pushup(int rt)
    	{
    		tree[rt].info=tree[lson(rt)].info+tree[rson(rt)].info;
    	}
    	void build(int rt,int l,int r)
    	{
    		tree[rt].lazy=0;
    		if(l==r)
    		{
    			tree[rt].info.ans=0;
    			tree[rt].info.lc=tree[rt].info.rc=s[l]-'a';
    			tree[rt].info._lc=tree[rt].info._rc=-1;
    			return;
    		}
    		int mid=(l+r)/2;
    		build(lson(rt),l,mid);  build(rson(rt),mid+1,r);
    		pushup(rt);
    	}
    	void update(int rt,int l,int r,int x,int y,int val)
    	{
    		if(x<=l&&r<=y)
    		{
    			tree[rt].info=tree[rt].info+val;
    			tree[rt].lazy=(tree[rt].lazy+val)%26;
    			return;
    		}
    		pushdown(rt);
    		int mid=(l+r)/2;
    		if(x<=mid)  update(lson(rt),l,mid,x,y,val);
    		if(y>mid)  update(rson(rt),mid+1,r,x,y,val);
    		pushup(rt);
    	}
    	node query(int rt,int l,int r,int x,int y)
    	{
    		if(x<=l&&r<=y)  return tree[rt].info;
    		pushdown(rt);
    		int mid=(l+r)/2;
    		if(y<=mid)  return query(lson(rt),l,mid,x,y);
    		if(x>mid)  return query(rson(rt),mid+1,r,x,y);
    		return query(lson(rt),l,mid,x,y)+query(rson(rt),mid+1,r,x,y);
    	}
    }T;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int t,n,m,pd,l,r,x,i,j;
    	scanf("%d",&t);
    	for(j=1;j<=t;j++)
    	{
    		scanf("%d%d %s",&n,&m,s+1);
    		T.build(1,1,n);  
    		for(i=1;i<=m;i++)
    		{
    			scanf("%d%d%d",&pd,&l,&r);
    			if(pd==1)
    			{
    				scanf("%d",&x);
    				T.update(1,1,n,l,r,x%26);
    			}
    			else
    			{
    				if(T.query(1,1,n,l,r).ans==1)  printf("NO\n");
    				else  printf("YES\n");
    			}
    		}
    	}
    	return 0;
    }
    
    

HZTG5883. 机关

2.18

闲话

  • 侯操时得知下午要跑课间操。
  • 上午 7:3012:00 打 accoders NOI 的模拟赛。
  • 下午到机房后把番外解说视频放完了。 huge 说后面这几天的训练计划进行了微调,添加了两次全真模拟,到时候我们都去 510 随机分配座位,看看自己在不同环境和不同设备下反应怎么样,减少了我们自己自主分配的时间。现在要让自己保持一个紧张的状态,昨天 field 还说有同学在水知乎呢,之前再怎么浪都过去了,要珍惜现在的时间。都已经坚持到现在了,能走到哪里就走到哪里。
    • 所以封掉了知乎、百度热搜、百度百科,百度搜索部分能够进行使用。
  • 然后讲题,讲完题时已经 15:10 了又赶紧去上体育课了,回来的路上正好碰见他们跑课间操了,直接润了。回到机房后 feifei 又强调了下训练计划的调整。
  • 回宿舍后得知因为下午课间操和奥赛课下课撞上了,所以各科教练都没让去跑课间操。
  • 临近 23:00 刚打算睡着的时候就听见楼下 FY 门口有施工的声音,听起来像来回把土堆到斗子里然后再倒出来。

做题纪要

luogu P5458 [BJOI2016] 水晶

luogu P5331 [SNOI2019] 通信

QOJ 9300. So Many Possibilities...

P1005. 排序

2.19

闲话

  • 候操时德育主任说今天晚新闻和晚一去开大会;团活课不上,补晚一的课;班主任说我们不用去开大会了,晚上奥赛课正常。
  • 到机房后发现 huge 桌子上有一个自己带的机械键盘。
  • @luobotianle 分享了 [ABC221G] Jumping sequence@ppllxx_9G 分享了 luogu P5163 WD与地图
  • 上午 feifei 让我们核对了下省选报名表的个人信息。

做题纪要

luogu P4140 奇数国

  • 两种操作等价于查询区间乘积的 φ 和单点修改,因为只有 60 个质数所以和 CF1114F Please, another Queries on Array? 一样做即可。

    点击查看代码
    const ll p=19961993,prime[65]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281};
    ll c[1010];
    ll qpow(ll a,ll b,ll p)
    {
    	ll ans=1;
    	while(b)
    	{
    		if(b&1)  ans=ans*a%p;
    		b>>=1;
    		a=a*a%p;
    	}
    	return ans;
    }
    struct SMT
    {
    	ll mul;
    	bitset<65>vis;
    	struct SegmentTree
    	{
    		ll mul;
    		bitset<65>vis;
    	}tree[400010];
    	#define lson(rt) (rt<<1)
    	#define rson(rt) (rt<<1|1)
    	void pushup(ll rt)
    	{
    		tree[rt].mul=tree[lson(rt)].mul*tree[rson(rt)].mul%p;
    		tree[rt].vis=tree[lson(rt)].vis|tree[rson(rt)].vis;
    	}
    	void build(ll rt,ll l,ll r)
    	{
    		if(l==r)
    		{
    			tree[rt].mul=3;  tree[rt].vis.reset();
    			tree[rt].vis[2]=1;
    			return;
    		}
    		ll mid=(l+r)/2;
    		build(lson(rt),l,mid);  build(rson(rt),mid+1,r);
    		pushup(rt);
    	}
    	void update(ll rt,ll l,ll r,ll pos,ll val)
    	{
    		if(l==r)
    		{
    			tree[rt].mul=val;  tree[rt].vis.reset();
    			for(ll i=1;i<=60;i++)  tree[rt].vis[i]=(val%prime[i]==0);
    			return;
    		}
    		ll mid=(l+r)/2;
    		if(pos<=mid)  update(lson(rt),l,mid,pos,val);
    		else  update(rson(rt),mid+1,r,pos,val);
    		pushup(rt);
    	}
    	void query(ll rt,ll l,ll r,ll x,ll y)
    	{
    		if(x<=l&&r<=y)
    		{
    			mul=mul*tree[rt].mul%p;
    			vis|=tree[rt].vis;
    			return;
    		}
    		ll mid=(l+r)/2;
    		if(x<=mid)  query(lson(rt),l,mid,x,y);
    		if(y>mid)  query(rson(rt),mid+1,r,x,y);
    	}
    	ll ask(ll rt,ll l,ll r,ll x,ll y)
    	{
    		mul=1;  vis.reset();
    		query(rt,l,r,x,y);
    		for(ll i=1;i<=60;i++)
    		{
    			if(vis[i]==1)  
    			{
    				mul=mul*c[i]%p;
    			}
    		}
    		return mul;
    	}
    }T;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll m,pd,l,r,i;
    	cin>>m;
    	for(i=1;i<=60;i++)  c[i]=(prime[i]-1)*qpow(prime[i],p-2,p)%p;
    	T.build(1,1,100000);
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd>>l>>r;
    		if(pd==0)  cout<<T.ask(1,1,100000,l,r)<<endl;
    		else  T.update(1,1,100000,l,r);
    	}
    	return 0;
    }
    

luogu P9754 [CSP-S 2023] 结构体

  • 模拟。

    • 对齐要求对地址的开始和结尾都有限制,需要注意。
    • 操作 4 本质是操作 3 的逆过程,基本做法相同。
    点击查看代码
    ll type_cnt=0,element_cnt=0,address=0;
    struct sx_type
    {
    	string name;
    	vector<ll>_type;
    	vector<string>_name;
    	ll siz,align;
    }member[110];
    struct sx_element
    {
    	string name;
    	sx_type type;
    	ll address;
    }element[110];
    map<string,ll>f;
    map<string,ll>g;
    map<pair<ll,ll>,ll>h;
    map<pair<ll,ll>,ll>::iterator it;
    string s,a[110],b[110];
    queue<string>q;
    void align(ll &address,ll limit)
    {
    	if(address%limit!=0)  address=(address/limit+1)*limit;
    }
    void init1(ll k)
    {
    	type_cnt++;  member[type_cnt].name=s;
    	for(ll i=1;i<=k;i++)
    	{
    		member[type_cnt]._type.push_back(f[a[i]]);
    		member[type_cnt]._name.push_back(b[i]);
    		member[type_cnt].align=max(member[type_cnt].align,member[f[a[i]]].align);
    		align(member[type_cnt].siz,member[f[a[i]]].align);
    		member[type_cnt].siz+=member[f[a[i]]].siz;
    	}
    	align(member[type_cnt].siz,member[type_cnt].align);
    	f[s]=type_cnt;
    }
    void init2()
    {
    	element_cnt++;  element[element_cnt].type=member[f[a[0]]];   element[element_cnt].name=b[0];
    	align(address,member[f[a[0]]].align);
    	element[element_cnt].address=address;
    	h[make_pair(address,address+member[f[a[0]]].siz-1)]=g[b[0]]=element_cnt;
    	address+=member[f[a[0]]].siz;
    }	
    ll query1()
    {
    	string t="";
    	for(ll i=0;i<s.size();i++)
    	{
    		if(s[i]=='.')
    		{
    			q.push(t);
    			t="";
    		}
    		else  t+=s[i];
    	}
    	q.push(t);
    	ll pos=element[g[q.front()]].address;
    	sx_type type=element[g[q.front()]].type,tmp;
    	q.pop();
    	while(q.empty()==0)
    	{
    		for(ll i=0;i<type._type.size();i++)
    		{
    			align(pos,member[type._type[i]].align);
    			if(type._name[i]==q.front())
    			{
    				tmp=member[type._type[i]];  type=tmp;
    				q.pop();
    				break;
    			}
    			else  pos+=member[type._type[i]].siz;
    		}
    	}
    	return pos;
    }
    string query2(ll k)
    {
    	if(k>=address)  return "ERR";
    	ll flag=0,pos=0,ed;
    	sx_element id;
    	for(it=h.begin();it!=h.end();it++)
    	{
    		if(it->first.first<=k&&k<=it->first.second)
    		{
    			id=element[it->second];
    			flag=1;
    			break;
    		}
    	}
    	if(flag==0)  return "ERR";
    	k-=id.address;
    	string t=id.name;
    	sx_type type=id.type,tmp;
    	while(type._type.empty()==0)
    	{
    		for(ll i=0;i<type._type.size();i++)
    		{
    			ed=pos+member[type._type[i]].siz;
    			if(pos<=k&&k<ed)
    			{
    				t+='.'+type._name[i];
    				tmp=member[type._type[i]];  type=tmp;
    				break;
    			}
    			else	
    			{
    				if(i==type._type.size()-1)  return "ERR";
    				align(ed,member[type._type[i+1]].align);
    				if(k<ed)  return "ERR";
    				pos=ed;
    			}
    		}
    	}
    	return t;
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("struct.in","r",stdin);
    	freopen("struct.out","w",stdout);
    #endif
    	ll m,pd,k,i,j;
    	cin>>m;
    	type_cnt++;  member[type_cnt].name="byte";  member[type_cnt].siz=member[type_cnt].align=1;  f["byte"]=type_cnt;
    	type_cnt++;  member[type_cnt].name="short"; member[type_cnt].siz=member[type_cnt].align=2; f["short"]=type_cnt;
    	type_cnt++;  member[type_cnt].name="int";   member[type_cnt].siz=member[type_cnt].align=4;   f["int"]=type_cnt;
    	type_cnt++;  member[type_cnt].name="long";  member[type_cnt].siz=member[type_cnt].align=8;  f["long"]=type_cnt;
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd;
    		if(pd==1)
    		{
    			cin>>s>>k;
    			for(j=1;j<=k;j++)  cin>>a[j]>>b[j];
    			init1(k);
    			cout<<member[type_cnt].siz<<" "<<member[type_cnt].align<<endl;
    		}
    		if(pd==2)
    		{
    			cin>>a[0]>>b[0];
    			init2();
    			cout<<element[element_cnt].address<<endl;
    		}
    		if(pd==3)
    		{
    			cin>>s;
    			cout<<query1()<<endl;
    		}
    		if(pd==4)
    		{
    			cin>>k;
    			cout<<query2(k)<<endl;
    		}
    	}
    	return 0;
    }
    

luogu P3834 【模板】可持久化线段树 2

  • 整体二分。

    • 整体二分需要修改对答案的判定相互独立,互不影响效果。
    • 修改如果对判定答案的有贡献,则贡献必须为一确定的与判定标准无关的值,且需要满足交换律、结合律,具有可加性。
    • 首先将所有操作按照 时间顺序 存入询问数组 q ,在后续过程中仍应保证区间内时间相对大小关系不变。
    • 核心思想是分治答案值域 [l,r] 时,将答案 mid>mid 的操作分成 q1,q2 ,接着继续分治。当 l=r 时记录答案并返回。
    • 常需要树状数组等其他数据结构加速 check
  • 本题中,可以将原序列也看做修改。

  • 树状数组维护单点加入和删除。时间复杂度为 O(nlog2n)

    点击查看代码
    struct node
    {
    	int l,r,k,id;
    }q[400010],tmp[2][400010];
    int a[200010],b[200010],ans[200010],n;
    struct BIT
    {
    	int c[200010];
    	int lowbit(int x)
    	{
    		return (x&(-x));
    	}
    	void add(int n,int x,int val)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))  c[i]+=val;
    	}
    	int getsum(int x)
    	{
    		int ans=0;
    		for(int i=x;i>=1;i-=lowbit(i))  ans+=c[i];
    		return ans;
    	}
    }T;
    void solve(int l,int r,int ql,int qr)
    {
    	if(ql>qr)  return;
    	if(l==r)
    	{
    		for(int i=ql;i<=qr;i++)  ans[q[i].id]=l;
    		return;
    	}
    	int mid=(l+r)/2,x=0,y=0,val;
    	for(int i=ql;i<=qr;i++)
    	{
    		if(q[i].id==0)
    		{
    			if(q[i].k<=mid)
    			{
    				T.add(n,q[i].l,1);
    				x++;  tmp[0][x]=q[i];
    			}
    			else
    			{
    				y++;  tmp[1][y]=q[i];
    			}
    		}
    		else
    		{
    			val=T.getsum(q[i].r)-T.getsum(q[i].l-1);
    			if(q[i].k<=val)
    			{
    				x++;  tmp[0][x]=q[i];
    			}
    			else
    			{
    				q[i].k-=val;
    				y++;  tmp[1][y]=q[i];
    			}
    		}
    	}
    	for(int i=1;i<=x;i++)
    	{
    		if(tmp[0][i].id==0)  T.add(n,tmp[0][i].l,-1);
    		q[ql+i-1]=tmp[0][i];
    	}
    	for(int i=1;i<=y;i++)  q[ql+x+i-1]=tmp[1][i];
    	solve(l,mid,ql,ql+x-1);  solve(mid+1,r,ql+x,qr);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int m,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];  b[i]=a[i];
    	}
    	sort(b+1,b+1+n);  b[0]=unique(b+1,b+1+n)-(b+1);
    	for(i=1;i<=n;i++)  q[i]=(node){i,i,lower_bound(b+1,b+1+b[0],a[i])-b,0};
    	for(i=1;i<=m;i++)
    	{
    		cin>>q[n+i].l>>q[n+i].r>>q[n+i].k;
    		q[n+i].id=i;
    	}
    	solve(1,b[0],1,n+m);
    	for(i=1;i<=m;i++)  cout<<b[ans[i]]<<endl;
    	return 0;
    }
    

luogu P1527 [国家集训队] 矩阵乘法

  • 树状数组维护单点修改,子矩阵求和。

    点击查看代码
    int a[510][510],b[2600010],ans[2600010],n;
    struct node
    {
    	int x1,y1,x2,y2,k,id;
    }q[2600010],tmp[2][2600010];
    struct BIT
    {
    	int c[510][510];
    	int lowbit(int x)
    	{
    		return (x&(-x));
    	}
    	void add(int n,int m,int x,int y,int val)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))
    			for(int j=y;j<=m;j+=lowbit(j))  c[i][j]+=val;
    	}
    	int getsum(int x,int y)
    	{
    		int ans=0;
    		for(int i=x;i>=1;i-=lowbit(i))
    			for(int j=y;j>=1;j-=lowbit(j))  ans+=c[i][j];
    		return ans;
    	}
    	int query(int x1,int y1,int x2,int y2)
    	{
    		return getsum(x2,y2)-getsum(x1-1,y2)-getsum(x2,y1-1)+getsum(x1-1,y1-1);
    	}
    }T;
    void solve(int l,int r,int ql,int qr)
    {
    	if(ql>qr)  return;
    	if(l==r)
    	{
    		for(int i=ql;i<=qr;i++)  ans[q[i].id]=l;
    		return;
    	}
    	int mid=(l+r)/2,x=0,y=0,val;
    	for(int i=ql;i<=qr;i++)
    	{
    		if(q[i].id==0)
    		{
    			if(q[i].k<=mid)
    			{
    				T.add(n,n,q[i].x1,q[i].y1,1);
    				x++;  tmp[0][x]=q[i];
    			}
    			else
    			{
    				y++;  tmp[1][y]=q[i];
    			}
    		}
    		else
    		{
    			val=T.query(q[i].x1,q[i].y1,q[i].x2,q[i].y2);
    			if(q[i].k<=val)
    			{
    				x++;  tmp[0][x]=q[i];
    			}
    			else
    			{
    				q[i].k-=val;
    				y++;  tmp[1][y]=q[i];
    			}
    		}
    	}
    	for(int i=1;i<=x;i++)
    	{
    		if(tmp[0][i].id==0)  T.add(n,n,tmp[0][i].x1,tmp[0][i].y1,-1);
    		q[ql+i-1]=tmp[0][i];
    	}
    	for(int i=1;i<=y;i++)  q[ql+x+i-1]=tmp[1][i];
    	solve(l,mid,ql,ql+x-1);  solve(mid+1,r,ql+x,qr);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int m,i,j;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=n;j++)
    		{
    			cin>>a[i][j];
    			b[0]++;  b[b[0]]=a[i][j];
    		}
    	}
    	sort(b+1,b+1+b[0]);  b[0]=unique(b+1,b+1+b[0])-(b+1);
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=n;j++)
    			q[(i-1)*n+j]=(node){i,j,i,j,lower_bound(b+1,b+1+b[0],a[i][j])-b,0};
    	}
    	for(i=1;i<=m;i++)
    	{
    		cin>>q[n*n+i].x1>>q[n*n+i].y1>>q[n*n+i].x2>>q[n*n+i].y2>>q[n*n+i].k;
    		q[n*n+i].id=i;
    	}
    	solve(1,b[0],1,n*n+m);
    	for(i=1;i<=m;i++)  cout<<b[ans[i]]<<endl;
    	return 0;
    }
    

luogu P3527 [POI 2011] MET-Meteors

  • 多倍经验: SP10264 METEORS - Meteors

  • 整体二分的过程中已经加入了 [1,l) 的贡献。

  • 树状数组维护区间修改,单点查询。

  • 过程中可能会炸 long long ,及时返回。

    点击查看代码
    ll st[600010],ed[600010],a[600010],ans[600010],m;
    pair<ll,ll>q[600010],tmp[2][600010];
    vector<ll>e[600010];
    struct BIT
    {
    	ll c[600010];
    	ll lowbit(ll x)
    	{
    		return (x&(-x));
    	}
    	void add(ll n,ll x,ll val)
    	{
    		for(ll i=x;i<=n;i+=lowbit(i))  c[i]+=val;
    	}
    	void update(ll n,ll l,ll r,ll val)
    	{
    		add(n,l,val);  add(n,r+1,-val);
    	}
    	ll getsum(ll x)
    	{
    		ll ans=0;
    		for(ll i=x;i>=1;i-=lowbit(i))  ans+=c[i];
    		return ans;
    	}
    }T;
    ll query(ll x,ll k)
    {
    	ll ans=0;
    	for(ll i=0;i<e[x].size();i++)  
    	{
    		ans+=T.getsum(e[x][i]);
    		if(ans>=k)  return ans;
    	}
    	return ans;
    }
    void solve(ll l,ll r,ll ql,ll qr)
    {
    	if(ql>qr)  return;
    	if(l==r)
    	{
    		for(ll i=ql;i<=qr;i++)  ans[q[i].second]=l;
    		return;
    	}  
    	ll mid=(l+r)/2,x=0,y=0,val;
    	for(ll i=l;i<=mid;i++)  T.update(m,st[i],ed[i],a[i]);
    	for(ll i=ql;i<=qr;i++)
    	{
    		val=query(q[i].second,q[i].first);
    		if(q[i].first<=val)
    		{
    			x++;  tmp[0][x]=q[i];
    		}
    		else
    		{
    			q[i].first-=val;
    			y++;  tmp[1][y]=q[i];
    		}
    	}
    	for(ll i=l;i<=mid;i++)  T.update(m,st[i],ed[i],-a[i]);
    	for(ll i=1;i<=x;i++)  q[ql+i-1]=tmp[0][i];
    	for(ll i=1;i<=y;i++)  q[ql+x+i-1]=tmp[1][i];
    	solve(l,mid,ql,ql+x-1);  solve(mid+1,r,ql+x,qr);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,k,x,i;
    	cin>>n>>m;
    	for(i=1;i<=m;i++)
    	{
    		cin>>x;  
    		e[x].push_back(i);  e[x].push_back(i+m);
    	}
    	for(i=1;i<=n;i++)
    	{
    		cin>>q[i].first;  q[i].second=i;
    	}
    	cin>>k;
    	for(i=1;i<=k;i++)
    	{
    		cin>>st[i]>>ed[i]>>a[i];
    		if(ed[i]<st[i])  ed[i]+=m;
    	}
    	st[k+1]=1;  ed[k+1]=2*m;  a[k+1]=0x3f3f3f3f3f3f3f3f;
    	m*=2;  solve(1,k+1,1,n);
    	for(i=1;i<=n;i++)  
    	{
    		if(ans[i]==k+1)  cout<<"NIE"<<endl;
    		else  cout<<ans[i]<<endl;
    	}
    	return 0;
    }
    

luogu P4175 [CTSC2008] 网络管理

  • 将单点修改看做看做删去一个数后再添加一个数然后正常做即可。

  • 可以将单点修改、根链求和转换成子树修改,单点查询。

    点击查看代码
    struct node
    {
    	int u,v,k,id;
    }q[160010],tmp[2][160010];
    int fa[80010],siz[80010],dep[80010],son[80010],dfn[80010],out[80010],top[80010],a[80010],b[160010],ans[80010],tot=0,n;
    vector<int>e[80010];
    void add(int u,int v)
    {
    	e[u].push_back(v);
    }
    void dfs1(int x,int father)
    {
    	siz[x]=1;
    	fa[x]=father;
    	dep[x]=dep[father]+1;
    	for(int i=0;i<e[x].size();i++)
    	{
    		if(e[x][i]!=father)
    		{
    			dfs1(e[x][i],x);
    			siz[x]+=siz[e[x][i]];
    			son[x]=(siz[e[x][i]]>siz[son[x]])?e[x][i]:son[x];
    		}
    	}
    }
    void dfs2(int x,int id)
    {
    	tot++;
    	dfn[x]=tot;
    	top[x]=id;
    	if(son[x]!=0)
    	{
    		dfs2(son[x],id);
    		for(int i=0;i<e[x].size();i++)
    			if(e[x][i]!=fa[x]&&e[x][i]!=son[x])  dfs2(e[x][i],e[x][i]);
    	}
    	out[x]=tot;
    }
    int lca(int u,int v)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]])  u=fa[top[u]];
    		else  v=fa[top[v]];
    	}
    	return dep[u]<dep[v]?u:v;
    }
    struct BIT
    {
    	int c[80010];
    	int lowbit(int x)
    	{
    		return (x&(-x));
    	}
    	void add(int n,int x,int val)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))  c[i]+=val;
    	}
    	void update(int n,int l,int r,int val)
    	{
    		add(n,l,val);  add(n,r+1,-val);
    	}
    	int getsum(int x)
    	{
    		int ans=0;
    		for(int i=x;i>=1;i-=lowbit(i))  ans+=c[i];
    		return ans;
    	}
    }T;
    void solve(int l,int r,int ql,int qr)
    {	
    	if(ql>qr)  return;
    	if(l==r)
    	{
    		for(int i=ql;i<=qr;i++)  ans[q[i].id]=l;
    		return;
    	}
    	int mid=(l+r)/2,x=0,y=0,val;
    	for(int i=ql;i<=qr;i++)
    	{
    		if(q[i].id==0)
    		{
    			if(q[i].k<=mid)
    			{
    				T.update(n,dfn[q[i].u],out[q[i].u],q[i].v);
    				x++;  tmp[0][x]=q[i];
    			}
    			else
    			{
    				y++;  tmp[1][y]=q[i];
    			}
    		}
    		else
    		{
    			val=T.getsum(dfn[q[i].u])+T.getsum(dfn[q[i].v])
    			-T.getsum(dfn[lca(q[i].u,q[i].v)])-T.getsum(dfn[fa[lca(q[i].u,q[i].v)]]);
    			if(q[i].k<=val)
    			{
    				x++;  tmp[0][x]=q[i];
    			}
    			else
    			{
    				q[i].k-=val;
    				y++;  tmp[1][y]=q[i];
    			}
    		}
    	}
    	for(int i=1;i<=x;i++)
    	{
    		if(tmp[0][i].id==0)  T.update(n,dfn[tmp[0][i].u],out[tmp[0][i].u],-tmp[0][i].v);
    		q[ql+i-1]=tmp[0][i];
    	}
    	for(int i=1;i<=y;i++)  q[ql+x+i-1]=tmp[1][i];
    	solve(l,mid,ql,ql+x-1);  solve(mid+1,r,ql+x,qr);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int m,cnt=0,k,u,v,i;
    	cin>>n>>m;
    	for(i=1;i<=n;i++)
    	{
    		cin>>a[i];
    		b[0]++;  b[b[0]]=a[i];
    		cnt++;  q[cnt]=(node){i,1,a[i],0};
    	}
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v;
    		add(u,v);  add(v,u);
    	}
    	dfs1(1,0);  dfs2(1,1);
    	for(i=1;i<=m;i++)
    	{
    		cin>>k>>u>>v;
    		if(k==0)
    		{
    			cnt++;  q[cnt]=(node){u,-1,a[u],0};
    			b[0]++;  a[u]=b[b[0]]=v;
    			cnt++;  q[cnt]=(node){u,1,a[u],0};
    		}
    		else  
    		{
    			if((dep[u]+dep[v]-2*dep[lca(u,v)]+1)<k)  ans[i]=-1;
    			else 
    			{
    				cnt++;  q[cnt]=(node){u,v,(dep[u]+dep[v]-2*dep[lca(u,v)]+1)-k+1,i};
    			}
    		}
    	}
    	sort(b+1,b+1+b[0]);  b[0]=unique(b+1,b+1+b[0])-(b+1);
    	for(i=1;i<=cnt;i++)  if(q[i].id==0)  q[i].k=lower_bound(b+1,b+1+b[0],q[i].k)-b;
    	solve(1,b[0],1,cnt);
    	for(i=1;i<=m;i++)
    	{
    		if(ans[i]==-1)  cout<<"invalid request!"<<endl;
    		else  if(ans[i]!=0)  cout<<b[ans[i]]<<endl;	
    	}
    	return 0;
    }
    
    

luogu P3242 [HNOI2015] 接水果

  • 对于盘子 (u,v,w) ,钦定 dfnudfnv

    • LCA(u,v)=u ,则能影响到的水果一个端点在 v 为根的子树内部,另一个端点不在 uv 方向的第一个儿子 x 的子树内部。
    • LCA(u,v)u ,则能影响到的水果两个端点分别在 u,v 为根的子树内部。
  • 此时是一个矩形加、单点查询的形式,写个扫描线即可。

    点击查看代码
    struct node
    {
    	int x,l,r,k,val,id;
    	bool operator < (const node &another) const
    	{
    		return (x==another.x)?(id<another.id):(x<another.x);
    	}
    }q[200010],tmp[2][200010];
    int fa[40010],siz[40010],dep[40010],son[40010],top[40010],dfn[40010],out[40010],b[40010],ans[40010],tot=0,n;
    vector<int>e[40010];
    void add(int u,int v)
    {
    	e[u].push_back(v);
    }
    void dfs1(int x,int father)
    {
    	siz[x]=1;
    	fa[x]=father;
    	dep[x]=dep[father]+1;
    	for(int i=0;i<e[x].size();i++)
    	{
    		if(e[x][i]!=father)
    		{
    			dfs1(e[x][i],x);
    			siz[x]+=siz[e[x][i]];
    			son[x]=(siz[e[x][i]]>siz[son[x]])?e[x][i]:son[x];
    		}
    	}
    }
    void dfs2(int x,int id)
    {
    	tot++;  dfn[x]=tot;
    	top[x]=id;
    	if(son[x]!=0)
    	{
    		dfs2(son[x],id);
    		for(int i=0;i<e[x].size();i++)
    			if(e[x][i]!=fa[x]&&e[x][i]!=son[x])  dfs2(e[x][i],e[x][i]);
    	}
    	out[x]=tot;
    }
    int lca(int u,int v)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]])  u=fa[top[u]];
    		else  v=fa[top[v]];
    	}
    	return dep[u]<dep[v]?u:v;
    }
    int dirson(int u,int rt)
    {
    	while(top[u]!=top[rt])
    	{
    		if(fa[top[u]]==rt)  return top[u];
    		u=fa[top[u]];
    	}
    	return son[rt];
    }
    struct BIT
    {
    	int c[400010];
    	int lowbit(int x)
    	{
    		return (x&(-x));
    	}
    	void add(int n,int x,int val)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))  c[i]+=val;
    	}
    	void update(int n,int l,int r,int val)
    	{
    		add(n,l,val);  add(n,r+1,-val);
    	}
    	int getsum(int x)
    	{
    		int ans=0;
    		for(int i=x;i>=1;i-=lowbit(i))  ans+=c[i];
    		return ans;
    	}
    }T;
    void solve(int l,int r,int ql,int qr)
    {
    	if(ql>qr)  return;
    	if(l==r)
    	{
    		for(int i=ql;i<=qr;i++)  ans[q[i].id]=l;
    		return;
    	}
    	int mid=(l+r)/2,x=0,y=0,val;
    	for(int i=ql;i<=qr;i++)
    	{
    		if(q[i].id==0)
    		{
    			if(q[i].k<=mid)  
    			{
    				T.update(n,q[i].l,q[i].r,q[i].val);
    				x++;  tmp[0][x]=q[i];
    			}
    			else
    			{
    				y++;  tmp[1][y]=q[i];
    			}
    		}
    		else
    		{
    			val=T.getsum(q[i].l);
    			if(q[i].k<=val)
    			{
    				x++;  tmp[0][x]=q[i];
    			}
    			else
    			{
    				q[i].k-=val;
    				y++;  tmp[1][y]=q[i];
    			}
    		}
    	}
    	for(int i=1;i<=x;i++)
    	{
    		if(tmp[0][i].id==0)  T.update(n,tmp[0][i].l,tmp[0][i].r,-tmp[0][i].val);
    		q[ql+i-1]=tmp[0][i];
    	}
    	for(int i=1;i<=y;i++)  q[ql+x+i-1]=tmp[1][i];
    	solve(l,mid,ql,ql+x-1);  solve(mid+1,r,ql+x,qr);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int m,k,u,v,w,x,cnt=0,i;
    	cin>>n>>m>>k;
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u>>v;
    		add(u,v);  add(v,u);
    	}
    	dfs1(1,0);  dfs2(1,1);
    	for(i=1;i<=m;i++)
    	{
    		cin>>u>>v>>w;
    		b[0]++;  b[b[0]]=w;
    		if(dfn[u]>dfn[v])  swap(u,v);
    		if(lca(u,v)==u)
    		{
    			x=dirson(v,u);
    			if(dfn[x]-1>=1)
    			{
    				cnt++;  q[cnt]=(node){1,dfn[v],out[v],w,1,0};
    				cnt++;  q[cnt]=(node){dfn[x],dfn[v],out[v],w,-1,0};
    			}
    			if(out[x]+1<=n)
    			{
    				cnt++;  q[cnt]=(node){dfn[v],out[x]+1,n,w,1,0};
    				cnt++;  q[cnt]=(node){out[v]+1,out[x]+1,n,w,-1,0};
    			}
    		}
    		else
    		{
    			cnt++;  q[cnt]=(node){dfn[u],dfn[v],out[v],w,1,0};
    			cnt++;  q[cnt]=(node){out[u]+1,dfn[v],out[v],w,-1,0};
    		}
    	}
    	sort(b+1,b+1+b[0]);  b[0]=unique(b+1,b+1+b[0])-(b+1);
    	for(i=1;i<=cnt;i++)  q[i].k=lower_bound(b+1,b+1+b[0],q[i].k)-b;
    	for(i=1;i<=k;i++)
    	{
    		cin>>u>>v>>w;
    		if(dfn[u]>dfn[v])  swap(u,v);
    		cnt++;  q[cnt]=(node){dfn[u],dfn[v],0,w,0,i};
    	}
    	sort(q+1,q+1+cnt);  solve(1,b[0],1,cnt);
    	for(i=1;i<=k;i++)  cout<<b[ans[i]]<<endl;
    	return 0;
    }
    

luogu P3250 [HNOI2016] 网络

  • check 时统计 [l,r]mid 的边经过 x 的次数。

  • 可以将根链加、单点查询转换成单点加、子树查询。

    点击查看代码
    struct node
    {
    	int u,v,k,val,id;
    }q[200010],tmp[2][200010];
    int fa[100010],siz[100010],dep[100010],son[100010],top[100010],dfn[100010],out[100010],b[200010],u[200010],v[200010],w[200010],ans[200010],tot=0,n;
    vector<int>e[100010];
    void add(int u,int v)
    {
    	e[u].push_back(v);
    }
    void dfs1(int x,int father)
    {
    	siz[x]=1;
    	fa[x]=father;
    	dep[x]=dep[father]+1;
    	for(int i=0;i<e[x].size();i++)
    	{
    		if(e[x][i]!=father)
    		{
    			dfs1(e[x][i],x);
    			siz[x]+=siz[e[x][i]];
    			son[x]=(siz[e[x][i]]>siz[son[x]])?e[x][i]:son[x];
    		}
    	}
    }
    void dfs2(int x,int id)
    {
    	top[x]=id;
    	tot++;  dfn[x]=tot;
    	if(son[x]!=0)
    	{
    		dfs2(son[x],id);
    		for(int i=0;i<e[x].size();i++)
    			if(e[x][i]!=fa[x]&&e[x][i]!=son[x])  dfs2(e[x][i],e[x][i]);
    	}
    	out[x]=tot;
    }
    int lca(int u,int v)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]])  u=fa[top[u]];
    		else  v=fa[top[v]];
    	}
    	return dep[u]<dep[v]?u:v;
    }
    struct BIT
    {
    	int c[100010];
    	int lowbit(int x)
    	{
    		return (x&(-x));
    	}
    	void add(int n,int x,int val)
    	{
    		for(int i=x;i<=n;i+=lowbit(i))  c[i]+=val;
    	}
    	void update(int u,int v,int val)
    	{
    		add(n,dfn[u],val);  add(n,dfn[v],val);
    		int rt=lca(u,v);  add(n,dfn[rt],-val);
    		if(fa[rt]!=0)  add(n,dfn[fa[rt]],-val);
    	}
    	int getsum(int x)
    	{
    		int ans=0;
    		for(int i=x;i>=1;i-=lowbit(i))  ans+=c[i];
    		return ans;
    	}
    }T;
    void solve(int l,int r,int ql,int qr)
    {
    	if(ql>qr)  return;
    	if(l==r)
    	{
    		for(int i=ql;i<=qr;i++)  ans[q[i].id]=(l==0)?-1:b[l];
    		return;
    	}
    	int mid=(l+r)/2,x=0,y=0,sum=0;
    	for(int i=ql;i<=qr;i++)
    	{
    		if(q[i].id==0)
    		{
    			if(q[i].k<=mid)
    			{
    				x++;  tmp[0][x]=q[i];
    			}
    			else
    			{
    				sum+=q[i].val;
    				T.update(q[i].u,q[i].v,q[i].val);
    				y++;  tmp[1][y]=q[i];
    			}
    		}
    		else
    		{
    			if(T.getsum(out[q[i].u])-T.getsum(dfn[q[i].u]-1)==sum)
    			{
    				x++;  tmp[0][x]=q[i];
    			}
    			else
    			{
    				y++;  tmp[1][y]=q[i];
    			}
    		}
    	}
    	for(int i=1;i<=x;i++)  q[ql+i-1]=tmp[0][i];
    	for(int i=1;i<=y;i++)
    	{
    		if(tmp[1][i].id==0)  T.update(tmp[1][i].u,tmp[1][i].v,-tmp[1][i].val);
    		q[ql+x+i-1]=tmp[1][i];
    	}
    	solve(l,mid,ql,ql+x-1);  solve(mid+1,r,ql+x,qr);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int m,pd,i;
    	cin>>n>>m;
    	for(i=1;i<=n-1;i++)
    	{
    		cin>>u[0]>>v[0];
    		add(u[0],v[0]);  add(v[0],u[0]);
    	}
    	dfs1(1,0);  dfs2(1,1);
    	for(i=1;i<=m;i++)
    	{
    		cin>>pd>>u[i];
    		if(pd==0)
    		{
    			cin>>v[i]>>w[i];
    			b[0]++;  b[b[0]]=w[i];
    			q[i]=(node){u[i],v[i],w[i],1,0};
    		}
    		if(pd==1)  q[i]=(node){u[u[i]],v[u[i]],w[u[i]],-1,0};
    		if(pd==2)  q[i]=(node){u[i],u[i],0,0,i};
    	}
    	sort(b+1,b+1+b[0]);  b[0]=unique(b+1,b+1+b[0])-(b+1);
    	for(i=1;i<=m;i++)  if(q[i].id==0)  q[i].k=lower_bound(b+1,b+1+b[0],q[i].k)-b;
    	solve(0,b[0],1,m);
    	for(i=1;i<=m;i++)  if(ans[i]!=0)  cout<<ans[i]<<endl;
    	return 0;
    }
    

AT_abc258_g [ABC258G] Triangle

  • bitset

    点击查看代码
    bitset<3010>f[3010];
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll n,ans=0,i,j;
    	char c;
    	cin>>n;
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=n;j++)
    		{
    			cin>>c;
    			if(j>=i+1)  f[i][j]=c-'0';
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		for(j=i+1;j<=n;j++)  
    		{
    			if(f[i][j]==1)  ans+=(f[i]&f[j]).count();
    		}
    	}
    	cout<<ans<<endl;
    	return 0;
    }
    

[ABC221G] Jumping sequence

  • 将切比雪夫坐标系转换成曼哈顿坐标系,要从 (0,0) 走到 (a+b2,ab2)

  • 从切比雪夫坐标系中的 (x,y) 向上、下、左、右、走分别能走到曼哈顿坐标系中的 (x+y+di2,x+ydi2),(x+ydi2,x+y+di2),(x+ydi2,x+y+di2),(x+y+di2,x+y+di2)

  • 此时就分离开了两维的限制,问题转化到了形如 i=1ncidi=x,ci{1,1}

  • ci 加一再除以 2 并移项后得到 i=1ncidi=x+i=1ndi2,ci{0,1}

  • bitset 加速背包转移即可。

    点击查看代码
    int d[2010],opt[2][2010];
    bitset<3600010>f[2010];
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	int n,x,y,_x,sum=0,i;
    	cin>>n>>x>>y;  _x=x;  x+=y;  y=_x-y;
    	f[0][0]=1;
    	for(i=1;i<=n;i++)
    	{
    		cin>>d[i];  sum+=d[i];
    		f[i]=f[i-1]|(f[i-1]<<d[i]);
    	}
    	x+=sum;  y+=sum;
    	if(0<=x&&x/2<=sum&&0<=y&&y/2<=sum&&x%2==0&&y%2==0&&f[n][x/2]==1&&f[n][y/2]==1)
    	{
    		x/=2;  y/=2;
    		cout<<"Yes"<<endl;
    		for(i=n;i>=1;i--)
    		{
    			if(x>=d[i]&&f[i-1][x-d[i]]==1)
    			{
    				x-=d[i];  opt[0][i]=1;
    			}
    			else  opt[0][i]=0;
    			if(y>=d[i]&&f[i-1][y-d[i]]==1)
    			{
    				y-=d[i];  opt[1][i]=1;
    			}
    			else  opt[1][i]=0;
    		}
    		for(i=1;i<=n;i++)
    		{
    			if(opt[0][i]==1)  cout<<(opt[1][i]==1?"R":"U");
    			else  cout<<(opt[1][i]==1?"D":"L");
    		}
    	}
    	else  cout<<"No"<<endl;
    	return 0;
    }
    

luogu P3332 [ZJOI2013] K大数查询

  • 线段树维护区间修改、区间查询。

  • 注意第 k 大划分左右部分询问时进行颠倒。

    点击查看代码
    struct node
    {
    	ll l,r,k,id;
    }q[50010],tmp[2][50010];
    ll b[50010],op[50010],ans[50010],n;
    struct SMT
    {
    	struct SegmentTree
    	{
    		ll sum,lazy,len;
    	}tree[200010];
    	#define lson(rt) (rt<<1)
    	#define rson(rt) (rt<<1|1)
    	void pushup(ll rt)
    	{
    		tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
    	}
    	void pushlazy(ll rt,ll lazy)
    	{
    		tree[rt].sum+=lazy*tree[rt].len;
    		tree[rt].lazy+=lazy;
    	}
    	void pushdown(ll rt)
    	{
    		if(tree[rt].lazy!=0)
    		{
    			pushlazy(lson(rt),tree[rt].lazy);
    			pushlazy(rson(rt),tree[rt].lazy);
    			tree[rt].lazy=0;
    		}
    	}
    	void build(ll rt,ll l,ll r)
    	{
    		tree[rt].sum=tree[rt].lazy=0;
    		tree[rt].len=r-l+1;
    		if(l==r)  return;
    		ll mid=(l+r)/2;
    		build(lson(rt),l,mid);  build(rson(rt),mid+1,r);
    	}
    	void update(ll rt,ll l,ll r,ll x,ll y,ll val)
    	{
    		if(x<=l&&r<=y)
    		{
    			pushlazy(rt,val);
    			return;
    		}
    		pushdown(rt);
    		ll mid=(l+r)/2;
    		if(x<=mid)  update(lson(rt),l,mid,x,y,val);
    		if(y>mid)  update(rson(rt),mid+1,r,x,y,val);
    		pushup(rt);
    	}
    	ll query(ll rt,ll l,ll r,ll x,ll y)
    	{
    		if(x<=l&&r<=y)  return tree[rt].sum;
    		pushdown(rt);
    		ll mid=(l+r)/2,ans=0;
    		if(x<=mid)  ans+=query(lson(rt),l,mid,x,y);
    		if(y>mid)  ans+=query(rson(rt),mid+1,r,x,y);
    		return ans;
    	}
    }T;
    void solve(ll l,ll r,ll ql,ll qr)
    {
    	if(ql>qr)  return;
    	if(l==r)
    	{
    		for(ll i=ql;i<=qr;i++)  ans[q[i].id]=l;
    		return;
    	}
    	ll mid=(l+r)/2,x=0,y=0,val;
    	for(ll i=ql;i<=qr;i++)
    	{
    		if(q[i].id==0)
    		{
    			if(q[i].k<=mid)
    			{
    				x++;  tmp[0][x]=q[i];
    			}
    			else
    			{
    				T.update(1,1,n,q[i].l,q[i].r,1);
    				y++;  tmp[1][y]=q[i];
    			}
    		}
    		else
    		{
    			val=T.query(1,1,n,q[i].l,q[i].r);
    			if(q[i].k<=val)
    			{
    				y++;  tmp[1][y]=q[i];
    			}
    			else
    			{
    				q[i].k-=val;
    				x++;  tmp[0][x]=q[i];
    			}
    		}
    	}
    	for(ll i=1;i<=x;i++)  q[ql+i-1]=tmp[0][i];
    	for(ll i=1;i<=y;i++)
    	{
    		if(tmp[1][i].id==0)  T.update(1,1,n,tmp[1][i].l,tmp[1][i].r,-1);
    		q[ql+x+i-1]=tmp[1][i];
    	}
    	solve(l,mid,ql,ql+x-1);  solve(mid+1,r,ql+x,qr);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("in.in","r",stdin);
    	freopen("out.out","w",stdout);
    #endif
    	ll m,i;
    	cin>>n>>m;  T.build(1,1,n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>op[i]>>q[i].l>>q[i].r>>q[i].k;
    		if(op[i]==1)  
    		{
    			q[i].id=0;
    			b[0]++;  b[b[0]]=q[i].k;
    		}
    		else  q[i].id=i;
    	}
    	sort(b+1,b+1+b[0]);  b[0]=unique(b+1,b+1+b[0])-(b+1);
    	for(i=1;i<=m;i++)  if(q[i].id==0)  q[i].k=lower_bound(b+1,b+1+b[0],q[i].k)-b;
    	solve(1,b[0],1,m);
    	for(i=1;i<=m;i++)  if(op[i]==2)  cout<<b[ans[i]]<<endl;
    	return 0;
    }
    

2.20

闲话

  • 上午 7:3012:15 去新机房打 accoders NOI 和学校 OJ 的模拟赛。结束的时候 huge 说今天的一些事故可能导致我们发挥不是很好,要辩证地去看待这个问题。
  • 下午 huge 说等我们把联考的题看了后再听多校讲题;体育课正常;回来之后讲题。
  • 去吃晚饭的路上遇见了 @chancelong ,他说他应该是染上甲流了,今天得回家,我说 huge 在楼上,问他需不需要去找 huge ,告诉他 huge 可能出现的地方后他就上楼了。
  • 晚上 huge 说下午的时候他把新机房虚拟机装好了,明天我们就可以直接用了。
  • 晚上回宿舍后听班里同学说课表上新加的政史地改成奥赛了。

做题纪要

luogu P5163 WD与地图

P1019. 国际象棋

luogu P8935 [JRKSJ R7] 茎

P1008. 城市规划

P1028. 好段子

posted @   hzoi_Shadow  阅读(199)  评论(8编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
历史上的今天:
2024-02-10 P8670 [蓝桥杯 2018 国 B] 矩阵求和 题解
扩大
缩小
点击右上角即可分享
微信分享提示