【未完成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; 
对于100%的数据,2 ≤a, b≤ 2,000,000,000。

第二题:借教室

描述

在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。

面对海量租借教室的信息,我们自然希望编程解决这个问题。我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借。共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要租借dj个教室。 
我们假定,租借者对教室的大小、地点没有要求。即对于每份订单,我们只需要每天提供dj个教室,而它们具体是哪些教室,每天是否是相同的教室则不用考虑。

借教室的原则是先到先得,也就是说我们要按照订单的先后顺序依次为每份订单分配教室。如果在分配的过程中遇到一份订单无法完全满足,则需要停止教室的分配,通知当前申请人修改订单。这里的无法满足指从第sj天到第tj天中有至少一天剩余的教室数量不足dj个。

现在我们需要知道,是否会有订单无法完全满足。如果有,需要通知哪一个申请人修改订单。

格式

输入格式

第一行包含两个正整数n,m,表示天数和订单的数量。 
第二行包含n个正整数,其中第i个数为ri,表示第i天可用于租借的教室数量。 
接下来有m行,每行包含三个正整数dj,sj,tj,表示租借的数量,租借开始、结束分别在第几天。 
每行相邻的两个数之间均用一个空格隔开。天数与订单均用从1开始的整数编号。

输出格式

如果所有订单均可满足,则输出只有一行,包含一个整数0。否则(订单无法完全满足)输出两行,第一行输出一个负整数-1,第二行输出需要修改订单的申请人编号。

样例1

样例输入1

4 3
2 5 4 3
2 1 3
3 2 4
4 2 4 

样例输出1

-1
2

限制

每个测试点1s

提示

对于10%的数据,有1≤ n,m≤ 10; 
对于30%的数据,有1≤ n,m≤1000; 
对于70%的数据,有1≤ n,m≤ 10^5; 
对于100%的数据,有1≤n,m≤10^6,0≤ri,dj≤10^9,1≤sj≤tj≤n。

第三题:疫情控制

描述

H国有n个城市,这n个城市用n-1条双向道路相互连通构成一棵树,1号城市是首都,也是树中的根节点。 
H国的首都爆发了一种危害性极高的传染病。当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点。但特别要注意的是,首都是不能建立检查点的。 
现在,在H国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队。一支军队可以在有道路连接的城市间移动,并在除首都以外的任意一个城市建立检查点,且只能在一个城市建立检查点。一支军队经过一条道路从一个城市移动到另一个城市所需要的时间等于道路的长度(单位:小时)。 
请问最少需要多少个小时才能控制疫情。注意:不同的军队可以同时移动。

格式

输入格式

第一行一个整数n,表示城市个数。 
接下来的n-1行,每行3个整数,u、v、w,每两个整数之间用一个空格隔开,表示从城市u到城市v有一条长为w的道路。数据保证输入的是一棵树,且根节点编号为1。 
接下来一行一个整数m,表示军队个数。 
接下来一行m个整数,每两个整数之间用一个空格隔开,分别表示这m个军队所驻扎的城市的编号。

输出格式

共一行,包含一个整数,表示控制疫情所需要的最少时间。如果无法控制疫情则输出-1。

样例1

样例输入1

4
1 2 1
1 3 2
3 4 3
2
2 2 

样例输出1

3

限制

每个测试点2s

提示

保证军队不会驻扎在首都。 
对于20%的数据,2≤ n≤ 10; 
对于40%的数据,2 ≤n≤50,0<w <10^5; 
对于60%的数据,2 ≤ n≤1000,0<w <10^6; 
对于80%的数据,2 ≤ n≤10,000; 
对于100%的数据,2≤m≤n≤50,000,0<w <10^9。

解题报告:

  先扯一点题外话吧。现在是暑假集训(shang ke)的倒数第二天,明天就要回家了(呼...),16天的集训,也测试了9次,9次(0.0)好像明天还有一场测试。(表示好心累)突然想起hzwer博客的那句话“自己选的路,跪着也要走完”,现在正是需要这种精神。这个暑假,好好奋斗吧。

  好的,废话就少说了。但其实今天的任务还没有做完,最后一题的长程序还没有编完,主要是今天有点浮躁,静不下心(毕竟明天就回家了),但是一定要编,学习一下上一届学长的刻苦。啊,又说了一大堆废话。-.-

  好的,开始了。

  首先第一题,同余方程。前两天才讲的数论,今天就考到了。一个扩展欧几里得,解方程的问题,用ax+by=gcd(a,b)解决。当时在想,负数会不会有特殊性?就先按最初讲的函数写了一遍,当b==0,x=1,y=0;再递归带回,却发现答案不对啊,然后陷入沉思。在草稿纸上模拟,又调试了好久,尝试着把b==0时,x=-1,y=1;递归带回,惊喜地发现样例过了。不放心,又自己出了一个数据,7 9,一试,错了。伤心。只好再调试,手动模拟过程,然后发现它的初值x=1,y=0才对。然后就懵了。突然灵光一闪,把最后的aa 分情况,样例中aa是小于0的,而这组数据aa是大于0的,所以用if判断一下。又自己出了几组数据,惊喜的发现全过了。评测也全过了。神奇的方法。

