[清华集训2017]小 Y 和地铁(神奇思路,搜索,剪枝,树状数组)

 

世界上最不缺的就是好题。

首先考虑暴搜。(还有什么题是从这东西推到正解的……)

首先单独一个换乘站明显没用,只用考虑一对对的换乘站。

那么有八种情况:(从题解偷图)

       

然后大力枚举每个换乘站的情况。同时判断交点。$O(n\times 8^{\frac{n}{2}})$。

然后考虑这种情况:

发现对于任意一条地铁线,要么与这两个都有交点,要么可以与这两个都没有交点。(其实会有与一个有两个交点,与另一个没有交点的情况。这时也可以把这条线换个方向,答案不会更差。思考思考为什么)

那么合法状态只剩四种:

   

再考虑这两个线路:

发现,虽然对于左端点在第一个红点右边的地铁线,这两种没有区别,但是对于左端点在第一个红点左边的地铁线,它们可能有区别。
不过由于我们搜索是按左端点从小到大搜的,所以可以贪心取目前这两条线最优的一条,不会影响后面的值。

最后每次合法状态只剩两个。$O(n\times 2^{\frac{n}{2}})$。

此时合法状态不可能再减少了。考虑加速求交点个数。

发现对于左端点小于当前地铁线的左端点的地铁线,与这个地铁线有交点当且仅当右端点在一个区间(具体看代码)内。

那么可以用树状数组优化。每次给右端点打个标记,然后就变成了求区间和。

$O(2^{\frac{n}{2}}\log n)$。

p.s:要调用很多次树状数组,常数也不小,我就挂了,挂成 80 分。

然而加上个普及组剪枝就过了。$sum>ans$ 时 $return$。

(普及组搜索剪枝不会了……智商越来越低了)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=45;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline int read(){
    int x=0,f=0;char ch=getchar();
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
int t,n,m,tp[maxn],l[maxn],r[maxn],ans,sum,b[2][maxn];
inline void update(int id,int p,int v){
    while(p<=n){
        b[id][p]+=v;
        p+=p&-p;
    }
}
inline int query(int id,int p){
    int s=0;
    while(p){
        s+=b[id][p];
        p-=p&-p;
    }
    return s;
}
inline int query(int id,int l,int r){return query(id,r)-query(id,l-1);}
void dfs(int dep){
    if(sum>ans) return;
    if(dep>m) return void(ans=sum);
    int t1=query(0,l[dep],r[dep]),t2=query(0,r[dep],n),t3=query(1,l[dep],r[dep]),t4=query(1,r[dep],n);
    update(0,r[dep],1);
    sum+=min(t1,t2+t3+t4);
    dfs(dep+1);
    sum-=min(t1,t2+t3+t4);
    update(0,r[dep],-1);
    update(1,r[dep],1);
    sum+=min(t3,t1+t2+t4);
    dfs(dep+1);
    sum-=min(t3,t1+t2+t4);
    update(1,r[dep],-1);
}
int main(){
    t=read();
    while(t--){
        m=0;ans=1e9;
        n=read();
        FOR(i,1,n) tp[i]=read();
        FOR(i,1,n) FOR(j,i+1,n) if(tp[i]==tp[j]) l[++m]=i,r[m]=j;
        dfs(1);
        printf("%d\n",ans);
    }
}
View Code

 

posted @ 2019-07-05 11:45  ATS_nantf  阅读(251)  评论(1编辑  收藏  举报