二分+二维前缀和+主席树【洛谷P2468】

传送门自己偷偷跑啦!
这个题很奇怪,是一个二合一的题目。
一半的数据是二维矩阵
一半数据是一维的序列

二维矩阵的数据范围只有200,我们考虑dp+二分答案:
value[i][j][k]表示从以(1,1)到(i,j)为对角线的矩阵中大于等于k的所有数字的权值和。
num[i][j][k]表示从以(1,1)到(i,j)为对角线的矩阵中大于等于k的数字的数目。
我们先预处理value和num,可是,怎么预处理二维前缀和呢?
大家都学习过容斥定理吧,我直接把预处理二维前缀和的代码放上来:

for(int k=0;k<=mx;k++)
{
	for(int i=1;i<=r;i++)
	{
		for(int j=1;j<=c;j++)
		{
			value[i][j][k] = value[i-1][j][k]+value[i][j-1][k]-value[i-1][j-1][k]+(G[i][j]>=k?G[i][j]:0);
			num[i][j][k] = num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]+(G[i][j]>=k?1:0);
		}
	}
}

前缀和要询问,二维的也不例外,询问的代码在下面:

ll getvalue(int x1,int y1,int x2,int y2,int k)
{
	return value[x2][y2][k]-value[x1-1][y2][k]-value[x2][y1-1][k]+value[x1-1][y1-1][k];
}
ll getnum(int x1,int y1,int x2,int y2,int k)
{
	return num[x2][y2][k]-num[x1-1][y2][k]-num[x2][y1-1][k]+num[x1-1][y1-1][k];
}

是不是很简单呢?

我们预处理出value和num之后就可以二分k来求出最大的下界。
然后结果就是num[i][j][ans]了吗?!
通过分析之后就发现,显然不是。
举个例子:对于序列1 2 3 4 5 6 7 8 9 3 3 3,给定h为40
显然我们二分出来的ans=3,那么num[i][j][ans]=10,所以我们要把多的东西给减掉。
value[i][j][ans]=51,那么(value[i][j][ans]-h)/ans=3,所以我们就可以算出来多出来几个了,我们把多出来的减掉,答案为num[i][j][ans]-(value[i][j][ans]-h)/ans

第二部分是一维的序列,问题的本质就是第k小,我们用主席树来维护。
由于权值的范围是1000以内,所以我们就给序列在[1,1000]之间建主席树。
T[rt].siz表示这个数出现了几次,T[rt].sum类似于前缀和。

由于我们要求的是最小值,我们显然要在区间内找最大值,由于主席树右边大,我们每次就在右边来找。统计的答案就是siz的和。
详细的在代码中:

int query(int l,int r,int x,int y,int k,int ans)
{
	if(l==r) return ans+ceil(k*1.0/l);//处理到叶子结点了,万一k没有取完,我们就把k取完。 但是要注意不要取多了,取k/l的向上取整就够了
	int mid = (l+r)/2;
	int sum = T[T[y].r].sum-T[T[x].r].sum;
	if(sum<=k) return query(l,mid,T[x].l,T[y].l,k-sum,ans+(T[T[y].r].siz-T[T[x].r].siz));
	else return query(mid+1,r,T[x].r,T[y].r,k,ans);
}

完整代码在这里:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+7;
const int maxc = 210;
int G[maxc][maxc];
int value[maxc][maxc][1010];
int num[maxc][maxc][1010];
int a[maxn];
int r,c,m;
ll getvalue(int x1,int y1,int x2,int y2,int k)
{
	return value[x2][y2][k]-value[x1-1][y2][k]-value[x2][y1-1][k]+value[x1-1][y1-1][k];
}
ll getnum(int x1,int y1,int x2,int y2,int k)
{
	return num[x2][y2][k]-num[x1-1][y2][k]-num[x2][y1-1][k]+num[x1-1][y1-1][k];
}
int root[maxn];
struct node
{
	int l;
	int r;
	int siz;
	int sum;
}T[maxn*50];
int cnt = 0;
void update(int l,int r,int &x,int y,int a)
{
	T[++cnt] =T[y];
	T[cnt].siz++;
	T[cnt].sum += a;
	x = cnt;
	if(l==r) return;
	int mid = (l+r)/2;
	if(a<=mid) update(l,mid,T[x].l,T[y].l,a);
	else update(mid+1,r,T[x].r,T[y].r,a);
} 
int query(int l,int r,int x,int y,int k,int ans)
{
	if(l==r) return ans+ceil(k*1.0/l);//处理到叶子结点了,万一k没有取完,我们就把k取完。 
	int mid = (l+r)/2;
	int sum = T[T[y].r].sum-T[T[x].r].sum;
	if(sum<=k) return query(l,mid,T[x].l,T[y].l,k-sum,ans+(T[T[y].r].siz-T[T[x].r].siz));
	else return query(mid+1,r,T[x].r,T[y].r,k,ans);
}
int main()
{
	scanf("%d%d%d",&r,&c,&m);
	if(r!=1)
	{
		int mx = -1;
		for(int i=1;i<=r;i++)
		{
			for(int j=1;j<=c;j++)
			{
				scanf("%d",&G[i][j]);
				mx = max(mx,G[i][j]);
			}
		}
		for(int k=0;k<=mx;k++)
		{
			for(int i=1;i<=r;i++)
			{
				for(int j=1;j<=c;j++)
				{
					value[i][j][k] = value[i-1][j][k]+value[i][j-1][k]-value[i-1][j-1][k]+(G[i][j]>=k?G[i][j]:0);
					num[i][j][k] = num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]+(G[i][j]>=k?1:0);
				}
			}
		}
		for(int i=0;i<m;i++)
		{
			int x1,y1,x2,y2,h;
			scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
			if(getvalue(x1,y1,x2,y2,0)<h)
			{
				printf("Poor QLW\n");
				continue;
			}
			int low = 0,high = mx+1;
			int ans = -1;
			while(low<=high)
			{
				int mid = (low+high)/2;
				if(getvalue(x1,y1,x2,y2,mid)>=h)
				{
					low = mid+1;
					ans = mid;
				}
				else
				{
					high = mid-1;
				}
			}
			printf("%d\n",getnum(x1,y1,x2,y2,ans)-(getvalue(x1,y1,x2,y2,ans)-h)/ans);
		}
	}
	else
	{
		for(int i=1;i<=c;i++)
		{
			scanf("%d",a+i);
		}
		for(int i=1;i<=c;i++)
		{
			update(1,1000,root[i],root[i-1],a[i]);
		}
		for(int i=0;i<m;i++)
		{
			int x1,y1,x2,y2,h;
			scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
			if(T[root[y2]].sum-T[root[y1-1]].sum<h)
			{
				printf("Poor QLW\n");
				continue;
			}
			printf("%d\n",query(1,1000,root[y1-1],root[y2],h,0));
		}
	} 
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/KIKO_caoyue/article/details/85135263