【luogu P4005 清华集训2017】小Y和地铁

题目描述

小 Y 是一个爱好旅行的 OIer。一天,她来到了一个新的城市。由于不熟悉那里的交通系统,她选择了坐地铁。

她发现每条地铁线路可以看成平面上的一条曲线,不同线路的交点处一定会设有

换乘站 。通过调查得知,没有线路是环线,也没有线路与自身相交。任意两条不同的线路只会在若干个点上相交,没有重合的部分,且没有三线共点的情况。即,如图所示的情况都是不存在的:

 

小 Y 坐着地铁 0 号线,路上依次经过了 n 个换乘站。她记下了每个换乘站可以换乘的线路编号,发现每条线路与她所乘坐的线路最多只有 2 个换乘站。现在小 Y 想知道,除掉她经过的换乘站以外,这个城市里最少有几个换乘站。只有你告诉她正确的答案,她才会答应下次带你去玩呢。

输入输出格式

输入格式:

 

从文件 metro.in 中读入数据。1

请.注.意.本.题.有.多.组.输.入.数.据。

输入数据的第一行是一个整数 T,表示输入数据的组数。接下来依次给出每组数据。

对于每组数据,第一行是一个整数 n,表示小 Y 经过的换乘站的数目。第二行为 n个用空格隔开的整数,依次表示每个换乘站的可以换乘的线路编号。这些编号都在 1 ~n 之内。

 

输出格式:

 

输出到文件 metro.out 中。

对于每组输入数据,输出一行一个整数,表示除掉这 n 个换乘站之外,最少有几个换乘站。

题意:算比较模糊的了,耿直一点就是一条线上一次出现一些点是中转站,会连接一些地铁线路(最多出现两次),如果一个点出现了两次必须要被连起来,求所有连线最小的交点。

题解:

     ①竟然是个爆搜!

     ②首先数据很小,所有的连线方式只有三种,用左端点出线的方向和右端点入线的方向命名就只有:上上,下下,上下,下上(如果你仔细分析一下会发现这样的定义我们已经省略了两种等价的连线)

       ③按左端点从左到右dfs,维护up和dn两颗BIT分别表示上下的已经连接了的点的右端点,可以统计出相交的情况(需要分类),加一个now>ans的剪枝

     ④然后带着一点点玄学色彩,过了。。。。。  

 

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=100,inf = 1e9;
 6 int n,l[N],r[N],tot;
 7 struct data{
 8     int l,r;
 9     bool operator <(const data&a)const{return l<a.l;}
10 }A[N];
11 struct BIT{
12     int c[N];
13     void add(int x,int d){for(;x<=n;x+=x&-x) c[x]+=d;}
14     int ask(int x){int t=0;for(;x;x-=x&-x)t+=c[x];return t;}    
15 }Up,Dn;
16 char gc(){
17     static char *p1,*p2,s[1000000];
18     if(p1==p2) p2=(p1=s)+fread(s,1,1000000,stdin);
19     return(p1==p2)?EOF:*p1++;
20 }
21 int rd(){
22     int x=0; char c=gc();
23     while(c<'0'||c>'9') c=gc();
24     while(c>='0'&&c<='9') x=x*10+c-'0',c=gc(); 
25     return x;
26 }
27 int tmp,ans;
28 void dfs(int cur){
29     if(cur>tot||tmp>ans) {ans=min(ans,tmp);return;}
30     int pre = tmp; 
31     tmp+=min(Up.ask(A[cur].r-1) - Up.ask(A[cur].l)
32     ,Dn.ask(n)-Dn.ask(A[cur].l)+Up.ask(n)-Up.ask(A[cur].r));
33     Up.add(A[cur].r,1); dfs(cur+1); 
34     Up.add(A[cur].r,-1); tmp=pre;
35     tmp+=min(Dn.ask(A[cur].r-1) - Dn.ask(A[cur].l)
36     ,Up.ask(n)-Up.ask(A[cur].l)+Dn.ask(n)-Dn.ask(A[cur].r));
37     Dn.add(A[cur].r,1); dfs(cur+1); 
38     Dn.add(A[cur].r,-1); tmp=pre;    
39 }
40 int main()
41 {    freopen("mzoj1119.in","r",stdin);
42     freopen("mzoj1119.out","w",stdout);
43     int T=rd();
44     while(T--){
45         n=rd();
46         for(int i=1;i<=n;i++) l[i]=r[i]=0; ans=inf;tmp=tot=0;
47         for(int i=1,x;i<=n;i++)if(!l[x=rd()]) l[x]=i; else r[x]=i; 
48         for(int i=1;i<=n;i++)if(l[i]&&r[i]) A[++tot]=(data){l[i],r[i]};
49         sort(A+1,A+tot+1);    
50         dfs(1);
51         printf("%d\n",ans);
52     }
53     return 0;
54 }//by tkys_Austin;

 

 

 

转载于:https://www.cnblogs.com/Paul-Guderian/p/8690675.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值