代码如下:

 1 #include<iostream>
 2 #include<cstdio>
 3 using namespace std;
 4 long long a,b;
 5 long long x,y;
 6 void gcd(long long aa,long long bb,long long &x,long long &y)
 7 {
 8     if (bb==0)
 9     {
10         if (aa>0)
11         {
12             x=1;y=0;return ;
13         }
14         else
15         {
16             x=-1;y=1;return ;//!!!x=-1,y=1!!!
17         }
18     }
19     else
20     {
21         gcd(bb,aa%bb,x,y);
22         long long t=x;
23         x=y;
24         y=t-aa/bb*y;
25     }
26 }
27 int main()
28 {
29     freopen("mod.in","r",stdin);
30     freopen("mod.out","w",stdout);
31     cin>>a>>b;
32     gcd(a,-b,x,y);
33     cout<<x;
34     return 0;
35 }

  其实正解不是我这样的,虽然我还没有仔细的研究这样做的理论证明,而且正解还对x mod b 我表示很惊讶,我没有这么做也是最小值?好好研究一下。

  另附正解(感谢hzwer的代码)

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cstdlib>
 5 #include<cmath>
 6 #include<algorithm>
 7 #define mod 1000000007
 8 #define ll long long
 9 #define inf (1LL<<60)
10 using namespace std;
11 ll read()
12 {
13     ll x=0,f=1;char ch=getchar();
14     while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
15     while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
16     return x*f;
17 }
18 ll ans;
19 int a,b;
20 void exgcd(int a,int b,int &x,int &y)
21 {
22     if(b==0){x=1;y=0;return;}
23     exgcd(b,a%b,x,y);
24     int t=x;x=y,y=t-a/b*y;
25 }
26 int main()
27 {
28     a=read();b=read();
29     int x,y;
30     exgcd(a,b,x,y);
31     x=(x%b+b)%b;
32     cout<<x<<endl;
33     return 0;
34 }

  第二题,借教室。第一遍浏览所有题的时候,一看这道题,立马想起前不久学的线段树,然后放心的去编第一题了。可是,现实是残酷的,我连线段树都编错了。还写了三篇博客来着。求心理阴影。更可笑的是,我在每个节点存的居然是和,不应该是最小值吗。Oh,no!好吧,被线段树伤透了心的我只好去网上学习了一个神奇的程序,虽然也用到了正解中的前缀和,但是判断过程和正解不一样,没有用二分,而是依次查找,感觉会很慢,但在cena上全过了,最慢也就0.7s,可是vijos可没给我面子,只得了35‘。

代码如下:

 1 #include<iostream>
 2 #include<cstdio>
 3 #define LL long long
 4 using namespace std;
 5 const int maxn=1000005;
 6 int n,m;
 7 LL ri[maxn],p[maxn],sum[maxn];
 8 int pos=123456789,ma;
 9 struct pp{
10     int d,s,t;
11 };
12 pp a[maxn];
13 int main()
14 {
15     freopen("classroom.in","r",stdin);
16     freopen("classroom.out","w",stdout);
17     cin>>n>>m;
18     for (int i=1;i<=n;i++)
19       scanf("%d",&ri[i]);
20     for (int i=1;i<=m;i++)
21     {
22         scanf("%d%d%d",&a[i].d,&a[i].s,&a[i].t);
23         if (a[i].t>ma) ma=a[i].t;
24         p[a[i].s]+=a[i].d;p[a[i].t]+=a[i].d;
25         if ((p[a[i].s]>ri[a[i].s]||p[a[i].t]>ri[a[i].t])&&pos>i)
26           pos=i;
27         sum[a[i].s]+=a[i].d;
28         sum[a[i].t+1]-=a[i].d;
29     }
30     for (int i=1;i<=n;i++)
31       sum[i]+=sum[i-1];
32     for (int i=1;i<=n;i++)
33         if (sum[i]>ri[i])
34         {
35             int tt=0;
36             for (int j=1;j<=m;j++)
37             {
38                 if (j>pos) break;
39                 if (a[j].s<=i&&a[j].t>=i)
40                   tt+=a[j].d;
41                 if (tt>ri[i])
42                 {
43                     pos=j;
44                     break;
45                 }
46             }
47         }
48     if (pos==123456789)
49       printf("0");
50     else
51     {
52         cout<<-1<<endl;
53         cout<<pos;
54     }
55     return 0;
56 }

  二分+前缀和、线段树+二分+前缀和 都是对的,这里给出前者的代码:

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<iostream>
 4 #include<string>
 5 #include<iostream>
 6 #include<vector>
 7 #include<cstdlib>
 8 #include<queue>
 9 #include<stack>
