NOIp 2011 Day2 解题报告

1.计算系数

本人比较耿直,没有想到递推的组合数公式,而是用了快速幂求逆元。

复杂度O(Klog10007)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7
 8 //variable//
 9 int a,b,k,n,m,mod=10007;
10
11 //function prototype//
12 int qpower(int,int,int);
13
14 //solve//
15 int main(){
16     scanf("%d%d%d%d%d",&a,&b,&k,&n,&m);
17     int ans=1;
18     ans=(ans*qpower(a,n,mod))%mod;
19     ans=(ans*qpower(b,m,mod))%mod;
20     for (int i=1;i<=k;++i){
21         ans=(ans*i)%mod;
22     }
23     for (int i=1;i<=n;++i){
24         ans=(ans*qpower(i,mod-2,mod))%mod;
25     }
26     for (int i=1;i<=m;++i){
27         ans=(ans*qpower(i,mod-2,mod))%mod;
28     }
29     printf("%d\n",ans);
30     return 0;
31 }
32
33 int qpower(int a,int b,int m){
34     int ret=1;
35     a%=m;
36     while (b){
37         if (b&1){
38             ret=(ret*a)%m;
39         }
40         b>>=1;
41         a=(a*a)%m;
42     }
43     return ret;
44 }

2.聪明的质检员

我们看出,随着基准值W的增大,检验值Y减小。于是想到二分。

满足wj≥W的数和这种数的数量都满足前缀和性质。于是对于二分到的W,可以O(N+M)求出每个区间的Y。

总复杂度O((N+M)logN)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 using namespace std;
 7
 8 //variable//
 9 int n,m,l[200100],r[200100],w[200100],v[200100],tot[200100];
10 long long s,sum[200100];
11
12 //function prototype//
13 bool judge(int);
14 void calc_ans(void);
15 long long calc_y(int);
16
17 //solve//
18 int main(){
19     scanf("%d%d%lld",&n,&m,&s);
20     for (int i=1;i<=n;++i){
21         scanf("%d%d",w+i,v+i);
22     }
23     for (int i=1;i<=m;++i){
24         scanf("%d%d",l+i,r+i);
25     }
26     calc_ans();
27     return 0;
28 }
29
30 void calc_ans(){
31     int left=1,right=1000000;
32     while (left<=right){
33         int mid=left+right>>1;
34         if (judge(mid)){
35             left=mid+1;//sum>=s
36         }else{
37             right=mid-1;//sum<s
38         }
39     }
40     long long yl=calc_y(left),yr=calc_y(right);
41     printf("%lld\n",min(s-yl,yr-s));
42 }
43
44 bool judge(int x){
45     long long y=calc_y(x);
46     return (y>=s);
47 }
48
49 long long calc_y(int x){
50     tot[0]=0;sum[0]=0ll;
51     for (int i=1;i<=n;++i){
52         sum[i]=sum[i-1];
53         tot[i]=tot[i-1];
54         if (w[i]>=x){
55             sum[i]+=(long long)v[i];
56             ++tot[i];
57         }
58     }
59     long long y=0ll;
60     for (int i=1;i<=m;++i){
61         y+=((long long)tot[r[i]]-tot[l[i]-1])*(sum[r[i]]-sum[l[i]-1]);
62     }
63     return y;
64 }

3.观光公交

一直到60%的数据都是可以DP的。真正的正解是贪心。

首先考虑如何计算结果,我们令maxArrivei表示从i点出发的人,Ti的最大值,也就是最迟的到达时间,没人从i出发则为0。再令enteri表示到达i点的最早时间,那么

enteri=Max(enteri−1,maxArrivei−1)+Di−1 (3)

那么对于从Ai点到Bi点的旅客i,他用的时间为

enterBi−Ti (4)

那么我们消耗的总时间就是

Summi=1(enterBi−Ti)=(Summi=1enterBi)−(Summ i=1Ti) (5)

由于减去的是一个常量,所以我们只要得到Summi=1enterBi的最小值即可。

设第i个点有cnti个人以该点为终点。那么答案也就是

Sumni=1cnti∗enteri (6)

接下来我们进一步观察enter的计算方法,可以发现3式等价于

enteri=Maxi−1 j=1(maxArrivej+Si−Sj) (7)

其中Si表示1点到i点的距离。

我们令Vi=maxArrivei−Si那么

enteri=Maxi−1 j=1(Vj)+Si (8)

