Discover Water Tank
Time Limit: 25000/12500 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 1026 Accepted Submission(s): 336
Problem Description
A lot of frogs are living in a water tank, but none of them know exactly how much water are there.
The water tank has an infinite height, but with a narrow bottom. The length of the tank isN, and the width is only 1.
Now N−1 boards has divided the tank into N parts, each with an 1×1 bottom. Boards may have different heights. Water cannot flow through the boards, but can run freely if the water level is higher, following the basic physical rules.
The Frog King wants to know more details of the tank, so he sent someone to chooseM positions and find whether there's water at that position.
For example, each time he'll choose(x,y), checking the xth part of the tank(1≤x≤N), counting from left to right, and find whether there's water at height (y+0.5).
Now the King gets M results, but he finds some of them might be wrong. The King wants to find out themaximum possible number of the correct results.
Input
First line contains an integer T, which indicates the number of test cases.
Every test case begins with two integers N and M, which is the numbers of tanks and numbers of results.
The second line of each test case contains N−1 integers, h1,h2,⋯,hN−1, and hi indicates the height of ith board's height.
Then M lines follow, the ith line, formated as 'x y z', indicates ith result. z is 0 if there is no water at height (y+0.5) in xth tank, otherwise z is 1.
⋅1≤T≤100.
⋅ For 90% data, 1≤N≤1000 and 1≤M≤2000
⋅ for 100% data, 1≤N≤105 and 1≤M≤2⋅105.
⋅1≤hi≤109 for all 1≤i≤N−1.
⋅ for every result, 1≤x≤N,1≤y≤109 and z is either 0 or 1.
Output
For every test case, you should output "Case #x: y",wherex indicates the case number and counts from 1, and y is the maximum possible number of the correct results.
Sample Input
2 3 4 3 4 1 3 1 2 1 0 2 2 0 3 3 1 2 2 2 1 2 0 1 2 1
Sample Output
Hint
Source
2015ACM/ICPC亚洲区上海站-重现赛(感谢华东理工)
非常巧妙的一道题。
大致题意就是,给你一个水池,有N-1个隔板分成N个格子,告诉你隔板的高度。给出你M个询问以及询问的结果,然后问你这M个询问中为真实的最多有几个。询问的格式是x、y、is,表示第x个格子,在高度为y的地方,是否有水。
首先,最基本的,你要想到,当没有水的时候,答案就是所有is==0的询问。然后当有水的时候,有可能满足条件的询问会更多。既然如此,我们显然可以对所有is==1的询问的水位按照从低到高的次序排序,然后枚举每一个水位高度,把对应格子的水位变成那个高度,统计原本为is==0的询问有多少不满足了这个记为a,然后再统计此时满足了的原本is==1的询问几位b,若b大于a,那么就在ans上加上这个差值,并把a、b清零。因为,水位到了当前高度之后可以满足更多询问。但是,在我们水位升高的时候,有可能水会溢出到别的格子里面,此时我们就要进行合并操作,即把可以相通的格子变成一个,这个可以用并查集实现。在合并的同时,我们也要把各个格子的a和b进行合并,因为会有累加效应。
合并操作完成了,那么我们就要看看,如何统计原本is==0的询问中有多少个到现在了已经不满足。在这里,如果没有合并操作,我们直接用一个堆即可,每次把所有小于当前水位的询问都弹出堆,弹出的次数就是我们要统计的a。但是这里要支持合并,所以我们有两种方式,我们可以选用一些可并堆,比如说我这里用的左偏树;另一种方式就是用STL优先队列加上启发式合并,这个理论上也是可以的。
大致就是如此,利用数据结构和排序,进行一个巧妙的贪心。具体见代码:
左偏树版本:
#include<bits/stdc++.h>
#define N 200010
using namespace std;
struct node
{
int l,r,num,dist;
} tree[N];
int l[N],r[N],lh[N],rh[N],O[N],X[N];
struct quest{int x,y,is;} q[N];
int f[N],heap[N],ans,tot,m,n;
vector<quest> a;
int find(int x)
{
return f[x]==x ? x:(f[x]=find(f[x]));
}
bool cmp(quest a,quest b)
{
return a.y<b.y;
}
inline int merge(int x,int y)
{
if (!x||!y) return x+y;
if (tree[x].num>tree[y].num) swap(x,y);
tree[x].r=merge(y,tree[x].r);
if (tree[tree[x].l].dist<tree[tree[x].r].dist) swap(tree[x].l,tree[x].r);
if (tree[x].r) tree[x].dist=tree[tree[x].r].dist+1;
else tree[x].dist=0;
return x;
}
inline int del(int x)
{
int l=tree[x].l,r=tree[x].r;
tree[x].l=tree[x].r=tree[x].dist=0;
return merge(l,r);
}
inline int New(int x)
{
tree[++tot].num=x;
tree[tot].l=tree[tot].r=tree[tot].dist=0;
return tot;
}
void init()
{
ans=tot=0; a.clear();
memset(X,0,sizeof(X));
memset(O,0,sizeof(O));
memset(tree,0,sizeof(tree));
memset(heap,0,sizeof(heap));
}
int Union(int x,int y)
{
x=find(x); y=find(y);
O[y]+=O[x]; X[y]+=X[x];
heap[y]=merge(heap[x],heap[y]);
if (x<y) lh[y]=lh[x],r[l[x]]=y,l[y]=l[x];
else rh[y]=rh[x],l[r[x]]=y,r[y]=r[x];
f[x]=y; return y;
}
int main()
{
int T_T,T;
cin>>T_T;T=0;
while(T_T--)
{
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
f[i]=i;
for(int i=1;i<n;i++)
{
l[i+1]=i; r[i]=i+1;
int x;scanf("%d",&x);
lh[i+1]=rh[i]=x;
}
l[n]=n-1;
lh[1]=rh[n]=0x3f3f3f3f;
for(int i=1;i<=m;i++)
{
int x,y,is;
scanf("%d%d%d",&x,&y,&is);
if (is) a.push_back(quest{x,y+1,is});
else
{
heap[x]=merge(heap[x],New(y));
ans++;
}
}
sort(a.begin(),a.end(),cmp);
for(int i=0;i<a.size();i++)
{
int x=find(a[i].x),y=a[i].y;
while(lh[x]<y) x=Union(l[x],x);
while(rh[x]<y) x=Union(r[x],x);
while(heap[x]&&tree[heap[x]].num<y)
{
heap[x]=del(heap[x]);
X[x]++;
}
O[x]++;
if (O[x]>X[x])
{
ans+=O[x]-X[x];
O[x]=X[x]=0;
}
}
printf("Case #%d: %d\n",++T,ans);
}
}
优先队列+启发式合并版本:
#include<bits/stdc++.h>
#define N 200010
using namespace std;
priority_queue<int,vector<int>,greater<int> > q[N];
int l[N],r[N],lh[N],rh[N],O[N],X[N];
struct quest{int x,y,is;};
int f[N],t[N],ans,m,n;
vector<quest> a;
int find(int x)
{
return f[x]==x ? x:(f[x]=find(f[x]));
}
bool cmp(quest a,quest b)
{
return a.y<b.y;
}
void init()
{
ans=0; a.clear();
memset(X,0,sizeof(X));
memset(O,0,sizeof(O));
memset(q,0,sizeof(q));
memset(t,0,sizeof(t));
}
void merge(int x,int y)
{
while(!q[x].empty())
{
q[y].push(q[x].top());
q[x].pop(); t[y]++;
}
}
int Union(int x,int y)
{
x=find(x); y=find(y);
if (t[x]>t[y]) swap(x,y);
O[y]+=O[x]; X[y]+=X[x];
merge(f[x],f[y]);
if (x<y) lh[y]=lh[x],r[l[x]]=y,l[y]=l[x];
else rh[y]=rh[x],l[r[x]]=y,r[y]=r[x];
f[x]=y; return y;
}
int main()
{
int T_T,T;
cin>>T_T;T=0;
while(T_T--)
{
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
f[i]=i;
for(int i=1;i<n;i++)
{
l[i+1]=i; r[i]=i+1;
int x;scanf("%d",&x);
lh[i+1]=rh[i]=x;
}
l[n]=n-1;
lh[1]=rh[n]=0x3f3f3f3f;
for(int i=1;i<=m;i++)
{
int x,y,is;
scanf("%d%d%d",&x,&y,&is);
if (is) a.push_back(quest{x,y+1,is});
else
{
q[f[x]].push(y);
ans++;
}
}
sort(a.begin(),a.end(),cmp);
for(int i=0;i<a.size();i++)
{
int x=find(a[i].x),y=a[i].y;
while(lh[x]<y) x=Union(l[x],x);
while(rh[x]<y) x=Union(r[x],x);
while(!q[f[x]].empty()&&q[f[x]].top()<y)
{
q[f[x]].pop(); X[x]++;
}
O[x]++;
if (O[x]>X[x])
{
ans+=O[x]-X[x];
O[x]=X[x]=0;
}
}
printf("Case #%d: %d\n",++T,ans);
}
}