大名鼎鼎的A+B Problem, 主席树优化最小割……

调题死活调不对,一怒之下改了一种写法交上去A了,但是改写法之后第4,5个点常数变大很多,于是喜提UOJ全站倒数第三

目前还不知道原来的写法为什么是错的,暂时先写一下A掉的那种写法的题解。

题目链接:

(BZOJ) https://www.lydsy.com/JudgeOnline/problem.php?id=3218

(UOJ) http://uoj.ac/problem/77

题解:

首先不难想到这样的最小割建图: (醒醒吧这种题就别老忘二分图上想了) 下文用\(Edge(u,v,w)\)表示从\(u\)向\(v\)连一条边权为\(w\)的单向边, \(S,T\)分别是源点和汇点

对每一个方格建一个点\(V_i\),然后\(Edge(S,V_i,b_i), Edge(V_i,T,w_i)\), 这样一来这个点属于\(T\)集(\(S\)的边被割掉了)就表示这个点染成白色(舍弃\(b_i\)代价), 否则染成黑色。

此外对于每个方格再建新点\(E_i\), 然后\(Edge(V_i,E_i,p_i)\), 再对于满足\(1\le j\le i, a_j\in[l_i,r_i]\)的\(j\), \(Edge(E_i,V_j,+\inf)\), 表示那个奇怪的方格的限制

至此网络流建模结束。这个还是比较好想的,然而边数\(O(n^2)\), \(TLE+MLE\)不谢。

然后我们考虑如果只有\(l_i\le a_i\le r_i\)这个限制,我们可以对\(a_i\)排序离散化之后转化成了\(E_i\)这个点要向一个区间内的\(V_j\)连边,这样的话我们可以建出一棵线段树,线段树每个节点向它的两个儿子连边,然后往区间连边相当于往这个区间拆成的\(O(\log n)\)个线段树节点连边,边数\(O(\log n)\).

然而这题……还要\(1\le j\le i\)?

这样我们就要把线段树改成主席树(vfk大毒瘤!)

后面的都是套路: 以\(i\)为历史版本\(a_i\)为下标建主席树,然后主席树的每个节点往儿子连边,其余的连边方式自行思考(很无脑),详见代码。

有一个地方需要注意: 有种可能是我往主席树里插入一个节点的时候,主席树这个节点上已经有值了。(也就是存在两个\(a_i\)相等的情况)

这怎么办呢?目前我还没有找到一个我认为合理的解决方法,最后直接简单粗暴地离散化的时候不去重,强行把相等变成不等的办法,导致UOJ上第4,5个点常数爆炸跑得和第9,10个点一样慢,全站倒数第三,不过还是过了。

然后是目前这题的一些还未解决的问题,我想到了若干种弥补相等问题的做法,结果全都是50分或者80分,目前相对来说最靠谱的一种(答案和正确答案相差最小)是对于每一个重叠的主席树节点\(T_i\)新建一个点\(T_i'\), \(Edge(T_{fa_i},T_i',+\inf), Edge(T_i',T_i,+\inf), Edge(T_i',T_j',+\inf)\) (\(fa_i\)表示主席树的父亲节点, \(j,j'\)是上一个历史版本的对应节点)。然而还是\(80\)分QAQ……所以哪位大佬能跟我说一下这个到底怎么解决啊,谢谢QwQ

代码实现

