2019.9.8队测

T1:

避难向导
(shelter.cpp 1s 256M)
“特大新闻,特大新闻!全国爆发了一种极其可怕的病毒,已经开始在各个城市 中传播开来!全国陷入了巨大的
危机!大量居民陷入恐慌,想要逃到其它城市以 避难!经调查显示,该病毒来自于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。
0<xi<=n
qi<=1000000
n<=100000
m<=300000
计算安全系数的中间过程可能需要64位整数
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

Solution:

首先我们可以通过直径来求安全系数,因为对于每个点来说离他最远的点一定是直径的端点

我们按照预处理倍增数组的方法预处理出一个mx数组,表示到祖先的路径上安全系数最大的值

每次求出lca(x,y),然后对x到lca求一遍,如果没有,再对lca到y求一遍,最后输出答案就行了

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+1;
int n,m,cnt,a,b,c,V,head[N];
int Mx,st,ed,dis[N],d[N];
int f[N][31],mx[N][31],dep[N];
struct Edge{int nxt,to,val;}edge[N<<1];
void ins(int x,int y,int z){
	edge[++cnt].nxt=head[x];
	edge[cnt].to=y;head[x]=cnt;
	edge[cnt].val=z;
}
void getdis(int x,int fa){
	for(int i=head[x];i;i=edge[i].nxt){
		int y=edge[i].to;
		if(y==fa) continue;
		int tmp=dis[y];
		dis[y]=dis[x]+edge[i].val;
		if(tmp>dis[y]) d[y]=tmp;
		else d[y]=dis[y];
		if(dis[y]>dis[Mx]) Mx=y;
		getdis(y,x);
	}
}
void dfs(int x,int fa){
	mx[x][0]=d[x];dep[x]=dep[fa]+1;
	for(int i=head[x];i;i=edge[i].nxt){
		int y=edge[i].to;
		if(y==fa) continue;
		f[y][0]=x;dfs(y,x);
	}
}
void trans(){
	for(int i=1;i<=21;i++)
		for(int j=1;j<=n;j++){
			f[j][i]=f[f[j][i-1]][i-1];
			mx[j][i]=max(mx[j][i-1],mx[f[j][i-1]][i-1]);
		}
}
int lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	if(x==y||y==1) return y;
	for(int i=21;i>=0;i--)
		if(dep[f[x][i]]>=dep[y]) x=f[x][i];
	if(x==y) return x;
	for(int i=21;i>=0;i--)
		if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
int find1(int x,int fa){
	int ff=f[fa][0];
	for(int i=21;i>=0;i--){
		int u=f[x][i];
		if(dep[u]<dep[ff]) continue;
		if(mx[x][i]<V) x=u;
	}return x==ff?-1:x;
}
int jump(int x,int fa){
	int ff=f[fa][0];
	for(int i=21;i>=0;i--){
		int u=f[x][i];
		if(dep[u]<dep[ff]) continue;
		if(mx[x][i]>=V) return 1;x=u;
	}return 0;
}
int find2(int x,int fa){
	if(!jump(x,fa))return -1;
	for(int i=21;i>=0;i--){
		int u=f[x][i];
		if(jump(u,fa)) x=u;
	}return x;
}
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
signed main(){
	n=read(),m=read();
	a=read(),b=read(),c=read();
	for(int i=1;i<n;i++){
		int x=read(),y=read(),z=read();
		ins(x,y,z),ins(y,x,z);
	}
	getdis(1,0);st=Mx;Mx=0;dis[st]=0;
	getdis(st,0);ed=Mx;Mx=0;dis[ed]=0;
	getdis(ed,0);Mx=0;
	for(int i=1;i<=n;i++) d[i]=(d[i]+a)*1ll*b%c;
	dfs(1,0);trans();
	for(int i=1;i<=m;i++){
		int x=read(),y=read();V=read();
		int fart=lca(x,y),ans=-1;
		if(d[x]>=V) ans=x;
		if(ans==-1)	ans=find1(x,fart);
		if(ans==-1){
			if(d[fart]>=V){ans=fart;goto Out;}
			ans=find2(y,fart);
		}Out:printf("%lld\n",ans);
	}return 0;
}

T2:

动漫排序
(sort.cpp 1s 256M)
小WW最近迷上了日本动漫,每天都有无数部动漫的更新等着他去看,所以他必须将所有的动漫排个顺序,当然,虽然有无数部动漫,但除了1号动漫,每部动漫都有且仅有一部动漫是它的前传(父亲),也就是说,所有的动漫形成一个树形结构。而动漫的顺序必须满足以下两个限制:
①一部动漫的所有后继(子孙)都必须排在它的后面。
②对于同一部动漫的续集(孩子),小W喜爱度高的须排在前面。
光排序小WW还不爽,他想知道一共有多少种排序方案,并且输出它mod10007的答案。
Input
第一行表示T表示数据组数。
接下来每组数据第一行n表示有多少部动漫等待排序,
接下来n行每行第一个数tot表示这部动漫有多少部续集,
接下来tot个数按照小WW喜爱从大到小给出它的续集的编号。
n≤1000。
Output
每组数据一行数ans,表示答案mod10007的结果。
Sample Input
1
5
3 4 3 2
0
1 5
0
0
Sample Output
2

Solution:

我们审视一下给我们的两个限制,发现我们可以重构一下图

我们可以把一个点的儿子按顺序连边,即将\(son_i\)\(son_{i+1}\)连边代替与\(x\)连边,这样子是等价的

