题解 P1314 【聪明的质监员】
sun1yu1jia1 · · 题解
发一个特别的题解,给大家扩展一下思路。
本方法仅供参考,因为跑得很慢,非常慢。
此题我定睛一看,求区间大于W的个数以及大于W的元素的权值和,这不是经典的主席树题吗?
我们维护两个主席树,第一个主席树存储前i个元素中大于W的元素个数,第二个主席树存储前i个元素中大于W的元素的权值和。
这样我们用第j个主席树减第i-1个主席树,然后乘起来就可以得到区间[i, j]的Y了。
也就是说,现在我们只要有了W,就可以求得Y。
我们可以发现,W越大,每一个区间大于W的元素一定越少,同时权值和也跟着越少,即W越大,Y越小。
那么我们二分出一个W(二分范围:1~max(wi)+1),计算出Y。
如果Y==S,直接输出0;
如果Y>S,枚举更大的W,这样可以使Y小一些;
如果Y<S枚举更小的W,使Y大一些。
和一般的二分不同的是无论Y和S哪个大,都要更新一下答案。
注意一下:
0 < wi, vi≤10^6,要开long long,同时要离散化。
然后我交了一下, 3192ms!吓得我赶紧把离散化中的lower_bound改成了哈希,然后把动态分配内存改成了内存池, 2704ms 。
代码如下:
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
inline long long Read()
{
long long i = 0, w = 1; char ch = 0;
while (ch > '9' || ch < '0')
{
if (ch == '-')
w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
i = i * 10 + ch - '0';
ch = getchar();
}
return i * w;
}
#define HASH_SIZE 3214567
#define RAM_SIZE 2400010
// 内存池
void * GetMemory(int iSize)
{
static char arrMem[RAM_SIZE];
static char * pTmpMem = arrMem;
static int iMemTop;
char * pAns = &pTmpMem[iMemTop];
iMemTop += iSize;
return pAns;
}
// 哈希
struct SHashNode {
int iData, iIndex;
SHashNode * pNext;
} * arrHash[HASH_SIZE];
void AddHash(int iData, int iIndex)
{
int iHash = iData % HASH_SIZE;
if (iHash < 0)
iHash = -iHash;
SHashNode * pTmp = arrHash[iHash];
while (pTmp)
{
if (pTmp->iData == iData)
return;
pTmp = pTmp->pNext;
}
pTmp = (SHashNode *)GetMemory(sizeof(SHashNode));
pTmp->iData = iData;
pTmp->iIndex = iIndex;
pTmp->pNext = arrHash[iHash];
arrHash[iHash] = pTmp;
}
struct SNode {
long long iData;
int iLNode, iRNode;
} arrNode[200010 << 5]; int iNodeTop;
struct SArea {
int iLeft, iRight;
} arrArea[200010];
// 矿石的个数、区间的个数和标准值
long long iNumTot, iAreaTot, iStd;
// i 号矿石的重量 wi 和价值vi
int arrWeight[200010], arrValue[200010];
// 二分右端点
int iMaxWeight;
// 两个主席树
int arrChairNum[200010], arrChairValue[200010];
// 离散化
int arrUnique[200010], iUnique;
// 在哈希中查找离散值
int GetIndex(int x)
{
int iHash = x % HASH_SIZE;
if (iHash < 0)
iHash = -iHash;
SHashNode * pTmp = arrHash[iHash];
while (pTmp)
{
if (pTmp->iData == x)
return pTmp->iIndex;
pTmp = pTmp->pNext;
}
return 0;
}
// 创建主席树根节点
int CreateChair(int iBegin, int iEnd)
{
int iNode = ++iNodeTop;
if (iBegin == iEnd)
return iNode;
int iMid = (iBegin + iEnd) / 2;
arrNode[iNode].iLNode = CreateChair(iBegin, iMid);
arrNode[iNode].iRNode = CreateChair(iMid + 1, iEnd);
return iNode;
}
// 更新主席树,iDelta为变化量
// 对于第一个主席树,iDelta==1
// 对于第二个主席树,iDelta==vi
int UpdateChair(int iBefNode, int iBegin, int iEnd, int iIndex, int iDelta)
{
int iNode = ++iNodeTop;
arrNode[iNode].iData = arrNode[iBefNode].iData + iDelta;
if (iBegin == iEnd)
return iNode;
int iMid = (iBegin + iEnd) / 2;
if (iIndex <= iMid)
{
arrNode[iNode].iLNode = UpdateChair(arrNode[iBefNode].iLNode, iBegin, iMid, iIndex, iDelta);
arrNode[iNode].iRNode = arrNode[iBefNode].iRNode;
}
else
{
arrNode[iNode].iLNode = arrNode[iBefNode].iLNode;
arrNode[iNode].iRNode = UpdateChair(arrNode[iBefNode].iRNode, iMid + 1, iEnd, iIndex, iDelta);
}
return iNode;
}
// 获取主席树和
long long GetSum(int iBefNode, int iAftNode, int iLeft, int iRight, long long iM)
{
if (arrUnique[iRight] < iM)
return 0;
if (arrUnique[iLeft] >= iM)
return arrNode[iAftNode].iData - arrNode[iBefNode].iData;
long long iAns = 0; int iMid = (iLeft + iRight) / 2;
iAns += GetSum(arrNode[iBefNode].iLNode, arrNode[iAftNode].iLNode, iLeft, iMid, iM);
iAns += GetSum(arrNode[iBefNode].iRNode, arrNode[iAftNode].iRNode, iMid + 1, iRight, iM);
return iAns;
}
long long GetY(long long iM)
{
long long iAns = 0;
for (int i = 1; i <= iAreaTot; i++)
{
long long a = GetSum(arrChairNum[arrArea[i].iLeft - 1], arrChairNum[arrArea[i].iRight], 1, iUnique, iM);
long long b = GetSum(arrChairValue[arrArea[i].iLeft - 1], arrChairValue[arrArea[i].iRight], 1, iUnique, iM);
iAns += a * b;
}
return iAns;
}
int main()
{
// 读入
iNumTot = Read();
iAreaTot = Read();
iStd = Read();
for (int i = 1; i <= iNumTot; i++)
{
arrUnique[++iUnique] = arrWeight[i] = Read();
iMaxWeight = max(iMaxWeight, arrWeight[i]);
arrValue[i] = Read();
}
// 离散化
sort(arrUnique + 1, arrUnique + 1 + iUnique);
iUnique = unique(arrUnique + 1, arrUnique + 1 + iUnique) - arrUnique;
iUnique--;
for (int i = 1; i <= iUnique; i++)
AddHash(arrUnique[i], i);
for (int i = 1; i <= iAreaTot; i++)
arrArea[i] = (SArea) { Read(), Read() };
// 准备主席树
arrChairNum[0] = CreateChair(1, iUnique);
arrChairValue[0] = CreateChair(1, iUnique);
for (int i = 1; i <= iNumTot; i++)
{
arrChairNum[i] = UpdateChair(arrChairNum[i - 1], 1, iUnique, GetIndex(arrWeight[i]), 1);
arrChairValue[i] = UpdateChair(arrChairValue[i - 1], 1, iUnique, GetIndex(arrWeight[i]), arrValue[i]);
}
// 二分
long long iAns = 40000000000000LL;
int iLeft = 1, iRight = iMaxWeight + 1, iMid;
while (iLeft <= iRight)
{
iMid = (iLeft + iRight) / 2;
long long iTmpY = GetY(iMid);
iAns = min(abs(iStd - iTmpY), iAns);
if (iTmpY < iStd)
iRight = iMid - 1;
else if (iTmpY > iStd)
iLeft = iMid + 1;
else
{
iAns = 0;
break;
}
}
printf("%lld", iAns);
return 0;
}