Educational Codeforces Round 54 (Rated for Div. 2)(solve5/7)

题目链接:http://codeforces.com/contest/1076

A

题意:去掉一个字母,让串字典序最小。

思想:如果串是一个山峰形状,删掉第一个上峰坡,如果一直是降序就删掉第一个。

B

题意:给你一个n,可以执行减去一个最小的素数因子,问你最多执行多少步。

思想:模拟,本身是素数就直接输出1。对于一个非素数,如果是偶数肯定每次都是减去2,结果就是n/2,如果是一个奇数,那么他的最小素数因子肯定是一个奇数,减去这个奇数肯定是偶数,就同上了.

C

题意:给你一个d,问你是否存在a+b==a*b 保证误差在1e-6范围内

思想:可以变成一个一元二次方程组,然后就可以求出来根,求解即可。或者二分一个结果,判断是否成立。但是对于那个求根公式需要考虑 根号下小于0的情况,特判下哪几种情况即可。

#include<bits/stdc++.h>
using namespace std;
const long double eps = 1e-6;
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		long double d;
		scanf("%Lf",&d);
		if(d==1 || d==2 || d==3)
		{
			printf("N\n");
			continue;
		}
		long double temp = (d*d-4.0*d);
		long double b = (d- sqrt(temp))/2.0;
		long double a = d - b;
		printf("Y %.12Lf %.12Lf\n",a,b);
	}	
	return 0;
}

D

题意:给你n个点m条边,问你保存k条边,最多有多少个点满足1到所有点的距离等于最短距离。

思想:Dijkstra+堆优化 跑最短路,dfs爆搜k条边即可。spfa据说被疯狂卡掉。我写的麻烦还dfs爆搜了,可以不需要,再求最短路的时候保存也行。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
const int INF = 0x3f3f3f3f;
int cnt,k;
int head[maxn];
int vis[maxn];
ll dist[maxn];
vector<int>V;
struct node{
	int to;
	int valu;
	int next;
	int id;
}no[maxn*2];
struct edge{
	int s;
	ll w;
	bool operator < (const edge& a) const{ return w>a.w;}
}; 
void add(int u,int v,int valu,int id)
{
	no[cnt].to=v;
	no[cnt].next=head[u];
	no[cnt].valu=valu;
	no[cnt].id=id;
	head[u]=cnt++;
}
void Dijkstra()
{
	memset(dist,INF,sizeof(dist));
	memset(vis,0,sizeof(vis));
	dist[1]=0;
	priority_queue<edge>q;
	q.push(edge{1,0});
	while(!q.empty())
	{
		edge temp = q.top();
		q.pop();
		if(vis[temp.s])
			continue;
		vis[temp.s]=1;
		for(int i=head[temp.s];i!=-1;i=no[i].next)
		{
			if(dist[no[i].to] > dist[temp.s] + no[i].valu)
			{
				dist[no[i].to] = dist[temp.s] + no[i].valu;
				q.push(edge{no[i].to,dist[no[i].to]});
			}
		} 
	}
}
void dfs(int u)
{
	if(k<=0)
		return ;
	if(vis[u])
		return ;
	vis[u]=1;
	for(int i=head[u];i!=-1;i=no[i].next)
	{
		if(k<=0)
			return ;
		if(vis[no[i].to])
			continue;
		if(dist[no[i].to] == dist[u] + no[i].valu)
		{
			V.push_back(no[i].id);
			k--;
			dfs(no[i].to);
		}
	} 
}
int main() 
{
	cnt=0;
	int n,m;
	memset(head,-1,sizeof(head));
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++)
	{
		int u,v,valu;
		scanf("%d%d%d",&u,&v,&valu);
		add(u,v,valu,i);
		add(v,u,valu,i);
	}
	Dijkstra();
	memset(vis,0,sizeof(vis));
	dfs(1);
	printf("%d\n",int(V.size()));
	for(int i=0;i<int(V.size());i++)
	{
		if(i)
			printf(" ");
		printf("%d",V[i]);
	}
	puts("");
	return 0;
}

E题

题意:给你n个点n-1条边的数,有m次修改,每次将以Vi所有距离Vi距离小于等于Ki的节点的权值增加Xi,输出m次修改后所有的节点的权值是多少,自己到自己的距离是0.

思想:树状数组维护当前节点对于子树的影响,Vi节点能影响到只有自己的孩子节点,所以对于所有的查询的Vi节点,树状数组维护对于孩子的影响,当节点结束之后在去掉即可。深度最大是3e5,不需要到1e9.

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
int cnt;
int head[maxn];
int dep[maxn];
int vis[maxn];
ll ans[maxn];
ll c[maxn];
struct node{
	int v;
	int next;
}no[maxn*2];
vector<pair<int,ll>>V[maxn];
void add(int u,int v)//建图 
{
	no[cnt].v=v;
	no[cnt].next=head[u];
	head[u]=cnt++;
}
void dfs(int u,int fa)//深度 
{
	for(int i=head[u];i!=-1;i=no[i].next)
	{
		int v = no[i].v;
		if(dep[v] || v==fa)
			continue;
		dep[v]=dep[u]+1;
		dfs(v,u);
	}
}
void up(int d,ll valu)
{
	for(int i=min(d,(int)3e5);i<=3e5;i+=(i&(-i)))
		c[i]+=valu;
}
ll qu(int d)
{
	ll Ans=0;
	for(int i=d;i>0;i-=(i&(-i)))
		Ans+=c[i];
	return Ans; 
}
void dfs1(int u)
{
	vis[u]=1;
	for(int i=0;i<V[u].size();i++)//先将当前节点所有影响都加上
		up(dep[u]+V[u][i].first,V[u][i].second);
	ans[u]=qu((int)3e5)-qu(dep[u]-1);//计算下祖先节点对于当前节点的影响
	for(int i=head[u];i!=-1;i=no[i].next)//dfs自己的孩子节点
	{
		int v=no[i].v;
		if(vis[v])
			continue;
		dfs1(v);
	}
	for(int i=0;i<V[u].size();i++)//影响删掉即可
		up(dep[u]+V[u][i].first,-V[u][i].second);
} 
int main()
{
	int n;
	cnt=0;
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for(int i=0;i<n-1;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	dep[1]=1;
	dfs(1,0);
	int m;
	scanf("%d",&m);
	for(int i=0;i<m;i++)
	{
		int u,d;
		ll valu;
		scanf("%d%d%lld",&u,&d,&valu);
		V[u].push_back(make_pair(min(d,(int)3e5),valu));
	}
	dfs1(1);
	for(int i=1;i<=n;i++)
	{
		if(i>=2)
			printf(" ");
		printf("%lld",ans[i]);
	}
	puts("");
	return 0;
} 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值