​http://www.elijahqi.win/archives/837​​​
题目描述

小T 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n 个矿石,从 1到n 逐一编号,每个矿石都有自己的重量 wi 以及价值vi 。检验矿产的流程是:

1 、给定m 个区间[Li,Ri];

2 、选出一个参数 W;

3 、对于一个区间[Li,Ri],计算矿石在这个区间上的检验值Yi:

这批矿产的检验结果Y 为各个区间的检验值之和。即:Y1+Y2…+Ym

若这批矿产的检验结果与所给标准值S 相差太多,就需要再去检验另一批矿产。小T

不想费时间去检验另一批矿产,所以他想通过调整参数W 的值,让检验结果尽可能的靠近

标准值S,即使得S-Y 的绝对值最小。请你帮忙求出这个最小值。

输入输出格式

输入格式:

输入文件qc.in 。

第一行包含三个整数n,m,S,分别表示矿石的个数、区间的个数和标准值。

接下来的n 行,每行2个整数,中间用空格隔开,第i+1 行表示 i 号矿石的重量 wi 和价值vi。

接下来的m 行,表示区间,每行2 个整数,中间用空格隔开,第i+n+1 行表示区间[Li,Ri]的两个端点Li 和Ri。注意:不同区间可能重合或相互重叠。

输出格式:

输出文件名为qc.out。

输出只有一行,包含一个整数,表示所求的最小值。

输入输出样例

输入样例#1:

5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
输出样例#1:

10
当作考试题,结果最近眼里全是线段树,果断二分加线段树,返回值写成int了,然后没有判断到底是大的差大还是小的差大,就只拿了5分gg

后来改了改,改成动态加点线段树,95,始终有一个点卡不过去

这是杀鸡焉用牛刀还是学习下正解吧

#include<cstdio>
#define N 220000
inline int read(){
int x=0;char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
return x;
}
struct node{
int l,r,left,right,sum1;long long sum2;int dfn;
}tree[N<<2];
int n,m,w[N],v[N],num,root,x[N],y[N];long long s;
inline int max(int x,int y){
return x>y?x:y;
}
long long abs(long long a,long long b){
if (a<=b) return b-a;else return a-b;
}
inline long long min(long long x,long long y){
return x<y?x:y;
}
inline void update(int x){
int l=tree[x].left,r=tree[x].right;
tree[x].sum1=tree[l].sum1+tree[r].sum1;
tree[x].sum2=tree[l].sum2+tree[r].sum2;
}
void build(int &x,int l,int r,int vv){
x=++num;tree[x].l=l;tree[x].r=r;
if (l==r){if (w[l]>=vv) tree[x].sum1=1,tree[x].sum2=v[l];else tree[x].sum1=tree[x].sum2=tree[x].left=tree[x].right=0;return;}
int mid=l+r>>1;
build(tree[x].left,l,mid,vv);build(tree[x].right,mid+1,r,vv);
update(x);
}
long long tmp1,tmp2;int cnt;
void query(int x,int l,int r){
if(!x) return;
if (l<=tree[x].l&&r>=tree[x].r){tmp1+=tree[x].sum1;tmp2+=tree[x].sum2;return;}
int mid=tree[x].l+tree[x].r>>1;
if (l<=mid) query(tree[x].left,l,r);
if (r>mid) query(tree[x].right,l,r);
}
void change(int &x,int l,int r,int p){
if (!x)x=++num;tree[x].l=l;tree[x].r=r;tree[x].dfn=cnt;if (tree[tree[x].left].dfn!=cnt) tree[x].left=0;if (tree[tree[x].right].dfn!=cnt) tree[x].right=0;
if (l==r){tree[x].sum1=1,tree[x].sum2=v[p]; tree[x].left=tree[x].right=0;return;}
int mid=l+r>>1;
if (p<=mid) change(tree[x].left,l,mid,p);else change(tree[x].right,mid+1,r,p);
update(x);
}
void print(int x){
if (tree[x].left) print(tree[x].left);
printf("%d %d %d %d\n",tree[x].l,tree[x].r,tree[x].sum1,tree[x].sum2);
if (tree[x].right) print(tree[x].right);
}
long long ans=1LL<<60;
inline long long check(int xx){
num=0;root=0;cnt++;//build(root,1,n,xx);
for (int i=1;i<=n;++i)
{if (w[i]>=xx) change(root,1,n,i);}
//print(root);printf("check\n");
long long tmp=0;
for (int i=1;i<=m;++i){
tmp1=0,tmp2=0;
int x1=x[i],y1=y[i];query(root,x1,y1);tmp+=tmp1*tmp2;
if (tmp-s>=ans) return s+1;
}
ans=min(abs(s,tmp),ans);
return tmp;
}
int main(){
freopen("1314.in","r",stdin);
n=read();m=read();scanf("%lld",&s);int l=1,r=0;
for (int i=1;i<=n;++i) w[i]=read(),v[i]=read(),r=max(r,w[i]);
for (int i=1;i<=m;++i) x[i]=read(),y[i]=read();
while (l<=r){
int mid=l+r>>1;long long tmpp=check(mid);
if (tmpp<=s) r=mid-1;else l=mid+1;
}
printf("%lld",ans);
return 0;
}

正解:前缀和优化+二分

#include<cstdio>
#define N 220000
inline int read(){
int x=0;char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=getchar();}
return x;
}
int n,m,w[N],v[N],num,root,x[N],y[N],sum1[N];long long s,sum2[N];
inline int max(int x,int y){
return x>y?x:y;
}
long long abs(long long a,long long b){
if (a<=b) return b-a;else return a-b;
}
inline long long min(long long x,long long y){
return x<y?x:y;
}
long long ans=1LL<<60;
inline long long check(int xx){
for(int i=1;i<=n;++i){
if (w[i]>=xx) sum1[i]=sum1[i-1]+1,sum2[i]=sum2[i-1]+v[i];else sum1[i]=sum1[i-1],sum2[i]=sum2[i-1];
}
long long tmp=0;
for (int i=1;i<=m;++i){
tmp+=(sum1[y[i]]-sum1[x[i]-1])*(sum2[y[i]]-sum2[x[i]-1]);
if (tmp-s>=ans) return s+1;
}
ans=min(abs(s,tmp),ans);
return tmp;
}
int main(){
freopen("1314.in","r",stdin);
n=read();m=read();scanf("%lld",&s);int l=1,r=0;
for (int i=1;i<=n;++i) w[i]=read(),v[i]=read(),r=max(r,w[i]);
for (int i=1;i<=m;++i) x[i]=read(),y[i]=read();
while (l<=r){
int mid=l+r>>1;long long tmpp=check(mid);
if (tmpp<=s) r=mid-1;else l=mid+1;
}
printf("%lld",ans);
return 0;
}