那么考虑减少一个Di,会产生什么影响。可以发现所有的j≤i的点的S,V都没有变化,所有j>i的点,Sj=Sj−1,Vj=Vj+1由于每个点的enteri值都取决于前面最大的V值,如果有多个最大的话,不妨让它被最后一个决定。那么考虑一个点的V值,他假如要决定后面一些enter值的话,必须满足之前的所有V值都不比他大。 我们可以令决定了后面的一些enter值的点分别为a0,a1,a2...ap−1,那么显然他们的V值也是单调不减的(由于条件)。同时易知每个ai必然决定了连续的一个子序列。也就是讲整个序列分成了一块一块,每块都有一个vai决定。ai决定了ai+1∼ai+1这一段的enter值。

我们可以发现,假如我们减少ai∼ai+1−1这些点中一个的D值,那么ai前面的块 完全不受影响,ai+1和之后的块的点x,Maxx−1 j=1(Vj)+了1,但是Sx−了1,所以enter值不变,同时也没有任何其它的影响。所以每个块之间是独立的。 考虑块[l,r],被[l,r]之前一个数决定,那么我们将Dl−1−1,可以将结果减少Sumi=r i=lcnti,同时由于将[l,r]中的数的V值变大了,[l,r]可能被分成几块,那样之后的操作就比之前的 操作烂了(因为每次只能减少一个块的Sumi=r i=lcnti,而块变小了),也就是说对于每个块来说,一次操作能取得的总时间减少量只会越来越少。 那么考虑当前能节省最大时间的块,如果不取他,那么由于之后的操作都比他要劣,所以随便把之后的一个操作换成他就能更优,而那样就不是最优解了。所以一定要取他。

其实只需要开一个堆,每次从堆里取出能减少最大时间的那个块[l,r],消耗k将Dl−1减小,那么有3种情况。

(1):要么Dl−1被减成0,那么就只能减Dl了,将[l+1,r]放进堆

(2):要么k被减完,结束。

(3):要么减到一半时,[l,r]中的一个数变成了新的可以决定后面enter值的数,那么[l,r]分裂。

(1)和(3)都最多出现n次,(2)最多出现1次,每次操作复杂度O(logn)。所以总复杂度 O(m+nlogn)

然而不开堆暴力貌似也是可以的。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6
 7 //variable//
 8 int n,m,k,a[10010],b[10010],c[10010],t[10010];
 9 int r[1010],get[1010],off[1010],in[1010],leave[1010],d[1010],s[1010];
10 long long ans;
11
12 //solve//
13 int main(){
14     scanf("%d%d%d",&n,&m,&k);
15     for (int i=1;i<n;++i) scanf("%d",d+i);
16     for (int i=0;i<m;++i){
17         scanf("%d%d%d",t+i,a+i,b+i);
18         off[b[i]]++;in[a[i]]++;
19         leave[a[i]]=max(leave[a[i]],t[i]);
20     }
21     for (int i=2;i<=n;++i){
22         get[i]=max(get[i-1],leave[i-1])+d[i-1];
23     }
24     int p=1;
25     for (int i=1;i<=n;++i){
26         while (p<n&&leave[p]<get[p]||p<=i) p++;
27         r[i]=p;
28         s[i]=s[i-1]+off[i];
29     }
30     while (k){
31         int maxp=0,station;
32         for (int i=1;i<n;++i)
33             if (s[r[i]]-s[i]>maxp&&d[i]>0){
34                 maxp=s[r[i]]-s[i];
35                 station=i;
36             }
37         if (maxp==0) break;
38         int maxt=0x7fffffff;
39         for (int j=station+1;j<n&&leave[j]<get[j];++j)
40             maxt=min(maxt,get[j]-leave[j]);
41         int maxDecK=min(d[station],min(maxt,k));
42         k-=maxDecK;
43         d[station]-=maxDecK;
44         for (int j=station+1;j<=r[station];++j)
45             get[j]=max(get[j-1],leave[j-1])+d[j-1];
46         for (int j=p=station;j<r[station];++j){
47             while (p<n&&leave[p]<get[p]||p<=j) p++;
48             if (p>=r[j]) break;
49             r[j]=p;
50         }
51     }
52     int ans=0;
53     for (int i=0;i<m;++i){
54         ans+=(long long)(get[b[i]]-t[i]);
55     }
56     cout<<ans<<endl;
57     return 0;
58 }

时间: 2024-08-06 11:57:32

NOIp 2011 Day2 解题报告的相关文章

