启智树提高组day4T1 T1(t1.cpp,1s,512MB)

启智树提高组day4T1 T1(t1.cpp,1s,512MB)    

题面描述    

度为2n 的实数序列A考虑下列问题:

设S为序列中所有元素的和。你可以做下列操作n次:

  • 选择两个未被选中过的下标i和j,要求i≠j ;
  • 将 Ai变为不超过Ai的最整数,即将 Ai向下取整;
  • 将Aj变为不小于Aj的最小整数,即将Aj向上取整。

要求操作完成之后,新的序列中所有元素之和S'和S的差的绝对值尽量小。

现在给你度为m的实数序列B,有Q组询问。每组询问要求将这个序列的个区间作为上述问题中的A求解。

输入格式

⼀⾏两个正整数m ,Q 。第⼆⾏ m个 0到 10^4之间的实数Ai ,每个实数都恰好保留了三位小数。

然后Q,每两个正整数Li,Ri ,表此次需要求解的区间为[Li,Ri] ,保证。

输出格式    

Q,每⾏⼀个实数,保留三位小数,表此次询问中 和 的差的绝对值可能取到的最小值。

样例输入

样例输出

样例解释

数据范围

解题思路

我的基本想法

输入这个数组,记录每个数字的小数部分;

取出并复制其需要区间

排序,但是要先去掉小数区间为0的,因为零的话既可以向上取整,也可以向下取整

一个循环,循环区间中不为0 的数字的个数的一半。每次循环时取出一个最小值和最大值;

    但是如果这时,最小值>0.5的话就要考虑使用0来补位。最大值同理。

 1 while(li<=ri)
 2 
 3 {
 4 
 5 if(range[li]>0.5&&zero) li--,zero--;
 6 
 7 else{
 8 
 9 ans-=range[li];
10 
11 // cout<<"ans+="<<range[li]<<endl;
12 
13 }
14 
15 if(range[ri]<=0.5&&zero) ri++,zero--;
16 
17 else{
18 
19 ans+=1-range[ri];
20 
21 // cout<<"ans+=1-"<<range[ri]<<endl;
22 
23 }
24 
25 li++;
26 
27 ri--;
28 
29 }

 

Code

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<iomanip>
 7 #include<map>
 8 #include<set>
 9 #include<queue>
10 #include<vector>
11 #define IL inline
12 #define re register
13 #define LL long long
14 #define ULL unsigned long long
15 using namespace std;
16 
17 template<class T>inline void read(T&x)
18 {
19     char ch=getchar();
20     while(!isdigit(ch))ch=getchar();
21     x=ch-'0';ch=getchar();
22     while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
23 }
24 int G[55];
25 template<class T>inline void write(T x)
26 
27 {
28     int g=0;
29     do{G[++g]=x%10;x/=10;}while(x);
30     for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
31 }
32 
33 double a[500000],m[500000];
34 int M,Q;
35 vector<double>range;
36 double ans;
37 int main()
38 {
39     cin>>M>>Q;
40     for(re int i=1;i<=M;i++) cin>>a[i],m[i]=a[i]-floor(a[i]);
41     int l,r;
42     while(Q--)
43     {
44         read(l),read(r);
45         range.clear();
46         ans=0;
47         int zero=0;
48         for(re int i=l;i<=r;i++) if(m[i]) range.push_back(m[i]); else zero++;
49         sort(range.begin(),range.end());    
50         re int li=0,ri=range.size()-1;
51         while(li<=ri)
52         {
53             if(range[li]>0.5&&zero) li--,zero--; 
54             else{
55                 ans-=range[li];
56 //                cout<<"ans+="<<range[li]<<endl;
57             }
58             if(range[ri]<=0.5&&zero) ri++,zero--;
59             else{
60                 ans+=1-range[ri];
61 //                cout<<"ans+=1-"<<range[ri]<<endl;
62             }
63             li++;
64             ri--;
65         }
66         
67         cout<<fixed<<setprecision(3)<<fabs(ans)<<endl;
68     }
69     return 0;
70 }
暴力

提交的时候,忘记去除调试信息……啊啊啊啊啊啊……

当然,这是暴力,10分吧。

正解

观察暴力,可以发现,一段区间的S’只和实数和,下取整和,上取整和有关系

所以我们只要给三种(实数,下取整小数,上取整小数)搞一个前缀和就好。

Code

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 template<class T>inline void read(T&x)
 4 {
 5     char ch=getchar();
 6     while(!isdigit(ch))ch=getchar();
 7     x=ch-'0';ch=getchar();
 8     while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
 9 }
10 int G[55];
11 template<class T>inline void write(T x)
12 
13 {
14     int g=0;
15     do{G[++g]=x%10;x/=10;}while(x);
16     for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
17 }
18 int N,q,c[1<<19];double t[1<<19],s[1<<19];
19 int main(){
20     scanf("%d%d",&N,&q);
21     for(int i=1;i<=N;i++){
22         double x;
23         scanf("%lf",&x),s[i]=s[i-1]+x,t[i]=t[i-1]+ceil(x);
24         c[i]=c[i-1]+(floor(x)!=ceil(x));
25     }
26     while(q--){
27         int l,r;read(l),read(r);
28         int n=r-l+1>>1,cnt=c[r]-c[l-1];
29         double tot=s[r]-s[l-1],tt=t[r]-t[l-1];//取出这一部分的和 
30         if(tt-min(cnt,n)>tot) tt-=min(cnt,n);
31         else{
32             int v=min({cnt,n,int(tt-tot+.5-1e-8)}),t=n-v;
33             tt-=v;
34             if(2*n-cnt<t) tt-=t-(2*n-cnt);
35         }
36         printf("%.3lf\n",abs(tt-tot));
37     }
38     return 0;
39 }

小结

提交时去除调试信息

posted @ 2020-08-20 11:54  Vanilla_chan  阅读(251)  评论(0编辑  收藏  举报