NOIP 2015模拟赛[八中题]题解&总结

这套题应该是发挥的最不错的一次了,该做的题都做了,都A了,虽然自己的代码能力还是有所不足,但是比较起前几个月已经有了不小的提升,希望自己继续保持!


【2015多校联训5】病毒分裂

Time Limit:10000MS  Memory Limit:165536K
Total Submit:33 Accepted:18
Case Time Limit:1000MS

Description

A 学校的实验室新研制出了一种十分厉害的病毒。由于这种病毒太难以人工制造了,所以专家们在一开始只做出了一个这样的病毒。
这个病毒被植入了特殊的微型芯片,使其可以具有一些可编程的特殊性能。最重要的一个性能就是,专家们可以自行设定病毒的分裂能力K,假如现在有x 个病毒,下一个分裂周期将会有Kx 个一模一样的病毒。你作为该实验室的数据分析员,需要统计出在分裂到第N 个周期前,一共有多少个病毒单体进行了分裂。一开始时总是只有一个病毒,这个局面算作第一个周期。由于答案可能很大,专家们只需要你告诉他们对给定的P 取模后的答案。

Input

一行三个整数,依次是K, N, P。

Output

一行一个整数,你的答案(对P 取模) 。

Sample Input

【样例输入1】
5 3 7

【样例输入2】
2 6 23

Sample Output

【样例输出1】
6

【样例输出2】
8

Hint

对于100%的数据,1<N<1018,1<K,P<231

Source

by BZ


这题应该是比较容易想到的,如果P保证为质数,只要求出了逆元再加上了一个等比求和公式就很容易过70分了

但是注意到并没有保证p为质数,因此求逆元就不怎么可行了,因此我们考虑尽量避开除法

我们先忽略a^0,假如让我们求a^1+a^2+.....+a^k,我们应该先算S=a^1+a^2+...+a^(k/2),这样后面的式子就等于

a^(k/2)*S,用递归解决就可以了

最后注意一下幂的奇偶

#include<cstdio>
#include<iostream>
#define LL long long
using namespace std;
LL k,n,p;
LL MG(LL a,LL b){
	LL ans=1;
	a%=p;
	while(b){
		if(b&1)ans=ans*a%p;
		b>>=1;
		a=a*a%p;
	}
	return ans;
}
LL solve(LL a){
	if(a==1)return k%p;
	LL temp=solve(a/2)%p;
	if(a%2==0)return (MG(k,a/2)*temp%p+temp)%p;
	return ((MG(k,a/2)*temp%p+MG(k,a))%p+temp)%p;
}
int main(){
	cin>>k>>n>>p;
	if(n==1){puts("1");return 0;}
	cout<<(solve(n-2)+1)%p;
}

【2015多校联训5】疫情延迟

Time Limit:10000MS  Memory Limit:165536K
Total Submit:16 Accepted:6
Case Time Limit:1000MS

Description

由于A 学校生物实验室里那个不负责的数据分析员,实验室的病毒威力被错误估算,导致了可怕的病毒泄漏,现在病毒即将在校园内传播开来。
校园里一共有n 个建筑物,生物实验室总是位于一号建筑物且在0 时刻受到病毒入侵。这n 个建筑物由m 条单向道路相连(也有可能有建筑物被孤立)。每条道路有两个信息:它的长度,它是多少年前修建的。当一个建筑物被病毒入侵,从被入侵的时刻起,病毒会从所有由这个建筑物通向其他建筑物的道路按着这条道路的方向以1 个单位每秒的速度行进。
校长得知这个事情后,决定放弃这个学校逃跑。校长总是位于编号为n 的行政楼,从零时刻开始需要共T 秒来逃出行政楼,且逃出行政楼即视为逃出了这个学校。也就是说,如果病毒入侵行政楼的时间不小于T,则校长能够成功逃离。
有些时候,校长没有足够的时间逃离,因为病毒到达行政楼的时间太快了。
为了让校长能够逃离学校,不得不拆除校园内的一些道路以延缓行政楼的被入侵时间(拆除道路视为在0 时刻被拆的道路全部消失)。当然,如果使得病毒根本无法到达行政楼,也是可以的。
但是,拆除道路会影响学校的历史气息,且破坏程度定义为拆除的道路中最古老的一条的年龄。请求出保证校长能够安全撤离的情况下,最小的破坏程度。