离散化时不去重的版本,全站倒数第三

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cassert>
#define llong long long
using namespace std; const int N = 8e4;
const int M = 1e6;
const llong INF = 100000000000ll;
namespace NetFlow
{
struct Edge
{
int v,nxt,rev; llong w;
} e[(M<<1)+3];
int dep[N+3];
int te[N+3];
int fe[N+3];
int que[N+3];
int n,en,s,t;
void addedge(int u,int v,llong w)
{
// printf("addedge %d %d %lld\n",u,v,w);
en++; e[en].v = v; e[en].w = w;
e[en].nxt = fe[u]; fe[u] = en; e[en].rev = en+1;
en++; e[en].v = u; e[en].w = 0;
e[en].nxt = fe[v]; fe[v] = en; e[en].rev = en-1;
}
bool bfs()
{
for(int i=1; i<=n; i++) dep[i] = 0;
int head = 1,tail = 1; que[1] = s; dep[s] = 1;
while(head<=tail)
{
int u = que[head]; head++;
for(int i=fe[u]; i; i=e[i].nxt)
{
if(dep[e[i].v]==0 && e[i].w)
{
dep[e[i].v] = dep[u]+1;
tail++; que[tail] = e[i].v;
}
}
}
return dep[t]!=0;
}
llong dfs(int u,llong cur)
{
if(u==t) return cur;
llong rst = cur;
for(int i=te[u]; i; i=e[i].nxt)
{
if(dep[e[i].v]==dep[u]+1 && rst>0 && e[i].w>0)
{
llong flow = dfs(e[i].v,min(e[i].w,rst));
if(flow>0)
{
e[i].w-=flow; rst-=flow; e[e[i].rev].w+=flow;
if(e[i].w>0) {te[u] = i;}
if(rst==0) return cur;
}
}
}
if(cur==rst) dep[u] = 0;
return cur-rst;
}
llong dinic()
{
// printf("Dinic n=%d\n",n);
llong ret = 0ll;
while(bfs())
{
for(int i=1; i<=n; i++) te[i] = fe[i];
ret += dfs(s,INF);
}
return ret;
}
}
using NetFlow::addedge;
using NetFlow::dinic; struct Element
{
llong b,w,p; int a,l,r; int id;
bool operator <(const Element &arg) const
{
return a<arg.a;
}
} a[N+3],b[N+3];
struct SgTNode
{
int ls,rs;
} sgt[N+3];
vector<int> disc;
int rtn[N+3];
int n,siz; int getdisc(int x)
{
return lower_bound(disc.begin(),disc.end(),x)-disc.begin()+1;
} void addval(int &pos,int le,int ri,int lrb,int id,llong w1,llong w2,llong w3)
{
siz++; sgt[siz] = sgt[pos];
pos = siz;
if(le==ri) {addedge(1,pos,w1); addedge(pos,id,w3); addedge(pos,2,w2); return;}
int mid = (le+ri)>>1;
if(lrb<=mid) {addval(sgt[pos].ls,le,mid,lrb,id,w1,w2,w3);}
else {addval(sgt[pos].rs,mid+1,ri,lrb,id,w1,w2,w3);}
if(sgt[pos].ls) {addedge(pos,sgt[pos].ls,INF);} //Judge 0
if(sgt[pos].rs) {addedge(pos,sgt[pos].rs,INF);}
} void AddEdge(int pos,int le,int ri,int lb,int rb,int id)
{
if(pos==0) return;
if(lb<=le && rb>=ri) {addedge(id,pos,INF); return;}
int mid = (le+ri)>>1;
if(lb<=mid) {AddEdge(sgt[pos].ls,le,mid,lb,rb,id);}
if(rb>mid) {AddEdge(sgt[pos].rs,mid+1,ri,lb,rb,id);}
} int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d%lld%lld%d%d%lld",&a[i].a,&a[i].b,&a[i].w,&a[i].l,&a[i].r,&a[i].p); a[i].id = i;
disc.push_back(a[i].a);
}
sort(disc.begin(),disc.end());
sort(a+1,a+n+1);
for(int i=1; i<=n; i++)
{
a[i].l = getdisc(a[i].l);
a[i].r = getdisc(a[i].r+1)-1;
if(a[i].l<1) a[i].l = 1;
if(a[i].r>disc.size()) a[i].r = disc.size();
a[i].a = i;
b[a[i].id] = a[i];
}
for(int i=1; i<=n; i++) a[i] = b[i];
// for(int i=1; i<=n; i++) printf("%d %lld %lld %d %d %lld\n",a[i].a,a[i].b,a[i].w,a[i].l,a[i].r,a[i].p);
siz = n+2;
rtn[0] = 0;
for(int i=1; i<=n; i++)
{
// printf("i%d:\n",i);
rtn[i] = rtn[i-1];
addval(rtn[i],1,disc.size(),a[i].a,i+2,a[i].b,a[i].w,a[i].p);
AddEdge(rtn[i-1],1,disc.size(),a[i].l,a[i].r,i+2);
// printf("siz=%d\n",siz);
// for(int j=1; j<=i; j++) printf("rtn[%d]=%d\n",j,rtn[j]);
// for(int j=n+3; j<=siz; j++) printf("i%d ls%d rs%d\n",j,sgt[j].ls,sgt[j].rs);
// puts("");
}
NetFlow::n = siz; NetFlow::s = 1; NetFlow::t = 2;
llong ans = 0ll;
for(int i=1; i<=n; i++) ans += a[i].b+a[i].w;
ans = ans-dinic();
printf("%lld\n",ans);
return 0;
}
/*
3
3 1 6 2 2 3
3 2 9 1 5 8
4 3 8 2 4 7
*/

