[dfs][前缀和][二分] Jzoj P5919 逛公园

Description

           琥珀色黄昏像糖在很美的远方,思念跟影子在傍晚一起被拉长……
Description
      小 B 带着 GF 去逛公园,公园一共有 n 个景点,标号为 1 . . . n。景点之间有 m 条路径相连。
      小 B 想选择编号在一段区间 [l, r] 内的景点来游玩,但是如果这些景点的诱导子图形成了环,那么 GF 将会不高兴。
      小 B 给出很多个询问 [x, y],想让你求有多少个区间 [l, r] 满足 x ≤ l, r ≤ y 且不会使 GF不高兴。
 

Input

第一行为两个整数 n, m,表示景点和路径的数量。
第 2 . . . m + 1 行每行两个整数 ui, vi 表示第 i 路径的两端。
第 m + 2 行是一个整数 q 表示询问的个数,接下来 m 行每行两个整数 xi, yi 表示询问。

Output

q 行,每行一个整数表示答案。
 

Sample Input

8 9
1 2
2 3
3 1
4 5
5 6
6 7
7 8
8 4
7 2
3
1 8
1 4
3 8
 

Sample Output

27
8
19

 
 

Data Constraint

对于 30% 的数据,n, m ≤ 100。
对于另外 10% 的数据,n = m + 1。
对于另外 10% 的数据,n = m
对于 100% 的数据,n, m ≤ 3 × 10^5, xi ≤ yi,不存在重边、自环,不存在一条边同时存在于两个不同的简单环。
 

Hint

诱导子图:子图 G′ = (V′, E′),原图 G = (V, E)。V′ 是 V 的子集,E′ = {(u, v)|u, v ∈V′,(u, v) ∈ E}

 

题解

  • 首先,我们先打一个tarjan来求环,然后我们发现,对于一个环只用知道环内结点编号的最大值和最小值就知道是否被包含了
  • 这样的话,题目就转换为了在一个数轴上有一些线段,每次询问一个区间不包含线段的方案数
  • 设k[i]为i能延伸的最远处,这个数组怎么求呢,那么就是在对于每条线段[l,r],我们令k[r]=l+1,然后从左到右k[1]=1,l[i]=max(k[i],k[i1])(i>2)
  • 还可以打一个前缀和sum[i],为前i个最多能延伸的长度
  • 这样我们对于一组询问就可以统计答案了
  • 若当前询问区间为[x,y],假设其中的一个位置i,若满足k[i]>=x,那么它对答案的贡献就是i−k[i]+1
  • 若满足k[i]<x,那么对答案的贡献就是ix+1
  • 这样的话,我们就能二分出来一个位置i满足k[i]>=x k[i-1]<x
  • 那么[i,y]的区间就可以用前缀和直接算,对于[x,i-1]就是个等差数列,(首项+末项)*项数/2

代码

 1 #include <cstdio> 
 2 #include <iostream>
 3 #include <cstring>
 4 #define N 600010
 5 using namespace std;
 6 struct edge{int to,from;}e[N];
 7 int n,m,cnt,Q,fa[N],dep[N],k[N],head[N];
 8 bool vis[N];
 9 long long sum[N];
10 void insert(int x,int y) { e[++cnt].to=y; e[cnt].from=head[x]; head[x]=cnt; }
11 void dfs(int x)
12 {
13     vis[x]=1;
14     for (int i=head[x];i;i=e[i].from)
15         if (e[i].to!=fa[x])
16         {
17             if (vis[e[i].to])
18             {
19                 if (dep[e[i].to]>dep[x]) continue;
20                 int mx=max(x,e[i].to),mn=min(x,e[i].to);
21                 for (int j=x;j!=e[i].to;j=fa[j]) mx=max(mx,j),mn=min(mn,j);
22                 k[mx]=mn;
23                 continue;
24             }
25             dep[e[i].to]=dep[x]+1,fa[e[i].to]=x,dfs(e[i].to);
26         }
27 }
28 int main()
29 {
30     scanf("%d%d",&n,&m);
31     for (int i=1,x,y;i<=m;i++) scanf("%d%d",&x,&y),insert(x,y),insert(y,x);
32     dfs(1);
33     for (int i=1;i<=n;i++) k[i]=max(k[i-1],k[i]),sum[i]=sum[i-1]+(i-k[i]);
34     scanf("%d",&Q);
35     while (Q--)
36     {
37         int x,y;
38         scanf("%d%d",&x,&y);
39         long long l=y+1;
40         for (int j=1<<18;j;j>>=1) if (l-j>0&&k[l-j]>=x) l-=j;
41         printf("%lld\n",(sum[y]-sum[l-1]+(long long)(l-x)*(l-x+1)/2));
42     }
43 }

 

posted @ 2018-10-22 21:48  BEYang_Z  阅读(160)  评论(0编辑  收藏  举报