题目链接:
http://172.18.70.217/problem/9
题目大意:
题目分析:
本题的题意就是让我们求出每次破坏后的两个数值。很容易想到按照病毒进行
B
F
S
BFS
BFS就可求出答案,但是这样的时间复杂度为
O
(
n
q
)
O(nq)
O(nq),超过了题目允许的范围,所以我们需要更快的做法。
首先,因为本质上对答案有贡献的只有那些防御值小于攻击值的边。因为我们需要加速,所以一个事实就是,如果之前就处理了低于某个攻击值的边,那么后面就不用处理了,这样时间复杂度会大大下降,因为每条边只遍历一次的缘故时间复杂度下降了。
故我们可以将边按防御值从小到大排序,并将每次攻击按攻击力从小到大排序。那么还有个问题,每次投放病毒的位置不同,怎么快速得到答案呢?因为这本身是一个图,一旦一个点上有病毒,周围边的防御值小于攻击力,那么边那一头的点就会被破坏,举个例子:
这里病毒起始点在五角星处,攻击力为
4
4
4,因为周围各点连边防御值小于
4
4
4所以被破坏,并以新破坏的点为起点重复刚才的行为。所以这些被感染的点在攻击力为
4
4
4的条件下是一个整体,这里就可以用并查集来维护了。按题目要求,并查集的域包括:父亲,最大值,最大值个数。我们按照攻击力遍历时就将这些防御值小于攻击值的点合并起来。查询时就查询病毒起始点所在的整体的情况就行了。由于本题时间卡得较死,所以各处都需要细心处理,不浪费多余的时间。算法时间复杂度大致为
O
(
m
a
x
(
m
,
q
)
)
O(max(m,q))
O(max(m,q))。
正解代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define inf 0x7fffffff
using namespace std;
typedef long long ll;
const ll maxn=300010;
struct node
{
ll u;
ll v;
ll w;
}e[400010];
vector<ll> vec[100005];
struct ano
{
ll name;
ll a;
ll b;
ll num;
ll vec;
vector<ll> *pos;
}p[400010];
ll n,m,q,imp[maxn],mon[maxn];
bool cmp1(node a,node b)
{
return a.w<b.w;
}
bool cmp2(ano a,ano b)
{
return a.vec<b.vec;
}
ll fa[maxn],Max[maxn],times[maxn],val[maxn];
ll resa[100005], resb[100005];
ll get(ll x)
{
if(fa[x]!=x)
fa[x]=get(fa[x]);
return fa[x];
}
void merge(ll a,ll b)
{
ll f1=get(a),f2=get(b);
if(f1!=f2)
{
fa[f1]=f2;
if(Max[f1]>Max[f2])
{
Max[f2]=Max[f1];
times[f2]=times[f1];
}
else if(Max[f1]==Max[f2])
times[f2]+=times[f1];
val[f2]+=val[f1];
}
}
int vis[maxn];
bool nus[maxn];
int main()
{
//freopen("TEST3.txt","r",stdin);
scanf("%lld%lld%lld",&n,&m,&q);
for(ll i=1;i<=n;i++)
scanf("%lld",&imp[i]);
ll whole=0;
for(ll i=1;i<=n;i++)
{
scanf("%lld",&mon[i]);
whole+=mon[i];
}
for(ll i=1;i<=m;i++)
{
ll x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
e[i].u=x;e[i].v=y;e[i].w=z;
}
sort(e+1,e+m+1,cmp1);
for(ll i=1;i<=q;i++)
{
p[i].name=i;
scanf("%lld%lld",&p[i].num,&p[i].vec);
p[i].pos = &vec[i];
for(ll j=1;j<=p[i].num;j++)
{
ll x;
scanf("%lld",&x);
p[i].pos->push_back(x);
}
}
sort(p+1,p+1+q,cmp2);
for(ll i=1;i<=n;i++)
{
fa[i]=i;
Max[i]=imp[i];
times[i]=1;
val[i]=mon[i];
}
ll end1=1;
for(ll i=1;i<=q;i++)
{
memset(nus,false,sizeof(nus));
while(end1<=m && e[end1].w<=p[i].vec)
{
merge(e[end1].u, e[end1].v);
++end1;
}
ll ans=0,count1=0,sum=0;
for(ll j=0;j<p[i].num;j++)
{
ll tmpf=get((*p[i].pos)[j]);
if(vis[tmpf]!=i)
{
if(Max[tmpf]>ans)
{
count1=times[tmpf];
ans=Max[tmpf];
}
else if(Max[tmpf]==ans)
count1+=times[tmpf];
sum+=val[tmpf];
vis[tmpf]=i;
}
}
p[i].a=ans*count1;
p[i].b=whole-sum;
}
for (int i =1;i<=q;++i)
{
resa[p[i].name]=p[i].a;
resb[p[i].name]=p[i].b;
}
for(ll i=1;i<=q;i++)
printf("%lld %lld\n",resa[i],resb[i]);
return 0;
}