附: 80分代码

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cassert>
#define llong long long
using namespace std; const int N = 8e4;
const int M = 1e6;
const llong INF = 100000000000ll;
namespace NetFlow
{
struct Edge
{
int v,nxt,rev; llong w;
} e[(M<<1)+3];
int dep[N+3];
int te[N+3];
int fe[N+3];
int que[N+3];
int n,en,s,t;
void addedge(int u,int v,llong w)
{
// printf("addedge %d %d %lld\n",u,v,w);
en++; e[en].v = v; e[en].w = w;
e[en].nxt = fe[u]; fe[u] = en; e[en].rev = en+1;
en++; e[en].v = u; e[en].w = 0;
e[en].nxt = fe[v]; fe[v] = en; e[en].rev = en-1;
}
bool bfs()
{
for(int i=1; i<=n; i++) dep[i] = 0;
int head = 1,tail = 1; que[1] = s; dep[s] = 1;
while(head<=tail)
{
int u = que[head]; head++;
for(int i=fe[u]; i; i=e[i].nxt)
{
if(dep[e[i].v]==0 && e[i].w)
{
dep[e[i].v] = dep[u]+1;
tail++; que[tail] = e[i].v;
}
}
}
return dep[t]!=0;
}
llong dfs(int u,llong cur)
{
if(u==t) return cur;
llong rst = cur;
for(int i=te[u]; i; i=e[i].nxt)
{
if(dep[e[i].v]==dep[u]+1 && rst>0 && e[i].w>0)
{
llong flow = dfs(e[i].v,min(e[i].w,rst));
if(flow>0)
{
e[i].w-=flow; rst-=flow; e[e[i].rev].w+=flow;
if(e[i].w>0) {te[u] = i;}
if(rst==0) return cur;
}
}
}
if(cur==rst) dep[u] = 0;
return cur-rst;
}
llong dinic()
{
// printf("Dinic n=%d\n",n);
llong ret = 0ll;
while(bfs())
{
for(int i=1; i<=n; i++) te[i] = fe[i];
ret += dfs(s,INF);
}
return ret;
}
}
using NetFlow::addedge;
using NetFlow::dinic; struct Element
{
llong b,w,p; int a,l,r;
} a[N+3];
struct SgTNode
{
int ls,rs,prv;
} sgt[N+3];
vector<int> disc;
int rtn[N+3];
int n,siz; int getdisc(int x)
{
return lower_bound(disc.begin(),disc.end(),x)-disc.begin()+1;
} void addval(int &pos,int le,int ri,int lrb,int id,llong w1,llong w2,llong w3,int prv)
{
siz++; sgt[siz] = sgt[pos];
if(le==ri && pos!=0) {siz++; addedge(prv,siz,INF); addedge(siz,siz-1,INF); addedge(siz,sgt[pos].prv,INF); pos = siz-1; sgt[pos].prv = siz;}
else pos = siz,sgt[pos].prv = siz;
if(le==ri) {addedge(1,pos,w1); addedge(pos,id,w3); addedge(pos,2,w2); return;}
int mid = (le+ri)>>1;
if(lrb<=mid) {addval(sgt[pos].ls,le,mid,lrb,id,w1,w2,w3,pos);}
else {addval(sgt[pos].rs,mid+1,ri,lrb,id,w1,w2,w3,pos);}
if(sgt[pos].ls) {addedge(pos,sgt[pos].ls,INF);} //Judge 0
if(sgt[pos].rs) {addedge(pos,sgt[pos].rs,INF);}
} void AddEdge(int pos,int le,int ri,int lb,int rb,int id)
{
if(pos==0) return;
if(lb<=le && rb>=ri) {addedge(id,pos,INF); return;}
int mid = (le+ri)>>1;
if(lb<=mid) {AddEdge(sgt[pos].ls,le,mid,lb,rb,id);}
if(rb>mid) {AddEdge(sgt[pos].rs,mid+1,ri,lb,rb,id);}
} int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d%lld%lld%d%d%lld",&a[i].a,&a[i].b,&a[i].w,&a[i].l,&a[i].r,&a[i].p);
disc.push_back(a[i].a);
}
sort(disc.begin(),disc.end());
disc.erase(unique(disc.begin(),disc.end()),disc.end());
for(int i=1; i<=n; i++)
{
a[i].a = getdisc(a[i].a);
a[i].l = getdisc(a[i].l);
a[i].r = getdisc(a[i].r+1)-1;
if(a[i].l<1) a[i].l = 1;
if(a[i].r>disc.size()) a[i].r = disc.size();
}
// for(int i=1; i<=n; i++) printf("%d %lld %lld %d %d %lld\n",a[i].a,a[i].b,a[i].w,a[i].l,a[i].r,a[i].p);
siz = n+2;
rtn[0] = 0;
for(int i=1; i<=n; i++)
{
// printf("i%d:\n",i);
rtn[i] = rtn[i-1];
addval(rtn[i],1,disc.size(),a[i].a,i+2,a[i].b,a[i].w,a[i].p,0);
AddEdge(rtn[i-1],1,disc.size(),a[i].l,a[i].r,i+2);
// printf("siz=%d\n",siz);
// for(int j=1; j<=i; j++) printf("rtn[%d]=%d\n",j,rtn[j]);
// for(int j=1; j<=siz; j++) printf("i%d ls%d rs%d\n",j,sgt[j].ls,sgt[j].rs);
// puts("");
}
NetFlow::n = siz; NetFlow::s = 1; NetFlow::t = 2;
llong ans = 0ll;
for(int i=1; i<=n; i++) ans += a[i].b+a[i].w;
ans = ans-dinic();
printf("%lld\n",ans);
return 0;
}

