8.31 NOIP 模拟赛
8.31 NOIP 模拟赛
第一题:病毒分裂(split.c/cpp/pas)
【题目描述】A 学校的实验室新研制出了一种十分厉害的病毒。由于这种病毒太难以人工制造了,所以专家们在一开始只做出了一个这样的病毒。这个病毒被植入了特殊的微型芯片,使其可以具有一些可编程的特殊性能。最重要的一个性能就是,专家们可以自行设定病毒的分裂能力 K,假如现在有 x 个病毒,下一个分裂周期将会有 Kx 个一模一样的病毒。你作为该实验室的数据分析员,需要统计出在分裂到第 N 个周期前,一共有多少个病毒单体进行了分裂。一开始时总是只有一个病毒,这个局面算作第一个周期。由于答案可能很大,专家们只需要你告诉他们对给定的 P 取模后的答案。
【输入】一行三个整数,依次是 K, N, P。
【输出】一行一个整数,你的答案(对 P 取模) 。
样例输入
【样例输入1】
5 3 7
【样例输入2】
2 6 23
样例输出
【样例输出1】
6
【样例输出2】
8
样例一解释:第一个周期有 1 个病毒,产生了一次分裂。第二个周期有 1*5=5个病毒,这五个病毒都会分裂。所以第三个周期前一共进行了 1+5 等于 6 次分裂。答案即为 6 mod 7 = 6
提示
对于100%的数据,1<N<1018,1<K,P<231。
题意:给出 K,N,P,求首项为 1,公比为 K 的等比数列前(N-1)项和模 P。
10 分做法:模不就是取余数吗?万一等比数列之和刚好是 P 的倍数呢?输出 0
碰碰运气好辣 O(∩_∩)O (模仿 CCF 的超水数据)。。。
50 分算法:迭代枚举项数,依次求出每一项模 P 的值,然后相加模 P 输出即可。
70 分算法:直接用等比数列求和公式,配合快速幂计算。但是这个公式中有除
法运算,需要求 P 的数论逆元(这就是前 70 分 P 是质数的原因)。
#include<stdio.h> #include<bits/stdc++.h> using namespace std; long long n,a,p; long long pow1(long long y,long long x) { long long ans=1; while(x) { if(x%2==1) ans=ans*y%p; x=x/2; y=y*y%p; } return ans; } long long getsum(long long a,long long n) { if(n==1) return a; long long s=getsum(a,n/2); if(n%2==1) { long long t=pow1(a,n/2+1); s=(s+s*t%p)%p; s=(s+t)%p; } else { long long t=pow1(a,n/2); s=(s+s*t%p)%p; } return s; } int main() { scanf("%lld%lld%lld",&a,&n,&p); if(n==2) { cout<<1; return 0; } n=n-2; printf("%lld\n",(getsum(a,n)+1)%p); return 0; }
第二题:疫情延迟(delay.c/cpp/pas)
【题目描述】
由于 A 学校生物实验室里那个不负责的数据分析员,实验室的病毒威力被错误估算,导致了可怕的病毒泄漏,现在病毒即将在校园内传播开来。校园里一共有 n 个建筑物,生物实验室总是位于一号建筑物且在 0 时刻受到病毒入侵。这 n 个建筑物由 m 条单向道路相连(也有可能有建筑物被孤立)。每条道路有两个信息:它的长度,它是多少年前修建的。当一个建筑物被病毒入侵,从被入侵的时刻起,病毒会从所有由这个建筑物通向其他建筑物的道路按着这条道路的方向以 1 个单位每秒的速度行进。校长得知这个事情后,决定放弃这个学校逃跑。校长总是位于编号为 n 的行政楼,从零时刻开始需要共 T 秒来逃出行政楼,且逃出行政楼即视为逃出了这个学校。也就是说,如果病毒入侵行政楼的时间不小于 T,则校长能够成功逃离。有些时候,校长没有足够的时间逃离,因为病毒到达行政楼的时间太快了。为了让校长能够逃离学校,不得不拆除校园内的一些道路以延缓行政楼的被入侵时间(拆除道路视为在 0 时刻被拆的道路全部消失)。当然,如果使得病毒根本无法到达行政楼,也是可以的。但是,拆除道路会影响学校的历史气息,且破坏程度定义为拆除的道路中最古老的一条的年龄。请求出保证校长能够安全撤离的情况下,最小的破坏程度。
【输入】第一行包含三个整数:n, m, T。接下来 m 行,每行描述一条有向道路。每行 4 个整数,si, ti, Li, Yi,分别表示这条道路的起点,终点,长度,这条道路的年龄。
【输出】如果不需要拆除任何道路,输出一行两个数:-1 和行政楼的被感染时刻(当然这个时刻大于等于 T),以空格隔开。否则输出一行包含一个数:最小的破坏程
样例输入
【样例输入1】
5 5 15
1 2 6 35
2 4 8 40
1 3 6 45
3 4 3 25
4 5 5 50
【样例输入2】
3 2 10
1 2 5 30
2 3 6 50
样例输出
【样例输出1】
25
【样例输出2】
-1 11
提示
【样例解释1】
每条边上的黑字对应这条路的长度,红字对应年龄。校长将在第15 秒逃出学校,
如果不拆除任何道路,行政楼将在第14 秒受到入侵。你可以拆除4 到5 的道路,
使得行政楼免于入侵,这样的破坏程度是50。但是最好的方法是拆掉3 到4 之
间的道路,这样使得行政楼在第19 秒受到入侵,校长就可以逃离了。
【数据范围】
对于20%的数据,n, m<=10
对于60%的数据,n, m<=100.
对于100%的数据,n<=20000, m<=100000.
数据保证在不拆除任何道路的情况下,从1 号楼到n 号楼一定存在路径。
T2 delay (图论相关)
题意:给出一张有向图,每条边有长度和年龄,删掉一些边使得起点 1 到终点 n
的最短路不低于给定的 T,要在保证这个前提下使得删掉的边中的最大的年龄值
最小,求这个最小值。
60 分算法:将边排序,枚举边的最大值,将边长小于等于这个的枚举值的边全
部删掉,判断起点到终点的最短路是否不低于 T。
100 分算法:只要想到上面这个做法,几乎就可以想到正解了。对于最大值的最
小值,比较直观的方法就是二分答案加判定。
先考虑单调性:枚举的最大值越大,删去的边就越多,保留的边对应地越少。对
于有向图 G,每增加一条边,显然不会让固定两点间的最短路变长;每删去一条
边,显然不会让固定两点间的最短路变短。所以二分答案是可行的
那么,只需要二分这个最大值,在做最短路的时候忽略掉小于等于这个最大值的
边,再检查起点和终点之间的最短路径是否小于 T,如果小了就调大上限,删
更多的边。
考虑时间复杂度,二分为 log(max(Yi)),堆优化的 Dijkstra 求最短路为(n+m)log(n),
综合以上,最终复杂度为 O(m log(n))。当然,用 SPFA 也许可以取得更高的效
率。
#include<stdio.h> #include<bits/stdc++.h> using namespace std; int n,m,t; struct node { int to; int next; int zhi; int age; } edge[100005]; int head[20005],num; int dis[20005],vis[20005]; inline void add(int x,int y,int w,int z) { edge[++num].to=y; edge[num].next=head[x]; edge[num].zhi=w; edge[num].age=z; head[x]=num; } struct Near { int v, dis; Near() {} Near(int a,int b) { v=a; dis=b; } bool operator < (const Near&t) const { return dis > t.dis; } }; int Dijkstra(int lim) { memset(dis, 0x3f, sizeof dis); memset(vis, 0, sizeof vis); priority_queue<Near> Q; dis[1] = 0; Q.push(Near(1, 0)); while (!Q.empty()) { int u = Q.top().v; Q.pop(); if (vis[u]) continue; vis[u] = 1; for (int p = head[u]; p; p=edge[p].next) { if (edge[p].age <= lim) continue; if (dis[u] + edge[p].zhi< dis[edge[p].to]) { dis[edge[p].to] = dis[u] +edge[p].zhi; Q.push(Near(edge[p].to, dis[edge[p].to])); } } } return dis[n]; } int work() { int l = 0, r = 1000000001, mid; while(l+1<r) { mid = (l + r)/2; if ( Dijkstra(mid) < t ) l = mid; else r = mid; } if ( Dijkstra(l) >= t) return l; else return r; } int main() { cin>>n>>m>>t; int si,ti,li,yi; for(int i=1; i<=m; i++) { cin>>si>>ti>>li>>yi; add(si,ti,li,yi); } if(Dijkstra(-1)>=t) { cout<<"-1"<<" "<<dis[n]; } else { cout<<work(); } }