10 #include<vector>
11 #include<cmath>
12 #ifdef WIN32
13 #define AUTO "%I64d"
14 #else
15 #define AUTO "%lld"
16 #endif
17 using namespace std;
18 const int maxn=1000000+5;
19 int n,m,r[maxn],d[maxn],s[maxn],t[maxn],a[maxn],sum[maxn];
20 bool flag=true;
21 bool judge(int k)
22 {
23     memset(a,0,sizeof(a));
24     memset(sum,0,sizeof(sum));
25     for (int i=1;i<=k;i++)
26     {
27         a[s[i]]+=d[i];
28         a[t[i]+1]-=d[i];
29     }
30     for (int i=1;i<=n;i++)
31     {
32         sum[i]=sum[i-1]+a[i];
33         if (sum[i]>r[i])
34           return false;
35     }
36     return true;
37 }
38 int main()
39 {
40     freopen("classroom.in","r",stdin);
41     freopen("classroom.out","w",stdout);
42     scanf("%d%d",&n,&m);
43     for (int i=1;i<=n;i++) scanf("%d",&r[i]);
44     for (int i=1;i<=m;i++) scanf("%d%d%d",&d[i],&s[i],&t[i]);
45     int L=1,R=m;
46     while (L+1<R)
47     {
48         int mid=(L+R)>>1;
49         if (judge(mid)) L=mid;
50         else R=mid;
51     }
52     bool judge_L=judge(L),judge_R=judge(R);
53     if (!judge_L) printf("-1\n%d",L);
54     else if (!judge_R) printf("-1\n%d",R);
55     else printf("0");
56     return 0;
57 }

  第三题,疫情控制。做到这道题的时候,已经没有什么时间了,毕竟刚才的线段树,哎,就不说了。这道题是二分+贪心+倍增(好像不用?),思路嘛,人太懒,就不想打字了,上面都打了这么多了。参考网址:http://blog.csdn.net/u011542204/article/details/47857687  还有http://blog.csdn.net/doyouseeman/article/details/51009073,自我认为解释得还不错。只是今天真的不想去看这种长长的程序了。

  今天还讲了倍增,是同学讲的,讲得不错,帮k啦啦啦。有时间自己再写一篇讲解倍增的博客。发一下同学anantheparty的倍增:http://blog.csdn.net/u011327397/article/details/51894479。

  好啦,今天就先这样吧。(写了这么久的博客。休息一下。)

时间: 2024-09-28 21:16:02

【未完成0.0】Noip2012提高组day2 解题报告的相关文章

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

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

[部分题解]noip2012提高组day2

这道题有多种解法,我用的是扩展欧几里得算法求到的答案 1 #include<iostream> 2 #include<fstream> 3 #include<cstdio> 4 using namespace std; 5 typedef long long ll; 6 ifstream fin("mod.in"); 7 FILE *fout = fopen("mod.out","w"); 8 void gcd

刷题总结——疫情控制(NOIP2012提高组)

题目: 题目背景 NOIP2012 提高组 DAY2 试题. 题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都,也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点.但特别要注意的是,首都是不能建立检查点的. 现在,在 H 国的一些城市中已经驻扎有军队,且

第五届蓝桥杯软件大赛C/C++本科B组决赛解题报告

mnesia在频繁操作数据的过程可能会报错:** WARNING ** Mnesia is overloaded: {dump_log, write_threshold},可以看出,mnesia应该是过载了.这个警告在mnesia dump操作会发生这个问题,表类型为disc_only_copies .disc_copies都可能会发生. 如何重现这个问题,例子的场景是多个进程同时在不断地mnesia:dirty_write/2 mnesia过载分析 1.抛出警告是在mnesia 增加dump

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/

常州培训 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)

[NOIP2012] 提高组 洛谷P1081 开车旅行

题目描述 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的 城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 Hi,城市 i 和城市 j 之间的距离 d[i,j]恰好是这两个城市海拔高度之差的绝对值,即 d[i,j] = |Hi− Hj|. 旅行过程中,小 A 和小 B 轮流开车,第一天小 A 开车,之后每天轮换一次.他们计划 选择一个城市 S 作为起点,一直向东行驶,并且最多行驶 X 公里就结束旅行.小 A 和小 B

洛谷P1080 [NOIP2012提高组D1T2]国王游戏 [2017年5月计划 清北学堂51精英班Day1]

P1080 国王游戏 题目描述 恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏.首先,他让每个大臣在左.右 手上面分别写下一个整数,国王自己也在左.右手上各写一个整数.然后,让这 n 位大臣排 成一排,国王站在队伍的最前面.排好队后,所有的大臣都会获得国王奖赏的若干金币,每 位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右 手上的数,然后向下取整得到的结果. 国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序, 使得获得奖赏最多的大

[NOIP2012] 提高组 洛谷P1084 疫情控制

题目描述 H 国有 n 个城市,这 n 个城市用 n-1 条双向道路相互连通构成一棵树,1 号城市是首都, 也是树中的根节点. H 国的首都爆发了一种危害性极高的传染病.当局为了控制疫情,不让疫情扩散到边境 城市(叶子节点所表示的城市),决定动用军队在一些城市建立检查点,使得从首都到边境 城市的每一条路径上都至少有一个检查点,边境城市也可以建立检查点.但特别要注意的是, 首都是不能建立检查点的. 现在,在 H 国的一些城市中已经驻扎有军队,且一个城市可以驻扎多个军队.一支军队可以在有道路连接的城