首页 > 编程语言 > 详细

#搜索,树剖,set#洛谷 3322 [SDOI2015]排序&洛谷 3320 [SDOI2015]寻宝游戏

时间:2020-02-14 17:23:14      阅读:49      评论:0      收藏:0      [点我收藏+]

JZOJ 4049 JZOJ 4050 From 2020.02.13【NOIP提高组】模拟A 组


洛谷 3322 [SDOI2015]排序

题目

小A有一个\(1\sim 2^N\)的排列\(A[1\sim 2^N]\),他希望将A数组从小到大排序,小A可以执行的操作有\(N\)种,每种操作最多可以执行一次,
对于所有的\(i(1<=i<=N)\),第\(i\)种操作为将序列从左到右划分为\(2^{N-i+1}\)段,每段恰好包括\(2^{i-1}\)个数,然后整体交换其中两段.
小A想知道可以将数组A从小到大排序的不同的操作序列有多少个,小A认为两个操作序列不同,当且仅当操作个数不同,或者至少一个操作不同(种类不同或者操作位置不同)。


分析

操作顺序不影响答案(这里不算操作位置)
答案乘阶乘,也就是操作顺序改变虽然操作位置改变
但是仍然可以通过一次搜索计算
因为第\(i+1\)次操作不能改变第\(i\)次操作(内部不会改变)
所以分四种情况讨论
1.所有段连续递增(继续搜索下一次操作)
2.有一个段没有连续递增(切开一半判断递增,继续搜索)
3.有两个段没有连续递增(两段各交换一半判断递增,继续搜索)
4.有三个或以上的段没有连续递增(显然无解)
所以代码就出来了


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
int a[4096],er[13],n; long long jc[13],ans;
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline bool check(int st,int m){
    for (rr int i=st+1;i<st+er[m];++i)
        if (a[i-1]+1!=a[i]) return 0;
    return 1;
}
inline void Swap(int &a,int &b){a^=b,b^=a,a^=b;}
inline void Twap(int l1,int l2,int m){for (rr int i=0;i<er[m];++i) Swap(a[l1+i],a[l2+i]);}
inline void dfs(int dep,int now){
    if (dep>n) {ans+=jc[now]; return;}
    rr int pos1=-1,pos2=-1;
    for (rr int i=0;i<er[n];i+=er[dep])
    if (!check(i,dep)){
        if (pos2==-1) pos2=pos1,pos1=i;
            else return;
    }
    if (pos1==-1&&pos2==-1) dfs(dep+1,now);
    else if (pos2==-1){
        Twap(pos1,pos1+er[dep-1],dep-1);
        if (check(pos1,dep)) dfs(dep+1,now+1);
        Twap(pos1,pos1+er[dep-1],dep-1);
    }else{
        for (rr int k1=0,flag=0;k1<2;++k1)
        for (rr int k2=0;k2<2;++k2){
            Twap(pos1+k1*er[dep-1],pos2+k2*er[dep-1],dep-1);
            if (check(pos1,dep)&&check(pos2,dep)) dfs(dep+1,now+1),flag=1;
            Twap(pos1+k1*er[dep-1],pos2+k2*er[dep-1],dep-1);
            if (flag) {flag=0; break;}
        }
    }
}
signed main(){
    n=iut(),er[0]=jc[0]=1;
    for (rr int i=1;i<=n;++i)
        er[i]=er[i-1]<<1,jc[i]=jc[i-1]*i;
    for (rr int i=0;i<er[n];++i) a[i]=iut();
    dfs(1,0);
    return !printf("%lld",ans);
}

洛谷 3320 [SDOI2015] 寻宝游戏

题目


分析

首先答案就是所有有宝藏(关键点)所构成的极小连通子树的边权和两倍
然后从哪一个点开始最小答案应该都是一样的,所以考虑从dfs序最小的开始,
沿着dfs序依次求两点间距离,用set维护该点dfs序的前驱和后继,
如果该点不存在前驱或后继那么把它变成最后一个点或最前一个点,
因为题目说到要返回,也就是最后一个点要回到第一个点


代码

#include <cstdio>
#include <cctype>
#include <set>
#define rr register
using namespace std;
const int N=100101; struct node{int y,w,next;}e[N<<1]; set<int>uk; set<int>::iterator it; bool v[N];
int k=1,n,m,ls[N],root,fat[N],nfd[N],dep[N],son[N],dfn[N],top[N],big[N],tot; long long W[N],ans;
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline void print(long long ans){
    if (ans>9) print(ans/10);
    putchar(ans%10+48);
}
inline void dfs1(int x,int fa){
    dep[x]=dep[fa]+1,fat[x]=fa,son[x]=1;
    for (rr int i=ls[x],mson=-1;i;i=e[i].next)
    if (e[i].y!=fa){
        W[e[i].y]=W[x]+e[i].w,dfs1(e[i].y,x),son[x]+=son[e[i].y];
        if (son[e[i].y]>mson) big[x]=e[i].y,mson=son[e[i].y];
    }
}
inline void dfs2(int x,int linp){
    dfn[x]=++tot,nfd[tot]=x,top[x]=linp;
    if (!big[x]) return; dfs2(big[x],linp);
    for (rr int i=ls[x];i;i=e[i].next)
    if (e[i].y!=fat[x]&&e[i].y!=big[x])
        dfs2(e[i].y,e[i].y);
}
inline signed Lca(int x,int y){
    while (top[x]!=top[y]){
        if (dep[top[x]]<dep[top[y]]) x^=y,y^=x,x^=y;
        x=fat[top[x]];
    }
    if (dep[x]>dep[y]) x^=y,y^=x,x^=y;
    return x;
}
inline long long doit(int x,int y){
    if (x==y) return 0;
    return W[x]+W[y]-2*W[Lca(x,y)];
}
signed main(){
    n=iut(); rr int Q=iut();
    for (rr int i=1;i<n;++i){
        rr int x=iut(),y=iut(),w=iut();
        e[++k]=(node){y,w,ls[x]},ls[x]=k,
        e[++k]=(node){x,w,ls[y]},ls[y]=k;
    }
    dfs1(1,0),dfs2(1,1);
    while (Q--){
        rr int x=iut(),lx,rx;
        if (!v[x]) uk.insert(dfn[x]);
        it=uk.lower_bound(dfn[x]);
        if (it!=uk.begin()) --it; else it=--uk.end(); lx=nfd[*it];
        it=uk.upper_bound(dfn[x]);
        if (it==uk.end()) it=uk.begin();  rx=nfd[*it];
        if (v[x]) uk.erase(dfn[x]);
        rr long long sw=doit(lx,x)+doit(x,rx)-doit(lx,rx);
        if (!v[x]) v[x]=1,ans+=sw;
            else v[x]=0,ans-=sw;
        print(ans),putchar(10);
    }
    return 0;
}

#搜索,树剖,set#洛谷 3322 [SDOI2015]排序&洛谷 3320 [SDOI2015]寻宝游戏

原文:https://www.cnblogs.com/Spare-No-Effort/p/12308110.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!