[并查集] Luogu P4865 Samcompu Loves Water

题目描述

Samcompu需要制定一个水计划。这个计划的主要目的就是为了避开老师监视的时间来水。

老师在中途会离开机房TT次,第ii次将会离开tim_itimi秒。Samcompu划水的时候可不是随便乱水的。他可是拥有"水"资源的。在他的库存中有NN个可以水的网站。Samcompu拥有一种黑科技,他可以几乎不耗任何时间在网站与网站之间跳转并且把跳转的网页的信息秒存。也就是说,Samcompu并不需要在每一次跳转的时候花费时间去浏览网页。当然,这只局限于NN个网站之间的N-1N1个跳转方式(保证每一个网站都可以跳转到另外的所有网站)。对于第ii种跳转方式,第u_iui个网站到第v_ivi个网站的跳转存在一个危险程度w_iwi,这个危险值可能会造成电脑卡死,如果Samcompu不能及时处理,那么就会完美地被老师发现。

值得一提的是,在被查水表很多次后,Samcompu总结出了一个规律:

老师走得越久,能够保证在被老师发现之前处理好电脑卡死的危险程度的上限就越高。简单来说,两者就是成正比的关系,比例系数为1。

可惜的是,Samcompu的黑科技并不稳定,在老师第ii次离开的时候,第K_iKi个跳转方式就不可用了。

当然,每一次水都可以从任意一个网站开始,也可以从任意一个网站结束。

现在Samcompu想知道,对于第ii次老师离开机房时,他能够有多少种不同的安全的水的方案。两种水的方案不同当且仅当这两种水的方案的第一个网站或者最后一个网站不同。

(补充说明: 一个安全的水的方案当且仅当当前是老师第jj次离开教室时跳转的路径中不存在一个跳转方式ii使得tim_j \leqslant w_itimjwi,每一次水完后不可用的跳转方式就会恢复。)

 

 

题解

  • 我们把所有的跳转方式排个序,然后就按照并查集的加边方式,合并的时候计算
  • 那么失效的跳转方式怎么弄呢?
  • 所以就可以把这些可能失效的跳转方式专门拿出来,处理一个询问时就先备份好之前的数组,然后暴力加上需要加上的边,记录答案后就直接恢复原数组就好了

代码

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 using namespace std;
 6 const int N=100010;
 7 int t,n,cnt,ans[N],fa[N],now[N],sl[N],last;
 8 struct node{ int p,q,num; }Q[N];
 9 struct edge{ int from,to,v,num; }e[N];
10 bool cmp(node x,node y){ return x.q<y.q||x.q==y.q&&x.p<y.p; }
11 bool cmp1(edge x,edge y){ return x.v<y.v; }
12 void insert(int u,int v,int w,int id){ e[++cnt].from=u,e[cnt].to=v,e[cnt].v=w,e[cnt].num=id; }
13 int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); }
14 int count(int x){ return x*(x-1); }
15 int main()
16 {
17     scanf("%d%d",&t,&n);
18     for (int i=1,x,y,z;i<n;i++) scanf("%d%d%d",&x,&y,&z),insert(x,y,z,i); 
19     for (int i=1;i<=t;i++) scanf("%d%d",&Q[i].p,&Q[i].q),Q[i].num=i;
20     sort(Q+1,Q+1+t,cmp),sort(e+1,e+n,cmp1);
21     for (int i=1;i<n;i++) now[e[i].num]=i;
22     for (int i=1;i<=t;i++)
23     {
24         if (i==1||Q[i].q!=Q[i-1].q)
25         {
26             for (int j=1;j<=n;j++) fa[j]=j,sl[j]=1;
27             last=1,ans[Q[i].num]=0;
28         }
29         else ans[Q[i].num]=ans[Q[i-1].num];
30         while (last<n&&e[last].v<Q[i].p)
31         {
32             if (last!=now[Q[i].q]&&find(e[last].from)!=find(e[last].to))
33             {
34                 int now1=find(e[last].from),now2=find(e[last].to);
35                 ans[Q[i].num]+=count(sl[now1]+sl[now2])-count(sl[now1])-count(sl[now2]),sl[now2]+=sl[now1];sl[now1]=0,fa[now1]=now2;
36             }
37             last++;
38         }
39     }
40     for (int i=1;i<=t;i++) printf("%d\n",ans[i]);
41 }

 

posted @ 2019-07-23 13:41  BEYang_Z  阅读(140)  评论(0编辑  收藏  举报