[JOISC2017]細長い屋敷

题目大意:
  $n(n\le5\times10^5)$个房间排成一排,相邻两个房间之间有一扇门,第$i$个房间和第$i+1$个房间之间的门可以用第$c_i$种钥匙打开(可能有多个门可以用同一种钥匙打开)。告诉你每个房间有哪几种钥匙。$q(q\le5\times10^5)$次询问,每次询问若一开始在$s$房间,能否到达$t$房间。

思路:
  分别用$l[i],r[i]$表示房间$i$能扩展到的最左/最右的房间。
  对于每种钥匙用一个数组存一下它出现过的房间位置。枚举每个点$i$,若$c_{l[i]-1}$在$l[i]\sim r[i]$中出现过,则$l[i]-1$能走到的$i$一定能走到,$r[i]+1$同理。不断向左、向右进行扩展。这样总共是$O(n^2\log n)$的,不过因为数据水,跑得比真算法还快。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<vector>
 4 inline int getint() {
 5     register char ch;
 6     while(!isdigit(ch=getchar()));
 7     register int x=ch^'0';
 8     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
 9     return x;
10 }
11 const int N=5e5+1;
12 int c[N],l[N],r[N];
13 std::vector<int> pos[N];
14 inline bool find(const int &c,const int &l,const int &r) {
15     return *std::lower_bound(pos[c].begin(),pos[c].end(),l)<=r;
16 }
17 int main() {
18     const int n=getint();
19     for(register int i=1;i<n;i++) c[i]=getint();
20     for(register int i=1;i<=n;i++) {
21         for(register int j=getint();j;j--) {
22             pos[getint()].push_back(i);
23         }
24     }
25     for(register int i=1;i<=n;i++) {
26         pos[l[i]=r[i]=i].push_back(n+1);
27     }
28     for(register int i=1;i<=n;i++) {
29         for(register int j=0;;j=0) {
30             if(r[i]!=n&&find(c[r[i]],l[i],r[i])) j=r[i]+1;
31             if(l[i]!=1&&find(c[l[i]-1],l[i],r[i])) j=l[i]-1;
32             if(!j) break;
33             l[i]=std::min(l[i],l[j]);
34             r[i]=std::max(r[i],r[j]);
35         }
36     }
37     for(register int i=getint();i;i--) {
38         const int s=getint(),t=getint();
39         puts(l[s]<=t&&t<=r[s]?"YES":"NO");
40     }
41     return 0;
42 }

 

posted @ 2018-04-16 15:08  skylee03  阅读(441)  评论(1编辑  收藏  举报