BZOJ 3218 UOJ #77 A+B Problem (主席树、最小割)的更多相关文章

  1. bzoj 3218: a + b Problem【主席树+最小割】

    直接建图比较显然,是(s,i,w),(i,t,b),(i,i',p),(i,j,inf),然而建出来之后发现边数是n方级别的,显然跑不过去,然后就有一种比较神的思路:把a离散了建一棵权值线段树,然后要 ...

  2. UOJ#77. A+B Problem [可持久化线段树优化建边 最小割]

    UOJ#77. A+B Problem 题意:自己看 接触过线段树优化建图后思路不难想,细节要处理好 乱建图无果后想到最小割 白色和黑色只能选一个,割掉一个就行了 之前选白色必须额外割掉一个p[i], ...

  3. BZOJ3218 UOJ#77 A+B Problem(最小割+主席树)

    竟然在BZOJ上拿了Rank1太给力啦. p.s.:汗,一发这个就被一堆人在2月27号强势打脸-- 传送门(BZOJ) 传送门(UOJ) 说说这道题目吧: 首先是说说这个构图吧.因为有选择关系,我们很 ...

  4. bzoj 3489 A simple rmq problem——主席树套线段树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题解:http://www.itdaan.com/blog/2017/11/24/9b ...

  5. BZOJ.3489.A simple rmq problem(主席树 Heap)

    题目链接 当时没用markdown写,可能看起来比较难受...可以复制到别的地方看比如DevC++. \(Description\) 给定一个长为n的序列,多次询问[l,r]中最大的只出现一次的数.强 ...

  6. BZOJ 1926: [Sdoi2010]粟粟的书架(主席树,二分答案)

    BZOJ 1926: [Sdoi2010]粟粟的书架(主席树,二分答案) 题意 : 给你一个长为\(R\)宽为\(C\)的矩阵,第\(i\)行\(j\)列的数为\(P_{i,j}\). 有\(m\)次 ...

  7. bzoj 1926: [Sdoi2010]粟粟的书架 (主席树+二分)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1926 题面; 1926: [Sdoi2010]粟粟的书架 Time Limit: 30 Se ...

  8. bzoj 4556 [Tjoi2016&Heoi2016]字符串——后缀数组+主席树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4556 本来只要查 ht[ ] 数组上的前驱和后继就行,但有长度的限制.可以二分答案解决!然后 ...

  9. 【题解】BZOJ3489 A Hard RMQ problem(主席树套主席树)

    [题解]A simple RMQ problem 占坑,免得咕咕咕了,争取在2h内写出代码 upd:由于博主太菜而且硬是要用指针写两个主席树,所以延后2hQAQ upd:由于博主太菜而且太懒所以他决定 ...

随机推荐

  1. Android push推送消息到达成功率优化

    Android push推送消息到达成功率优化 问题:server向client发送消息.未考虑client是否在线,这种消息到达率是非常低的. 第一次优化:使用server离线缓存数据,推断假设cl ...

  2. spring:利用Spring AOP 使日志输入与方法分离

    对方法进行日志输出是一种很常见的功能.传统的做法是把输出语句写在方法体的内部,在调用该方法时,用输入语句输出信息来记录方法的执行! 1.先写一个普通类: package com.importnew; ...

  3. leetcode数组相关

    目录 4寻找两个有序数组的中位数 11盛最多水的容器,42接雨水 15三数之和,16最接近的三数之和,18四数之和 26/80删除排序数组中的重复项, 27移除元素 31下一个排列 53最大子序和 5 ...

  4. Java 8 实战 P1 Fundamentals

    目录 Chapter 1. Java 8: why should you care? Chapter 2. Passing code with behavior parameterization Ch ...

  5. King(差分约束)

    http://poj.org/problem?id=1364 题意:输入i,n,gt(lt),k; 判断是否存在这样一个序列,从第 i 项加到第 n+i 项的和 <(lt) k 或 >(g ...

  6. codevs2596 售货员的难题(状压dp)

    2596 售货员的难题  时间限制: 1 s  空间限制: 32000 KB  题目等级 : 钻石 Diamond     题目描述 Description 某乡有n个村庄(1<n<=15 ...

  7. 辨析 singleton 和 prototype

    <bean id="person1" class="com.bean.life.Person"> <property name="n ...

  8. 网络流模板(模板题:POJ1273)

    模板题:POJ1273 EK: #include <queue> #include <cstdio> #include <cstring> #include < ...

  9. javascript执行环境及作用域

    执行环境(execution context,为简单起见,有时也成为“环境”)是javascript中最为重要的一个概念.执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为.每个执行环境 ...

  10. H265

    H265 h265  一.名词 CTU: 编码树单元 CU: 编码单元 PU: 以CU为根,对CU进行划分,一个预测单元PU包含一个亮度预测块PB和两个色度预测块PB. TU: 以CU为根,变换单元T ...