Codeforces Round #294 (Div. 2) (C D E)

A and B and Team Training

        给出大神和菜鸟的人数,必须1:2或者2:1组队,问最多组几队。如果n的2倍小于等于m,答案就是n,反过来也一样。否则答案是它们的和除以3,忽略余数。

#include <iostream>    
#include <stdio.h>    
#include <string>    
#include <map>    
#include <vector>    
#include <stack>    
#include <queue>    
#include <string.h>    
#include <algorithm>    
#include <math.h>    
    
using namespace std;    


int main(){
	int n,m;
	cin>>n>>m;
	if(n*2<=m){
		cout<<n<<endl;
	}else if(m*2<=n){
		cout<<m<<endl;
	}else{
		cout<<(n+m)/3<<endl;
	}
	return 0;
}


A and B and Interesting Substrings

        给出26个小写字母的权和一个串,问这个串有多少这样的字串,满足头尾相同,且夹在中间的字符权值的和为0。为了迅速算出某一段的权值,计算前缀和就可以了。做法扫一遍,用map保存和查询前缀和。如果遇到同字母且前缀和相等,就是满足要求的。注意会爆int。

#include <iostream>    
#include <stdio.h>    
#include <string>    
#include <map>    
#include <vector>    
#include <stack>    
#include <queue>    
#include <string.h>    
#include <algorithm>    
#include <math.h>    
    
using namespace std;    
#define ll long long

ll x[26];
char str[100010];
ll sum;

map<ll,ll> mp[26];

int main(){
	for(int i=0;i<26;i++)cin>>x[i];
	scanf("%s",str+1);
	int len=strlen(str+1);
	ll ans=0;
	for(int i=1;i<=len;i++){
		ans+=mp[str[i]-'a'][sum];
		sum+=x[str[i]-'a'];
		mp[str[i]-'a'][sum]++;
	}
	cout<<ans<<endl;
	return 0;
}


A and B and Lecture Rooms

        一棵树。给出两个点,问这棵树上有多少点,到这两个点的距离相等。分析一下就可以知道,如果这两个点的距离是奇数,答案肯定是0。如果距离为偶数,答案就是这两个点的中点,砍掉往这两个点方向的边以后树上剩下的点的个数。解题的关键在于如何迅速得到两个点的距离,这里做法是转为有根树,通过倍增算法求LCA,同时得到深度和子树大小等信息即可计算出来。

        拿到询问的两个点x,y后,分为下面几种情况。

1.它们是同一个点,显然答案为n。

2.它们深度一样,那么x,y的中点就是它们的LCA,只用砍掉两个方向的边就可以了(用倍增算法迅速找到需要砍的子树)。

3.它们深度不一样,首先计算出中点(肯定在较深的一侧,假设为x),然后在中点为根的子树上砍掉x方向的边就可以了(同样用倍增找)。

#include <iostream>
#include <stdio.h>
#include <vector>
#include <memory.h>

using namespace std;
#define max_size 100010

int d[max_size],p[max_size][18];
int head[max_size];
int cnt;

struct Edge{
	int v;
	int pre;
}eg[max_size];

void add(int x,int y){
	eg[cnt].v=y;
	eg[cnt].pre=head[x];
	head[x]=cnt++;
}

void dfs(int k){
	if(head[k]==0)return;
	
	int m,x,i,j;
	for(i=head[k];i!=0;i=eg[i].pre){
		x=eg[i].v;
		p[x][0]=k;
		m=k;
		d[x]=d[k]+1;
		for(j=0;p[m][j]!=0;j++){
			p[x][j+1]=p[m][j];//利用公式p[x][j]=p[p[x][j-1]][j-1],这里的m就是p[x][j-1];
			m=p[m][j];
		}
		dfs(x);
	}
}

int find_lca(int x,int y){
	int m,k;
	if(x==y)return x;
	if(d[x]<d[y]){
		m=x;
		x=y;
		y=m;
	}
	m=d[x]-d[y];
	k=0;
	while(m){//将x的深度调到和y的深度一样
		if(m&1)
		x=p[x][k];
		m>>=1;
		k++;
	}
	if(x==y)return x;
	k=0;
	//向上调节,找最近公共祖先,算法的核心,相当于一个二分查找。
	while(x!=y){
		if(p[x][k]!=p[y][k]||p[x][k]==p[y][k]&&k==0)
		//如果p[x][k]还不相等,说明节点p[x][k]还在所求点的下面,所以继续向上调节
		//如果相等了,并且就是他们父节点,则那个节点一定就是所求点。
		{
			x=p[x][k];
			y=p[y][k];
			k++;
		}
		else//如果p[x][k]=p[y][k],可以说明p[x][k]一定是x和y的共祖先,但不一定是最近的。
		//所以向下找看还有没有更近的公共祖先
		{
			k--;
		}
	}
	return x;
}

vector<int> E[max_size];
bool vis[max_size];
int siz[max_size];
int predfs(int u){
	vis[u]=1;
	int sz=E[u].size();
	siz[u]=1;
	for(int i=0;i<sz;i++){
		int v=E[u][i];
		if(!vis[v]){
			add(u,v);
			siz[u]+=predfs(v);
		}
	}
	return siz[u];
}

int find_ancestor(int u,int dis){
	int k=0;
	while(dis){
		if(dis&1){
			u=p[u][k];
		}
		dis>>=1;
		k++;
	}
	return u;
}

int main(){
	cnt=1;
	int n,m,u,v,ans;
	cin>>n;
	for(int i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		E[u].push_back(v);
		E[v].push_back(u);
	}
	predfs(1);
	dfs(1);
	
	cin>>m;
	while(m--){
		scanf("%d%d",&u,&v);
		int LCA=find_lca(u,v);
		if(d[u]<d[v])swap(u,v);
		
		if(u==v){		//同一个点 
			ans=n;
		}else{
			int dis=d[u]+d[v]-2*d[LCA];
			if(dis&1){
				ans=0;
			}else{		//深度一样 
				if(d[u]==d[v]){
					int cutu=find_ancestor(u,dis/2-1);
					int cutv=find_ancestor(v,dis/2-1);
					ans=n-siz[cutu]-siz[cutv];
				}else{	//深度不一样 
					int an=find_ancestor(u,dis/2);
					int cut=find_ancestor(u,dis/2-1);
					ans=siz[an]-siz[cut];
				}
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值