然后我们设\(f[x]\)代表在以\(x\)为根的子树内的合法遍历顺序的数量,则\(f[1]\)就是最后的答案

因为重构后的图一定是一颗二叉树,这就很好转移:\(f[x]=f[son_1]\times f[son_2]\times C_{sz[son_1]+sz[son_2]}^{sz[son_1]}\)

其中最后的排列组合表示将第一个数的方案有序插入第二个数的方案数,即插板法

最后一定要记得多mod

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+1;
const int p=1e4+7;
int n,cnt,head[N],sz[N],f[N],fac[N];
struct Edge{int nxt,to;}edge[N<<1];
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
void ins(int x,int y){
	edge[++cnt].nxt=head[x];
	edge[cnt].to=y;head[x]=cnt;
}
int qpow(int a,int b){
	int re=1;
	while(b){
		if(b&1) re=(re*1ll*a)%p;
		b=b>>1;a=(a*1ll*a)%p;
	}return re%p;
}
int C(int x,int y){
	int re=fac[x];
	re=(re*1ll*qpow(fac[y],p-2))%p;
	re=(re*1ll*qpow(fac[x-y],p-2))%p;
	return re;
}
void format(){
	cnt=0;
	memset(head,0,sizeof(head));
}
void dfs(int x,int fa){
	f[x]=1;sz[x]=1;
	int s[2],a[2],flag=0;
	for(int i=head[x];i;i=edge[i].nxt){
		int y=edge[i].to;
		if(y==fa) continue;
		dfs(y,x);sz[x]+=sz[y];
		s[flag++]=sz[y];
		a[flag-1]=f[y];
	}
	if(flag==1) f[x]=a[0];
	if(flag==2) f[x]=(a[0]*1ll*a[1])%p*C(s[0]+s[1],s[0])%p;
}
void solve(){
	format();n=read();
	for(int i=1;i<=n;i++){
		int num=read(),lst=i;
		for(int j=1;j<=num;j++){
			int x=read();
			ins(lst,x);ins(x,lst);
			lst=x;
		}
	}dfs(1,0);
	printf("%lld\n",f[1]);
}
signed main(){
	int t=read();fac[1]=1;
	for(int i=2;i<=1000;i++) fac[i]=(fac[i-1]*1ll*i)%p;
	while(t--) solve();
	return 0;
}

T3

仓库
(warehouse.cpp 1s 256M)
喵星系有n个星球,星球以及星球间的航线形成一棵树。
从星球a到星球b要花费[dis(a,b) Xor M]秒。(dis(a,b)表示ab间的航线长度,Xor为位运算中的异或)
为了给仓库选址,pf想知道,星球i(1<=i<=n)到其它所有星球花费的时间之和。
Input
第一行包含两个正整数n,M。
接下来n-1行,每行3个正整数a,b,c,表示a,b之间的航线长度为c。
N<=100000 M<=15 答案不超过2*10^9
Output
n行,每行一个整数,表示星球i到其它所有星球花费的时间之和。
Sample Input
4 0
1 2 1
1 3 2
1 4 3
Sample Output
6
8
10
12

Solution:

去掉异或操作是非常简单的,考虑加上异或之后怎么做

我们发现m的值最大为15,也就是说他只会影响二进制后4位,我们从这一点下手

\(g[x][j]\)表示以\(x\)为根的子树到\(x\)的距离\(mod \,16\)后为\(j\)的点的个数,\(f[x][j]\)则表示所有点到\(x\)

然后算出对每个点来说其他点到他的距离和,算答案的时候减去每个点多算的值即可

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+1;
int n,m,cnt,sum,head[N],val[N];
int sz[N],f[N][16],g[N][16];
struct Edge{int nxt,to,val;}edge[N<<1];
void ins(int x,int y,int z){
	edge[++cnt].nxt=head[x];
	edge[cnt].to=y;head[x]=cnt;
	edge[cnt].val=z;
}
void dfs1(int x,int fa,int dis){
	sz[x]=1;sum+=dis;g[x][0]=1;
	for(int i=head[x];i;i=edge[i].nxt){
		int y=edge[i].to;
		if(y==fa) continue;
		dfs1(y,x,dis+edge[i].val);
		sz[x]+=sz[y];
		for(int j=0;j<16;j++)
			g[x][(j+edge[i].val)%16]+=g[y][j];
	}
}
void dfs2(int x,int fa,int v){
	val[x]=v;
	for(int j=0;j<16;j++) f[x][j]+=g[x][j];
	for(int i=head[x];i;i=edge[i].nxt){
		int y=edge[i].to;
		if(y==fa) continue;
		for(int j=0;j<16;j++)
			f[y][(j+edge[i].val)%16]+=f[x][j]-g[y][((j-edge[i].val)%16+16)%16];
		dfs2(y,x,v+edge[i].val*1LL*(sz[1]-sz[y]-sz[y]));
	}
}
int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<n;i++){
		int x=read(),y=read(),z=read();
		ins(x,y,z),ins(y,x,z);
	}
	dfs1(1,0,0);
	dfs2(1,0,sum);
	for(int i=1;i<=n;i++){
		int ans=val[i];
		for(int j=0;j<16;j++)
			ans+=f[i][j]*1ll*((j^m)-j);
		printf("%lld\n",ans-m);
	}
	return 0;
}
posted @ 2019-09-09 16:44  DQY_dqy  阅读(196)  评论(0编辑  收藏  举报