[BZOJ3218]a+b Problem
壹、题目
贰、思考
似乎和文理分科有点像,但是这个题的要求是有异色的是 "奇怪" 的。
考虑正难则反,如果一个点是奇怪的,那么它的贡献就是 ,反之,如果它不是奇怪的,就是 了,如果要求一个点不是 "奇怪" 的,那么就要求所有满足 的点都是黑色,也就是说如果这些点都是黑色,就会有额外的贡献。
这就和文理分科十分像了,考虑源点黑,汇点白,划分两个点集,首先从 向每个点连接 的边,意味着选择黑色,然后从每个点向汇点连接 的边,意味着选择白色,考虑都是黑色的,就建虚点,对于每个虚点,从源点连接一条边权为对应格子的 ,然后从这个虚点向每个满足条件的格子连接一个 ,然后直接跑最大流求最小割,最后的答案就是所有的点减去最小割。
然后这种方法 了 去吔屎吧,然鹅什么都布吉岛的我布吉岛怎么去优化,然后我 了。
这个方法有点问题,因为 可能出现负边,但是有补救方法,具体可以看部分 伍 。
真正的解决方案:
从 向每个点连接 (就是 )的边,然后从每个点向汇点连接 (就是 )的边,对于每个虚点,从源点连接一条边权为对应格子的 (就是 ),然后从这个虚点向每个满足条件的格子连接一个 ,然后直接跑最大流求最小割,最后的答案就是所以点的 减去这个图上的最小割。
叁、题解
好不容易想出来建图,然后布吉岛怎么优化......
考虑我们原先的建图方式需要多少空间?,那还是去自闭吧......
使用传说中的线段树优化建图,但是是二维偏序,所以还要考虑可持久化,而在线段树上的边,需要注意的是全部的值都应该赋为 表示不可断掉。
可耻就话的时候不仅要继承点,还要继承边,具体实现看代码。
肆、代码
还没跳出来,然鹅不想调了,先留个坑在这里吧......
我TM终于把坑填了......调出来啦,是线段树函数里面 初值的问题......
using namespace Elaina;
const int maxn=5e3;
const int inf=(1<<30)-1;
int a[maxn+5],b[maxn+5],w[maxn+5],l[maxn+5],r[maxn+5],p[maxn+5];
int n,maxa;
ll ans=0;
inline void input(){
n=readin(1);
rep(i,1,n)a[i]=readin(1),b[i]=readin(1),w[i]=readin(1),l[i]=readin(1),r[i]=readin(1),p[i]=readin(1);
rep(i,1,n)maxa=Max(Max(maxa,a[i]),Max(l[i],r[i]));
}
struct edge{int to,nxt,c;
edge(const int T=0,const int N=0,const int C=0):to(T),nxt(N),c(C){}
}e[maxn*200+5];
int tail[maxn*200+5],ecnt;
int cur[maxn*200+5];
int ncnt;
inline void add_edge(const int u,const int v,const int c){
e[++ecnt]=edge(v,tail[u],c);tail[u]=ecnt;
e[++ecnt]=edge(u,tail[v],0);tail[v]=ecnt;
}
inline void update(const int i,const int x){
e[i].c-=x,e[i^1].c+=x;
}
int rt[maxn+5];
int ls[maxn*200+5],rs[maxn*200+5];
#define mid ((l+r)>>1)
/**
* @param x 新建的当前点
* @param i 前一个版本的点
* @param p 从树连到 p 点上面
* @param a p 所对应的点的 a[] 的值
*/
void modify(int& x,const int p,const int a,const int i,const int l=0,const int r=maxa){
x=++ncnt;
ls[x]=ls[i],rs[x]=rs[i];
// 相当于继承前一个版本的俩儿子
if(i)add_edge(x,i,inf);
// 到底层了, 加边走人
if(l==r)return add_edge(x,p,inf);
if(a<=mid){
modify(ls[x],p,a,ls[i],l,mid);
add_edge(x,ls[x],inf);
}
else{
modify(rs[x],p,a,rs[i],mid+1,r);
add_edge(x,rs[x],inf);
}
}
/**
* @param L 询问左端点
* @param R 询问右端点
* @param p 这个点往树上连边
* @param i 现在在树上的哪个点
*/
void query(const int L,const int R,const int p,const int i,const int l=0,const int r=maxa){
if(!i)return;
if(L>R)return;
if(L<=l && r<=R)return add_edge(p,i,inf);
if(L<=mid)query(L,R,p,ls[i],l,mid);
if(mid<R)query(L,R,p,rs[i],mid+1,r);
}
inline void build(){
memset(tail,-1,sizeof tail);ecnt=-1;
rep(i,1,n){
ans=ans+b[i]+w[i]+p[i];
add_edge(0,i,b[i]);
add_edge(i,n+1,w[i]+p[i]);
}
ncnt=n+1;
rep(i,1,n){
int nde=++ncnt;
add_edge(0,nde,p[i]);
add_edge(nde,i,inf);
if(i>1)query(l[i],r[i],nde,rt[i-1]);
modify(rt[i],i,a[i],rt[i-1]);
}
}
int dis[maxn*200+5];
queue<int>Q;
inline int bfs(){
memset(dis,-1,(ncnt+1)<<2);
while(!Q.empty())Q.pop();
dis[0]=0;Q.push(0);
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int i=tail[u],v;~i;i=e[i].nxt)if(e[i].c){
v=e[i].to;
if(dis[v]==-1){
dis[v]=dis[u]+1;
Q.push(v);
}
}
}
return dis[n+1]!=-1;
}
int dfs(const int u,const int flow,const int t){
if(u==t)return flow;
int rest=flow;
for(int& i=cur[u];~i;i=e[i].nxt)if(e[i].c){
int v=e[i].to;
if(dis[v]==dis[u]+1){
int k=dfs(v,Min(rest,e[i].c),t);
rest-=k,update(i,k);
if(rest==0)break;
}
}
return flow-rest;
}
inline void dinic(){
while(bfs()){
memcpy(cur,tail,(ncnt+1)<<2);
ans-=dfs(0,inf,n+1);
}
writc(ans,'\n');
}
signed main(){
input();
build();
dinic();
return 0;
}
伍、思路の一些问题及补救措施
我们加边时要加容量为 的边,会出现负容量,这个其实是有补救措施的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下