Input

第一行包含三个整数:n, m, T。
接下来m 行,每行描述一条有向道路。每行4 个整数,si, ti, Li, Yi,分别表
示这条道路的起点,终点,长度,这条道路的年龄。

Output

如果不需要拆除任何道路,输出一行两个数:-1 和行政楼的被感染时刻(当然
这个时刻大于等于T),以空格隔开。
否则输出一行包含一个数:最小的破坏程度。

Sample Input

【样例输入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

Sample Output

【样例输出1】
25

【样例输出2】
-1 11

Hint

【样例解释1】

每条边上的黑字对应这条路的长度,红字对应年龄。校长将在第15 秒逃出学校,
如果不拆除任何道路,行政楼将在第14 秒受到入侵。你可以拆除4 到5 的道路,
使得行政楼免于入侵,这样的破坏程度是50。但是最好的方法是拆掉3 到4 之
间的道路,这样使得行政楼在第19 秒受到入侵,校长就可以逃离了。
【数据范围】
对于20%的数据,n, m<=10
对于60%的数据,n, m<=100.
对于100%的数据,n<=20000, m<=100000.
数据保证在不拆除任何道路的情况下,从1 号楼到n 号楼一定存在路径。

Source

by BZ


发现假如选择了一条边年龄为U,那么其他年龄≤U的边都可以全部拆毁(因为最后的破坏程度总是为U)

