NOIP 2011 提高组 复赛 day2 qc 聪明的质监员
1.题目看看样子,感觉有背包问题的味道,动态规划是免不了了。
2.当务之急,是弄明白题意,吃透样例。
3.读了半天题,发现样例的输出推不出,归结为Yi的计算西格玛看不懂怎么算,这种西格玛的写法,j加在下方,没有值,第一次看到,看不懂啊。
4.参考了http://www.doc88.com/p-8572941809341.html才弄明白题意Yi=(区间符合条件矿石个数和)*(区间符合条件矿石价格和)
5.补充一点,西格玛的优先级高于乘除。
6.如果没有时间限制,此题还是比较简单的,两个循环搞定,有些类似冒泡算法。
7.看到数据范围S<=10^12,描述要采用long long 类型了。
8.原以为矿石是按重量大小自小到大输入的,仔细搜索,没有,那么排序不可避免,冒泡排序排除,时间复杂度满足不了,那么快速排序上。较好的时间复杂度O(nlogn).
9.转念一想,拍好序有啥用呢,区间限制,拍好序的矿石与区间无法对应。
10.这样吧,为了熟悉程序,第一目标30分,第二目标50分,开始编程。
11.比较好奇,S必须用long long ,怎么输入输出数据S,linux与windows有很大不同。本人采用%lld
12.很明显Yi(Wi)是减函数。考虑遍历一次所有矿石,找出重量最轻,最重的数据。在重量区间进行每次重量加1的排查,不现实,数量太大,想来想去,没有比二分法更好了。
13.w=0,w=max两个值,猜提交有20分,提交0分,14个数据WA,6个数据TLE,那么好吧,老老实实讨论一般的情况。
14.代码写好,样例通过,提交40分,前8个数据AC,9,14WA,10-13,15-20TLE
15.再看数据范围,发现m与n是同一数量级,矿石输入未说明按重量自小到大,那么以目前的算法TLE不可避免。
16.受http://blog.csdn.net/visors/article/details/50813986(后查证long long f(int W)函数有误,
w[i]>W,应该成w[i]>=W,不建议读者跟踪此文章代码,
)启发,
准备改写long long f(int w)函数。当然,此时突然感觉此法
其实本人在NOIP 2011 提高组 复赛i] day1 选择客栈 中用过,
不过,在此处却没想到,实在可惜。思想有些类似最长上升子序列。
17.开始改造,提交,全WA,马上知道,TLE这关过了,重读题目,发现//区间处理 ,区间的含义是包括了li ,故应减li-1,此处容易出错 ,修改,提交55分,明白应该是二分法,这块出问题,开始修改。
18.对二分法找不到的情况进行模拟,对二分法的认识更深刻了。
19.多次提交都是60分,无奈祭出对拍法宝,一度怀疑是二分法的问题,随着跟踪的深入,才发现是long long f(int w)函数写得有问题。
20.提交还是60分,找了http://hzwer.com/5061.html来研究。
21.程序跟到最后,发现思路全无问题,问题出在取绝对值函数abs上,跳进stdlib.h文件才发现,对于long long 类型,函数应写成llabs,彻底服了,网上似乎没有人提到这个问题,当然,用C编的人极少。
22.前面的种种问题,竟然是栽在abs上,对于long long 应改成llabs。好吧,一天时间,就是因为这个函数,希望对后来者有用。提交AC。
23.此题还学到一招,long long 可以在C++里用cin ,cout进行输入输出,可以回避掉scanf,printf的windows,linux下的不同输入方式的问题。
2017-1-14 16:11
附上AC代码,编译环境Dev-C++4.9.9.2
//2011 qc
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define inf 9999999
struct node1{
int w;
int v;
}ks[200000+10];
struct node2{
int li;
int ri;
}qj[200000+10];
struct node3{
long long count;
long long v;
}sum[200000+10];
int n,m;
long long f(int w){
long long y;
long long count,v;
int i;
int li,ri;
y=0;
memset(sum,0,sizeof(sum));
for(i=1;i<=n;i++){//一次扫描即可。
sum[i]=sum[i-1];
if(ks[i].w>=w){
sum[i].count++;
sum[i].v+=ks[i].v;
}
}
for(i=1;i<=m;i++){//区间处理 ,区间的含义是包括了li ,故应减li-1,此处容易出错
li=qj[i].li;
ri=qj[i].ri;
count=sum[ri].count-sum[li-1].count;
v=sum[ri].v-sum[li-1].v;
y+=count*v;
}
return y;
}
int main(){
int i,j,k;
long long s;
long long count;
long long v;
long long y;
int w;
long long ans;
int min,max,mid;
scanf("%d%d%lld",&n,&m,&s);
for(i=1;i<=n;i++)
scanf("%d%d",&ks[i].w,&ks[i].v);
for(i=1;i<=m;i++)
scanf("%d%d",&qj[i].li,&qj[i].ri);
min=0;
max=-inf;
for(i=1;i<=n;i++){
if(ks[i].w>max)
max=ks[i].w;
}
if((y=f(max))>=s)
ans=y-s;
else if((y=f(min))<=s)
ans=s-y;
else{
while(min<=max){
mid=(min+max)/2;
y=f(mid);
if(y>s)
min=mid+1;
else if(y<s)
max=mid-1;
else
break;
}
if(min<=max)
ans=0;
else{
if(llabs(f(min)-s)>=llabs(f(max)-s))
ans=llabs(f(max)-s);
else
ans=llabs(f(min)-s);
}
}
printf("%lld\n",ans);
return 0;
}
附上40分代码,编译环境Dev-C++4.9.9.2
//2011 qc
#include <stdio.h>
#include <stdlib.h>
#define inf 9999999
struct node1{
int w;
int v;
}ks[200000+10];
struct node2{
int li;
int ri;
}qj[200000+10];
int n,m;
long long f(int w){
long long y;
int count,v;
int i,j;
y=0;
for(i=1;i<=m;i++){
count=0;
v=0;
for(j=qj[i].li;j<=qj[i].ri;j++){
if(ks[j].w>=w){
count++;
v+=ks[j].v;
}
}
y+=count*v;
}
return y;
}
int main(){
int i,j,k;
long long s;
int count;
int v;
long long y;
int w;
long long ans;
int min,max,mid;
scanf("%d%d%lld",&n,&m,&s);
for(i=1;i<=n;i++)
scanf("%d%d",&ks[i].w,&ks[i].v);
for(i=1;i<=m;i++)
scanf("%d%d",&qj[i].li,&qj[i].ri);
min=0;
max=-inf;
for(i=1;i<=n;i++){
if(ks[i].w>max)
max=ks[i].w;
}
if((y=f(max))>=s)
ans=y-s;
else if((y=f(min))<=s)
ans=s-y;
else{
while(min<=max){
mid=(min+max)/2;
if(f(mid)>=s&&f(mid+1)<=s)
break;
if(f(mid)>s)
min=mid;
else
max=mid;
}
if(abs(f(mid)-s)>=abs(f(mid+1)-s))
ans=abs(f(mid+1)-s);
else
ans=abs(f(mid)-s);
}
// printf("y=%lld s=%lld\n",y,s);
printf("%lld\n",ans);
return 0;
}