AtCoder Beginner Contest 133题解

AtCoder Beginner Contest 133

A.T or T

◇题目传送门◆

题目大意

给定三个数 n , a , b n,a,b n,a,b要求输出 n × a n\times a n×a b b b中较小的一个。

分析

简单题,用if语句判断一下就是了。

参考代码

#include
#include
using namespace std;

int main() {
	#ifdef LOACL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	int n,a,b;
	scanf("%d %d %d",&n,&a,&b);
	if(n*a>b)printf("%d\n",b);
	else printf("%d\n",n*a);
	return 0;
}

B.Good Distance

◇题目传送门◆

题目大意

给定 D D D维空间上的 N N N个点,要求有多少对点 i , j i,j i,j满足 i < j i<j i<j i , j i,j i,j间的距离为整数。

D D D维空间中点 ( y 1 , y 2 , … , y D ) (y_1,y_2,\ldots,y_D) (y1,y2,,yD)与点 ( z 1 , z 2 , … , z D ) (z_1,z_2,\ldots,z_D) (z1,z2,,zD)间距离公式为 ( y 1 − z 1 ) 2 + ( y 2 − z 2 ) 2 + ⋯ + ( y D − z D ) 2 \sqrt{(y_1-z_1)^2+(y_2-z_2)^2+\dots+(y_D-z_D)^2} (y1z1)2+(y2z2)2++(yDzD)2

分析

由于题目中数据很小,暴力枚举判断即可。

参考代码

#include
#include
#include
using namespace std;

int N,D;
int X[15][15];

bool Check(int i,int j) {
	int x=0;
	for(int k=1;k<=D;k++)
		x+=((X[i][k]-X[j][k])*(X[i][k]-X[j][k]));
	int tmp=sqrt(x);
	return tmp*tmp==x;
}

int main() {
	#ifdef LOACL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	scanf("%d %d",&N,&D);
	for(int i=1;i<=N;i++) {
		for(int j=1;j<=D;j++)
			scanf("%d",&X[i][j]);
	}
	int ans=0;
	for(int i=1;i<=N;i++)
		for(int j=i+1;j<=N;j++)
			if(Check(i,j))ans++;
	printf("%d\n",ans);
	return 0;
}

C.Remainder Minimization 2019

◇题目传送门◆

题目大意

给定区间 [ L , R ] [L,R] [L,R],要求选出 i , j i,j i,j使得 L ≤ i < j ≤ R L\le i<j\le R Li<jR ( i × j ) m o d    2019 (i\times j)\mod 2019 (i×j)mod2019最小。

分析

我们容易发现当 [ L , R ] [L,R] [L,R]中有 2019 2019 2019的倍数时,答案一定为 0 0 0

那么当 [ L , R ] [L,R] [L,R]中没有 2019 2019 2019的倍数时,我们就可以考虑暴力枚举 i , j i,j i,j。显然我们可以证明此时 R − L + 1 ≤ 2019 R-L+1\le 2019 RL+12019,所以总时间复杂度为 O ( 2019 2 ) O({2019}^2) O(20192),不会超时。

参考代码

#include
#include
#include
using namespace std;

map<long long,int> m;

int main() {
	#ifdef LOACL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	long long l,r;
	scanf("%lld %lld",&l,&r);
	if(r/2019-(l-1)/2019>0) {
		puts("0");
		return 0;
	}
	long long ans=0x3f3f3f3f;
	for(long long i=l;i<=r;i++)
		for(long long j=i+1;j<=r;j++)
			ans=min(ans,i*j%2019);
	printf("%lld\n",ans);
	return 0;
}

D.Rain Flows into Dams

◇题目传送门◆

题目大意

N N N座山围成一圈( N N N为奇数),从 1 1 1 N N N标号。约定 0 0 0 N N N N + 1 N+1 N+1 1 1 1

i i i和第 i + 1 i+1 i+1座山之间有一座水库,标号为 i i i

当第 i i i座山下 2 x 2x 2x个单位雨时,第 i i i和第 i − 1 i-1 i1座水库就能收到 x x x单位水。

给定每个水库收到的水量 A i A_i Ai,求每座山的雨量。

分析

