2021牛客暑期多校训练营#2:L-WeChat Walk

L-WeChat Walk

原题链接:https://ac.nowcoder.com/acm/contest/11253/L

题目大意

n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1\le n\le 2·10^5) n(1n2105)个人,他们的好友关系可以用有 n n n个点, m ( 1 ≤ m ≤ 2 ⋅ 1 0 5 ) m(1\le m\le 2·10^5) m(1m2105)的图来表示。

每个人会给出ta的微信步数,如果一个人的微信步数大于零,且严格大于他好友列表中任何一个人的步数,则称ta为“步数冠军”。

一天被分成 q q q个时刻,求每个人是“步数冠军”的时间总合。每一时刻,编号为 u u u的人,新走了 m m m步。

任何人的总步数均不超过 1 0 4 10^4 104

解题思路

1

我们先考虑能否直接枚举每个点:

此图为完全图时:在这里插入图片描述
在草稿纸上一算, O ( m q ) O(mq) O(mq)!tle是少不了了。

此图为菊花图时:
在这里插入图片描述
拿出万能的草稿纸,一算,可不得了, O ( m 2 q ) O(m^2q) O(m2q),憋想了。

不对,加几个特判也还是一条好汉!那这可怎么办?:
在这里插入图片描述
所以说,不要妄想全部暴力了。

2

对于朋友比较少的人比如我,祭出我们的二分打法,来试试看吧!

将枚举的人用函数表示,如下图:
在这里插入图片描述

ta的朋友与ta的关系如下:
在这里插入图片描述

“步数冠军”是蓝线:

在这里插入图片描述

那么,求每个人获得的蓝线总量即可。

代码
for(int j=0;j<tm[i].size();j ++)
            {
                int y=tm[i][j].second;
                int x=tm[i][j].first;
                int r=q;
                if(j<tm[i].size()-1)r=tm[i][j+1].second;
                for(auto v:e[i])
                {
                    auto it=lower_bound(tm[v].begin(),tm[v].end(),(pair<int,int>){x,0});
                    if(it!=tm[v].end())r=min(r,(*it).second);
                }
                ans+=max(0,r-y) ;
            } 

3

当一个人朋友很多时,显然不能直接枚举。

ta和ta的朋友表示如下:
在这里插入图片描述

“步数冠军”还是用蓝线表示:

在这里插入图片描述

每一时刻都对应一个步数,对应那一刻的“步数冠军”,把所有作为步数冠军的时间加起来,就得到了答案。

代码
vector<int>res(10010,q+10);
			for(auto v:e[i])
			for(auto x:tm[v])
			res[x.first]=min(res[x.first],x.second);
			for(int i=9999;i>=1;i--)res[i]=min(res[i],res[i+1]);
			for(int j=0;j<tm[i].size();j++)
            {
                int y=tm[i][j].second;
                int x=tm[i][j].first;
                int r=q;
                if(j<tm[i].size()-1)r=tm[i][j+1].second;
                r=min(r,res[x]) ;
                ans+=max(0,r-y) ;
            }

4

由分块思想得出大小点判断应以 n \sqrt{n} n 为分界点此题完结

代码实现

ac代码如下

#include<bits/stdc++.h>
using namespace std;
const int s=447;
int n,m,q;
int main()
{
	cin>>n>>m>>q;vector < vector< pair<int,int> > >tm(n+100); 
	vector<vector<int> > e(n+100);
	for(int i=0;i<m;i++)
	{
		int x,y,u;
		cin>>x>>y;
		e[x].push_back(y),e[y].push_back(x);
	}
	for(int i=1;i<=q;i++)
	{
		int u,w;
		cin>>u>>w;
		tm[u].push_back({w,i});
	}
	for(int i=1;i<=n;i++)
	for(int j=1;j<tm[i].size();j++)
	tm[i][j].first+=tm[i][j-1].first;
	for(int i=1;i<=n;i++)
	{
		int ans=0; 
		if(e[i].size()<=s)
		{
			for(int j=0;j<tm[i].size();j ++)
            {
                int y=tm[i][j].second;
                int x=tm[i][j].first;
                int r=q;
                if(j<tm[i].size()-1)r=tm[i][j+1].second;
                for(auto v:e[i])
                {
                    auto it=lower_bound(tm[v].begin(),tm[v].end(),(pair<int,int>){x,0});
                    if(it!=tm[v].end())r=min(r,(*it).second);
                }
                ans+=max(0,r-y) ;
            } 
		}
		else{
			vector<int>res(10010,q+10);
			for(auto v:e[i])
			for(auto x:tm[v])
			res[x.first]=min(res[x.first],x.second);
			for(int i=9999;i>=1;i--)res[i]=min(res[i],res[i+1]);
			for(int j=0;j<tm[i].size();j++)
            {
                int y=tm[i][j].second;
                int x=tm[i][j].first;
                int r=q;
                if(j<tm[i].size()-1)r=tm[i][j+1].second;
                r=min(r,res[x]) ;
                ans+=max(0,r-y) ;
            }
		}cout<<ans;puts("");
	}
	
}	

ps:

这是我的第八篇博客,也是我最长最详细的一篇。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值