NOIp 2013 Day2 解题报告

NOIp 2013 Day2 解题报告 1.   积木大赛 每次只要选取连续最大的一段区间即可. 继续归纳可得,答案为∑i=1nmax{0,hi-hi-1} 复杂度O(N) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //variable/

NOIp 2011 Day1 解题报告

1.    铺地毯 送分题…… 枚举给定矩形,判定给定点是否在矩形内.求出标号最大的哪个矩形即可. 复杂度O(N) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //variable// 9 int n,a[10010],b[10010],g

常州培训 day2 解题报告

第一题: 题目大意: 给出一个M面的骰子,投N次,求最大期望值. 最大期望值的定义: 比如M=2,N=2, 那么 2次可以是 1,1,最大值为1: 1,2最大值为2: 2,1最大值为2: 2,2 最大值为2: 最大期望值就是  (1+2+2+2)/4=1.75 也就是把所有情况的最大值加起来,除以方案数.M,N<=5000 解题过程:1.这题只能说自己数学水平不够,只能找找规律骗点分,竟然混了个40分.小的数据直接模拟,拿40分. 2.正解: 考虑最大值为1的情况,sum=1*(1^N-0^N)

NOIp 2013 Day1 解题报告

NOIp 2013 Day1 解题报告 1.   转圈游戏 不难看出答案就是(x+m*10k) mod n 用快速幂算法,复杂度O(log2k) 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 8 //variable// 9 int n,m,x,

2016.7.12 NOIP2013提高组 day2解题报告(未完成版)

考试马不停蹄地到来,昨天的程序还没改完,今天又考了day2,虽然没有昨天那么懵逼,但还是不尽如人意,现在还没讲题,我打算先自己看一次解题报告,争取加深理解,毕竟一位前辈说过,做一套题的质量取决于题本身的质量和你思考的程度. 考试总结: 1.数据分析推测可行算法很重要,要灵活掌握常用算法的时间复杂度: 2.对拍的方式要熟练,写对拍耗费的时间过多: 3.要加强代码实现的能力,比较突出的表现就是写200-300行多函数模拟或搜索的能力: 4.不要急于写过不完的程序,要多拿一点时间来分析数据,样例不够还

【未完成0.0】Noip2012提高组day2 解题报告

第一次写一套题的解题报告,感觉会比较长.(更新中Loading....):) 题目: 第一题:同余方程 描述 求关于x的同余方程ax ≡ 1 (mod b)的最小正整数解. 格式 输入格式 输入只有一行,包含两个正整数a, b,用一个空格隔开. 输出格式 输出只有一行,包含一个正整数x0,即最小正整数解.输入数据保证一定有解. 样例1 样例输入1 3 10 样例输出1 7 限制 每个测试点1s 提示 对于40%的数据,2 ≤b≤ 1,000: 对于60%的数据,2 ≤b≤ 50,000,000:

CH Round #55 - Streaming #6 (NOIP模拟赛day2)解题报告

T1九九归一 描述 萌蛋在练习模n意义下的乘法时发现,总有一些数,在自乘若干次以后,会变成1.例如n=7,那么5×5 mod 7=4,4×5 mod 7=6,6×5 mod 7=2,2×5 mod 7=3,3×5 mod 7=1.如果继续乘下去,就会陷入循环当中.萌蛋还发现,这个循环的长度经常会是φ(n),即小于n且与n互质的正整数的个数.例如,φ(7)=6,而上述循环的长度也是6,因为5,4,6,2,3,1共有6个数.再如n=6,那么5×5 mod 6=1.这个循环的长度很短,只有2,而恰好φ

TJOI2015 day2解题报告

其实第三题还没看啦~~前两题就总结下吧 T1:[TJOI2015]旅游 描述:(BZ没题面只能口述了..)一个人在一棵树上走,每次从a->b会进行一次贸易(也就是在这条路径上买入物品然后在后面卖出)然后每次经过一个点该点的物品价格会上涨v,求每次贸易的最大获利 很裸的一道树链剖分,就是题目描述太不明白了..这样就是在某条路径上找到某个点减去后面路径的最小点的值的最大值.可以用线段树的区间合并解决.就是在求答案时的合并答案上方向搞反了查了很久...以前也犯过着种错误,以后不能再犯了.. CODE:

NOIp2016 Day1&amp;Day2 解题报告

Day1 T1 toy 本题考查你会不会编程. //toy //by Cydiater //2016.11.19 #include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <map> #in