我们记 T i T_i Ti为第 i i i座山的雨量。

则我们可列出方程: { T 1 + T 2 2 = A 1 T 2 + T 3 2 = A 2 T 3 + T 4 2 = A 3    …        … T N − 1 + T N 2 = A N − 1 T N + T 1 2 = A N \begin{cases}\frac{T_1+T_2}{2}=A_1\\\frac{T_2+T_3}{2}=A_2\\\frac{T_3+T_4}{2}=A_3\\\ \ \dots\ \ \ \ \ \ \dots\\\frac{T_{N-1}+T_N}{2}=A_{N-1}\\\frac{T_N+T_1}{2}=A_N\end{cases} 2T1+T2=A12T2+T3=A22T3+T4=A3        2TN1+TN=AN12TN+T1=AN

整理一下: { T 1 + T 2 = 2 A 1 ( 1 ) T 2 + T 3 = 2 A 2 ( 2 ) T 3 + T 4 = 2 A 3 ( 3 )    …        … T N − 1 + T N = 2 A N − 1 ( N − 1 ) T N + T 1 = 2 A N ( N ) \begin{cases}T_1+T_2=2A_1&(1)\\T_2+T_3=2A_2&(2)\\T_3+T_4=2A_3&(3)\\\ \ \dots\ \ \ \ \ \ \dots\\T_{N-1}+T_N=2A_{N-1}&(N-1)\\T_N+T_1=2A_N&(N)\end{cases} T1+T2=2A1T2+T3=2A2T3+T4=2A3        TN1+TN=2AN1TN+T1=2AN(1)(2)(3)(N1)(N)

( 1 ) + ( 2 ) + ( 3 ) + ⋯ + ( N ) 2 \frac{(1)+(2)+(3)+\dots+(N)}{2} 2(1)+(2)+(3)++(N)得: ∑ i = 1 N T i = ∑ i = 1 N A i \sum_{i=1}^{N}T_i=\sum_{i=1}^{N}A_i i=1NTi=i=1NAi

又因为 N N N是一个奇数,所以我们可以发现,将奇数号方程左边相加,我们发现 T 2 T_2 T2 T N T_N TN都出现了一次,仅有 T 1 T_1 T1出现了一次,所以:

( 1 ) + ( 3 ) + ( 5 ) + ⋯ + ( N ) (1)+(3)+(5)+\dots+(N) (1)+(3)+(5)++(N)得: T 1 + ∑ i = 1 N T i = ∑ k = 1 N − 1 2 A 2 k − 1 T_1+\sum_{i=1}^{N}T_i=\sum_{k=1}^{\frac{N-1}{2}}A_{2k-1} T1+i=1NTi=k=12N1A2k1

两式相减即可解得 T 1 T_1 T1,再顺次带入各个方程即可解得所有解。

参考代码

#include
#include
using namespace std;

const int Maxn=1e5;

int N,A[Maxn+5];

int ans[Maxn+5];

int main() {
	#ifdef LOACL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	scanf("%d",&N);
	for(int i=1;i<=N;i++)
		scanf("%d",&A[i]);
	int sum=0,tmp=0;
	for(int i=1;i<=N;i++) {
		if(i&1)tmp+=2*A[i];
		sum+=A[i];
	}
	ans[1]=tmp-sum;
	for(int i=2;i<=N;i++)
		ans[i]=2*A[i-1]-ans[i-1];
	for(int i=1;i<=N;i++)
		printf("%d ",ans[i]);
	return 0;
}

E.Virus Tree 2

◇题目传送门◆

题目大意

给定一棵有 N N N个节点的树和 K K K种颜色,要求给每个点涂上颜色,使得距离小于等于 2 2 2的两个节点不同色,问方案数模 1 0 9 + 7 10^9+7 109+7

分析

画一下图就可以发现,只要点的深度大于 2 2 2且当前节点有 n n n个兄弟节点,那么该层可选的颜色数为 ∏ i = 0 n − 1 K − 2 − i \prod_{i=0}^{n-1} K-2-i i=0n1K2i,当一个点深度小于 2 2 2且其兄弟节点个数为 n n n则该点的可选颜色种数为 ∏ i = 0 n − 1 K − i − 当 前 节 点 深 度 \prod_{i=0}^{n-1}K-i-当前节点深度 i=0n1Ki

由乘法原理,将所有点的可选颜色数乘起来即可。

参考代码

#include
#include
#include
using namespace std;

typedef long long ll;
const int Maxn=1e5;
const ll Mod=1000000007;

int N,K;
vector<int> G[Maxn+5];
void addedge(int u,int v) {
	G[u].push_back(v);
	G[v].push_back(u);
}

long long ans;

long long DFS(int u,int fa,int dep) {
	int tmp=K-min(dep,2);
	for(int i=0;i<(int)G[u].size();i++) {
		int v=G[u][i];
		if(v==fa)continue;
		if(DFS(v,u,dep+1)==0)
			return 0;
		ans=ans*tmp%Mod;
		tmp--;
	}
	return ans;
}

int main() {
	#ifdef LOACL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	scanf("%d %d",&N,&K);
	for(int i=1;i<N;i++) {
		int u,v;
		scanf("%d %d",&u,&v);
		addedge(u,v);
	}
	ans=K;
	printf("%lld\n",DFS(1,-1,1));
	return 0;
}

F.Colorful Tree

◇题目传送门◆

前言

ABC怎么会出现可持久化线段树+LCA???

第一次ABC做到这么难的题。。。

不是ABC一般都出的贪心,简单DP之类的东西吗???(逃

所以以后还是要认真对待ABC了。

题目大意

给定一棵有 N N N个节点的树,每条边都有一个颜色和长度。现给出 Q Q Q个查询,每个查询先将所有颜色值为 c c c的边的长度改为 d d d,再查询节点 u u u到节点 v v v的最短距离。

每次查询独立,互不影响。

分析

由于直接查询复杂度为 O ( N Q ) O(NQ) O(NQ),在题目中的条件下这个复杂度显然会爆,所以我们不能暴力。

我们记根节点 1 1 1到当前节点 u u u的距离为 d ( u ) d(u) d(u)。计算所有 d ( u ) d(u) d(u)的复杂度为 O ( N ) O(N) O(N)

考虑LCA,利用LCA,原始路径长度转化为 d ( u ) + d ( v ) − 2 × d ( L C A ( u , v ) ) d(u)+d(v)-2\times d(LCA(u,v)) d(u)+d(v)2×d(LCA(u,v))。我们使用倍增LCA可以使得到这个结果的复杂度降至 O ( log ⁡ 2 N ) O(\log_2 N) O(log2N)

那么我们如何记录每条路径上某种颜色的边数和该颜色的边的长度和呢?

没错,我们就要用到可持久化数组了。但为了保证复杂度,我们将它用可持久化线段树来写。(只需实现单点修改和查询,连回传更新都可以不要)

线段树中以颜色为下标,第 u u u个历史版本记录从根节点到节点 u u u的路径上各种颜色的出现次数和该种颜色的长度,计算 u u u v v v的路径上的某种颜色的边数和该颜色的边的长度的方法和计算原始路径长度的方法类似。

初始化LCA和可持久化线段树的时间复杂度均为 O ( N log ⁡ 2 N ) O(N\log_2 N) O(Nlog2N),查询一次需要的复杂度为 O ( log ⁡ 2 N ) O(\log_2 N) O(log2N)。初始化LCA和可持久化线段树以及 d d d数组的总时间复杂度为 O ( N log ⁡ 2 N ) O(N\log_2 N) O(Nlog2N),查询总时间为 O ( Q log ⁡ 2 N ) O(Q\log_2 N) O(Qlog2N),故总时间复杂度为 O ( ( N + Q ) log ⁡ 2 N ) O((N+Q)\log_2 N) O((N+Q)log2N),非常优秀,在AtCoder上可以跑约250ms。

据说还可以用复杂度为 O ( ( N + Q ) N ) O((N+Q)\sqrt{N}) O((N+Q)N )的树上莫队(听说很复杂。。。也没学过。。。)或者复杂度为 O ( N ) O(N) O(N)离线LCA算法(没学,逃。。。)来做。。。

参考代码

#include
#include
using namespace std;

const int Maxn=1e5;
const int Maxlogn=17;

int N;

struct Edge {
	static Edge *ecnt;
	int to,dis,col;
	Edge *nxt;
};
Edge e[2*Maxn+5];
Edge *G[Maxn+5];
Edge* Edge::ecnt=&e[0];
void addedge(int u,int v,int c,int d) {
	Edge *p=++Edge::ecnt;
	p->to=v,p->dis=d,p->col=c;
	p->nxt=G[u],G[u]=p;
}

struct SegmentTree {
	struct Segment {
		int cnt,sum;
		Segment *ch[2];
	};
	Segment t[Maxn*25+5];
	Segment *ncnt,*NIL;
	SegmentTree() {NIL=ncnt=&t[0];NIL->ch[0]=NIL->ch[1]=NIL;}
	Segment * newnode() {
		Segment *ret=++ncnt;
		ret->cnt=ret->sum=0;
		ret->ch[0]=ret->ch[1]=NIL;
		return ret;
	}
	Segment * add(Segment *pre,int l,int r,int pos,int val) {
		Segment *rt=newnode();(*rt)=(*pre);
		if(l==r) {
			rt->cnt++,rt->sum+=val;
			return rt;
		}
		int mid=(l+r)>>1;
		if(pos<=mid)
			rt->ch[0]=add(pre->ch[0],l,mid,pos,val);
		else rt->ch[1]=add(pre->ch[1],mid+1,r,pos,val);
		return rt;
	}
	void query(Segment *rt,int l,int r,int pos,int &ret_cnt,int &ret_sum) {
		if(l==r) {
			ret_cnt=rt->cnt;
			ret_sum=rt->sum;
			return;
		}
		int mid=(l+r)>>1;
		if(pos<=mid)
			query(rt->ch[0],l,mid,pos,ret_cnt,ret_sum);
		else query(rt->ch[1],mid+1,r,pos,ret_cnt,ret_sum);
	}
};
SegmentTree T;
SegmentTree::Segment *RT[Maxn+5];

int dep[Maxn+5];
int dis[Maxn+5];
int fa[Maxlogn+5][Maxn+5];
void DFS(int u,int par,int depth) {
	dep[u]=depth;
	fa[0][u]=par;
	for(int i=1;i<=Maxlogn;i++)
		fa[i][u]=fa[i-1][fa[i-1][u]];
	for(Edge *p=G[u];p!=NULL;p=p->nxt) {
		int v=p->to;
		if(v==par)continue;
		RT[v]=T.add(RT[u],1,N,p->col,p->dis);
		dis[v]=dis[u]+p->dis;
		DFS(v,u,depth+1);
	}
}

int CalcLCA(int u,int v) {
	if(dep[u]<dep[v])
		swap(u,v);
	for(int i=Maxlogn;i>=0;i--)
		if(dep[fa[i][u]]>=dep[v])
			u=fa[i][u];
	if(u==v)return u;
	for(int i=Maxlogn;i>=0;i--)
		if(fa[i][u]!=fa[i][v])
			u=fa[i][u],v=fa[i][v];
	return fa[0][u];
}

int main() {
	#ifdef LOACL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	int Q;
	scanf("%d %d",&N,&Q);
	for(int i=1;i<N;i++) {
		int u,v,c,d;
		scanf("%d %d %d %d",&u,&v,&c,&d);
		addedge(u,v,c,d);
		addedge(v,u,c,d);
	}
	RT[1]=T.NIL;
	DFS(1,0,1);
	while(Q--) {
		int c,d,u,v;
		scanf("%d %d %d %d",&c,&d,&u,&v);
		int lca=CalcLCA(u,v);
		int c1,c2,c3,d1,d2,d3;
		T.query(RT[u],1,N,c,c1,d1),T.query(RT[v],1,N,c,c2,d2),T.query(RT[lca],1,N,c,c3,d3);
		int totc=c1+c2-2*c3,totd=d1+d2-2*d3,pred=dis[u]+dis[v]-2*dis[lca];
		printf("%d\n",pred-totd+d*totc);
	}
	return 0;
}

你可能感兴趣的:(#,AtCoder)