NOIP 2011 提高组 复赛 day2 qc 聪明的质监员

来源:互联网 发布:数据建模工具 编辑:程序博客网 时间:2024/04/29 04:14

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;
}




0 0