所以我们先将边年龄从小到大排序,然后二分边的编号,最后判定一下就行了

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=2e4+5,inf=0x3f3f3f3f;
inline void _read(int &x){ 
    char t=getchar();bool sign=true; 
    while(t<'0'||t>'9') 
    {if(t=='-')sign=false;t=getchar();} 
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0'; 
    if(!sign)x=-x; 
} 
int n,m,t,last[maxn],dis[maxn];
bool vis[maxn];
struct node{
	int a,b,c,res,Next;
	node(int a,int b,int c,int res,int Next):a(a),b(b),c(c),res(res),Next(Next){}
	bool operator<(const node& h)const{
		return res<h.res;
	}
};
vector<node>s,v;
void insert(int a,int b,int c,int res){
	s.push_back(node(a,b,c,res,last[a]));
	last[a]=s.size()-1;
}
void spfa(int op,int lim){
	queue<int>q;
	memset(vis,0,sizeof(vis));
	memset(dis,inf,sizeof(dis));
	int i,v,x;
	vis[op]=1,q.push(op);
	dis[op]=0;
	while(q.size()){
		x=q.front();q.pop();
		vis[x]=0;
		for(i=last[x];i>=0;i=s[i].Next){
			v=s[i].b;
			int temp=s[i].c;
			if(s[i].res<=lim)temp=inf;
			if(dis[v]>dis[x]+temp){
				dis[v]=dis[x]+temp;
				if(!vis[v]){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
}
bool ok(int k){
	spfa(1,s[k].res);
	if(dis[n]<t)return 0;
	return 1;
}
int main(){
	memset(last,-1,sizeof(last));
	_read(n);_read(m);_read(t);
	int i,j,x,y,z,g;
	for(i=1;i<=m;i++){
		_read(x);_read(y);_read(z);_read(g);
		v.push_back(node(x,y,z,g,0));
		insert(x,y,z,g);
	}
	spfa(1,-inf);
	if(dis[n]>=t){
		printf("-1 %d",dis[n]);
		return 0;
	}
	memset(last,-1,sizeof(last));
	s.clear();
	sort(v.begin(),v.end());
	for(i=0;i<v.size();i++)
		insert(v[i].a,v[i].b,v[i].c,v[i].res);
	int l=0,r=v.size()-1;
	while(l<=r){
		int mid=(l+r)>>1;
		if(ok(mid))r=mid-1;
		else l=mid+1;
	}
	cout<<s[l].res;
}

【2015多校联训5】避难向导

Time Limit:20000MS  Memory Limit:165536K
Total Submit:12 Accepted:1
Case Time Limit:1000MS

Description

“特大新闻,特大新闻!全国爆发了一种极其可怕的病毒,已经开始在各个城市中传播开来!全国陷入了巨大的危机!大量居民陷入恐慌,想要逃到其它城市以避难!经调查显示,该病毒来自于C 市的A 学校的一次非法的……”
“哎。”你关上电视,叹了口气。作为A 学校的校长,你一天前为了保命,独自逃离了A 学校,抛弃了全校师生,包括那个曾经帮你计算并拆除道路的工程师。
你良心受到了巨大的谴责,因此决定做出一些补救,回答一些逃难的人提出的询问。
已知该国一共有n 个城市,并且1 号城市是首都。(n-1)条双向的公路连接这些城市,通过这些公路,任意两个城市之间存在且仅存在一条路径。每条公路有一个长度。如果一个城市只与一条公路相连,则称它为边境城市。
该国政府有一个奇怪的规定:每个城市有一个封闭系数di,定义di 为离这个城市最远的边境城市到这个城市的距离。市民们认为,一个城市的安全系数Si 和它的封闭系数有很重要的联系。a,b,c 是该国的幸运数字,所以大家公认一个城市的安全系数Si = (di + a) * b mod c。
市民们一共会提出m 次询问。每个询问包含三个信息,xi,yi 和qi。xi 是询问者所在的城市编号。你要为这个询问者在xi 到yi 的必经之路上找出一个离xi最近的避难城市,并且要求这个避难城市的安全系数大于等于qi。如果存在这样的城市(包含xi 和yi),则输出城市编号,否则输出一行包括一个数-1。

Input

第一行五个数:依次是n, m, a, b, c。
接下来n-1 行描述公路的信息。每行三个数,前两个数代表这条公路连接的两个城市的编号,第三个数表示这条公路的长度。
再接下来m 行,每行描述一个询问,包含三个数xi, yi 和qi。

Output

对于每个询问,输出一行包含一个整数,存在符合要求的城市则输出城市编号,不存在则输出-1。

Sample Input

7 6 5 6 20
1 2 4
2 4 2
2 5 3
1 3 5
3 6 6
6 7 7
7 5 15
3 4 5
5 4 2
4 5 2
6 6 10
3 5 19

Sample Output

6
3
2
4
6
-1

Hint

【样例解释】
从城市1 到城市7 的安全系数依次是:18, 2, 8, 14, 0, 18, 0。

【数据范围】
对于100%数据, 0<xi, yi<=n; a,b,c,qi<=1,000,000;
注意:计算安全系数的中间过程可能需要使用64 位整数。

Source

by BZ


这题讲真是比较恶心,首先你很难想到正解,就算你想到了也不好写

考试的时候根本没想出来,直接写的暴力,还是拿了35分

首先我们需要在O(n)(或者更短)的时间内求出每个点的安全值,这个地方我们可以用到树形dp,也可以用树的直径求

这里讲一讲树的直径来求每个点的安全点,因为dp比较难写,也有点难理解,树的直径虽然时间效率不及dp,但是可读性更高,写起来也更容易些

我们首先求出直径两个端点,然后以这两个端点为根求出其他点到这两个点的距离,其中最大值就是i的安全值,再进行一下和a,b,c有关的计算就可以了

在回答询问的时候,我们要用到两个和倍增有关的函数find1和find2

这两个函数分别是求一个点到深度为upto 的祖先的路径上的离x 最近的和离x 最远的合法点,找不到返回-1,fa和maxq是倍增数组,分别表示祖先和到祖先的路径上的最大点权(不含自身)。
这两个递归函数中,每递归一层d 值会减少,所以递归层数不超过logn 层,函数体内部有一个循环,但容易发现这个循环在递归中只会执行一次,且这个循环复杂度也是logn 的。
综上,成功做到了O(logn)的单次查询,可以轻松通过100 分的数据。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
const LL maxn=3e5+5,inf=1e15;
inline void _read(LL &x){ 
    char t=getchar();bool sign=true; 
    while(t<'0'||t>'9') 
    {if(t=='-')sign=false;t=getchar();} 
    for(x=0;t>='0'&&t<='9';t=getchar())x=x*10+t-'0'; 
    if(!sign)x=-x; 
} 
LL n,m,last[maxn*3],dis[maxn],d[maxn],a,b,c,root,fa[maxn][20];
LL v[maxn],depth[maxn],maxq[maxn][20],z;
bool vis[maxn];
struct node{
    LL a,b,c,Next;
    node(LL a,LL b,LL c,LL Next):a(a),b(b),c(c),Next(Next){}
};
vector<node>s;
void insert(LL a,LL b,LL c){
    s.push_back(node(a,b,c,last[a]));
    last[a]=s.size()-1;
    s.push_back(node(b,a,c,last[b]));
    last[b]=s.size()-1;
}
void dfs1(LL u,LL f){
    LL i,v;
    vis[u]=1;
    depth[u]=depth[f]+1;
    LL k=ceil(log(depth[u])/log(2));
    for(i=1;i<=k;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
    for(i=last[u];i>=0;i=s[i].Next){
        v=s[i].b;
        if(vis[v])continue;
        dis[v]=dis[u]+s[i].c;
        fa[v][0]=u;
        dfs1(v,u);
    }
}
LL LCA(LL x,LL y){
    if(depth[x]<depth[y])swap(x,y);
    LL i,k=depth[x]-depth[y],S=ceil(log(n)/log(2));
    for(i=0;i<=S;i++)
        if(k&(1<<i))x=fa[x][i];
    if(x==y)return x;
    S=ceil(log(depth[x])/log(2));
    for(i=S;i>=0;i--)
        if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
LL findans1(LL x,LL k,LL F){
    if(depth[x]<=depth[F])return -1;
    for(;depth[fa[x][k]]<depth[F]&&k>=0;k--);
    if(maxq[x][k]<z)return findans1(fa[x][k],k-1,F);
    for(LL i=k-1;i>=0;i--)
        if(maxq[x][i]<z)x=fa[x][i];
    return fa[x][0];
}
LL findans2(LL y,LL k,LL F){
	LL i,res;
    if(depth[y]<=depth[F])return -1;
    for(;depth[fa[y][k]]<depth[F]&&k>=0;k--);
	res=findans2(fa[y][k],k-1,F);
	if(~res)return res;
	if(maxq[y][k]<z)return -1;
	for(i=k-1;i>=0;i--)
		if(maxq[fa[y][i]][i]>=z)y=fa[y][i];
	return fa[y][0];
}
int main_main(){
    memset(last,-1,sizeof(last));
    _read(n);_read(m);_read(a);_read(b);_read(c);
    LL i,j,x,y,D1=1,D2,temx=-inf,k;
    for(i=1;i<n;i++){
        _read(x);_read(y);_read(z);
        insert(x,y,z);
        d[x]++,d[y]++;
    }
    for(i=1;i<=n;i++)
        if(d[i]!=1){root=i;break;}
    dfs1(root,0);
    for(i=2;i<=n;i++)
        if(dis[i]>dis[D1])D1=i;
    for(i=1;i<=n;i++){
        LL F=LCA(D1,i);
        if(dis[i]+dis[D1]-2*dis[F]>temx)temx=dis[i]+dis[D1]-2*dis[F],D2=i;
    }
    for(i=1;i<=n;i++){
        LL F1=LCA(D1,i),F2=LCA(D2,i);
        v[i]=max(dis[i]+dis[D1]-2*dis[F1],dis[i]+dis[D2]-2*dis[F2]);
        v[i]=(v[i]+a)*b%c;
    }
    k=ceil(log(n)/log(2));
    for(i=1;i<=n;i++){
    	for(j=1;j<=k;j++)fa[i][j]=0;
    	maxq[i][0]=v[fa[i][0]];
	}
    for(j=1;j<=k;j++)
    	for(i=1;i<=n;i++){
    		fa[i][j]=fa[fa[i][j-1]][j-1];
    		maxq[i][j]=max(maxq[i][j-1],maxq[fa[i][j-1]][j-1]);
		}
    for(i=1;i<=m;i++){
        _read(x);_read(y);_read(z);
        LL F=LCA(x,y),ans=-1;
        if(v[x]>=z)ans=x;
        if(ans==-1){
            k=ceil(log(depth[x])/log(2));
            ans=findans1(x,k,F);
        }
        if(ans==-1){
            k=ceil(log(depth[y])/log(2));
            ans=findans2(y,k,F);
        }
        if(ans==-1&&v[y]>=z)ans=y;
        cout<<ans<<endl;
    }
} 

最后说两句:

以后应该和今天一样,能拿到手的分都拿到手,不要小看5分,5分也是分!




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值