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

Posted 1000suns

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[清华集训2017]小 Y 和地铁(神奇思路,搜索,剪枝,树状数组)相关的知识,希望对你有一定的参考价值。

 

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

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

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

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

技术图片 技术图片 技术图片 技术图片 技术图片 技术图片 技术图片 技术图片

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

然后考虑这种情况:

技术图片

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

那么合法状态只剩四种:

技术图片 技术图片 技术图片 技术图片

再考虑这两个线路:

技术图片

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

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

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

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

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

$O(2^\\fracn2\\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

 

以上是关于[清华集训2017]小 Y 和地铁(神奇思路,搜索,剪枝,树状数组)的主要内容,如果未能解决你的问题,请参考以下文章

[LOJ#2325]「清华集训 2017」小Y和恐怖的奴隶主

UOJ#339. 清华集训2017小 Y 和二叉树 贪心

LibreOJ #2325. 「清华集训 2017」小Y和恐怖的奴隶主(矩阵快速幂优化DP)

[清华集训]小 Y 和恐怖的奴隶主

「清华集训 2017」无限之环

[LOJ#2327]「清华集训 2017」福若格斯