首页 > 其他分享 >[省选联考 2024] 迷宫守卫 题解

[省选联考 2024] 迷宫守卫 题解

时间:2024-03-10 17:55:20浏览次数:20  
标签:lf 子树 nn 省选 题解 mx mathcal 联考 dp

首先 Bob 肯定是贪心操作,即如果能操作且右儿子中第一个数小于左儿子中的第一个数就一定操作(因为排列中的数两两不同),否则不操作。

考虑一个 dp,即 \(f_{i,j}\) 表示 \(i\) 中的子树操作完以后使得第一个数为 \(j\) 的最小代价。发现总状态数是 \(\mathcal O(2^nn)\) 的,对于一个点的转移可以通过枚举左右子树中最左边的数来更新自己的状态。

考虑怎么优化,发现我们只需要知道左右儿子中最左边的数的大小关系,可以将左、右儿子中的数从小到大排序,预处理出前缀、后缀状态的 \(\min\) 然后枚举较小值再二分一下即可做到 \(\mathcal O(2^nn^2)\)。如果使用归并排序+扫描线可以做到 \(\mathcal O(2^nn)\)。

考虑求完这个东西以后怎么构造答案。先枚举出第一个数最大可以是多少,然后开始递归求解。

对于当前考虑的子树,我们已经知道了最终的左边第一个数是啥,那么我们就可以知道这个数是在左子树中还是右子树中,也就是 Bob 有没有在这个点操作。

求出使得 Bob 最终会这样操作,在另一个子树中需要花掉的最小魔力值,用剩余的魔力值贪心操作最终在左边的那个子树,递归求解。然后再用剩下的魔力值递归求解另一个子树。

时间复杂度 \(\mathcal O(2^nn\sim 2^nn^2)\)。我写的是 \(\mathcal O(2^nn^2)\),能通过此题,优化有点太麻烦了。

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 65538
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
int T,n,mx,a[mxn],sz[mxn<<1],p1[mxn<<1],p2[mxn<<1];
ll k,d[mxn],f1[mxn<<1],f2[mxn<<1],f3[mxn<<1];
vector<ll>dp[mxn<<1];
void dfs(int x,int lf){
    if(x>=1<<n){
        dp[x].resize(2);
        sz[x]=1,dp[x][1]=0;
        return;
    }
    dfs(x<<1,lf);
    dfs(x<<1|1,lf+sz[x<<1]);
    sz[x]=sz[x<<1]+sz[x<<1|1];
    dp[x].resize(sz[x]+1);
    rep(i,1,sz[x])dp[x][i]=1e18;
    rep(i,1,sz[x<<1])p1[i]=p2[i]=i;
    sort(p1+1,p1+sz[x<<1]+1,[&](int x,int y){return a[lf+x]<a[lf+y];});
    
    sort(p2+1,p2+sz[x<<1]+1,[&](int i,int j){
		return a[lf+sz[x<<1]+i]<a[lf+sz[x<<1]+j];
	});
	
    f1[0]=f2[sz[x<<1]+1]=f3[sz[x<<1]+1]=1e18;
    rep(i,1,sz[x<<1])f1[i]=min(f1[i-1],dp[x<<1|1][p2[i]]);
    drep(i,sz[x<<1],1){
    	f2[i]=min(f2[i+1],dp[x<<1|1][p2[i]]);
    	f3[i]=min(f3[i+1],dp[x<<1][p1[i]]);
	}
    rep(i,1,sz[x<<1]){
    	int l=1,r=sz[x<<1]+1;
    	while(l<r){
    		int mid=(l+r)>>1;
    		if(a[lf+sz[x<<1]+p2[mid]]>a[lf+i])r=mid;
    		else l=mid+1;
		}
		dp[x][i]=min(dp[x][i],dp[x<<1][i]+min(f2[l],f1[l-1]+d[x]));
	}
	rep(j,1,sz[x<<1]){
    	int l=1,r=sz[x<<1]+1;
    	while(l<r){
    		int mid=(l+r)>>1;
    		if(a[lf+sz[x<<1]+j]<=a[lf+p1[mid]])r=mid;
    		else l=mid+1;
		}
		dp[x][sz[x<<1]+j]=min(dp[x][sz[x<<1]+j],dp[x<<1|1][j]+f3[l]);
	}
}
ll solve(int x,int s,int lf,ll mx){
    if(x>=1<<n){
        printf("%d ",a[lf+1]);
        return 0;
    }
    if(s<=sz[x<<1]){
        ll mn=1e18;
        rep(i,1,sz[x<<1|1]){
            mn=min(mn,dp[x<<1|1][i]+(a[lf+sz[x<<1]+i]<a[lf+s]?d[x]:0));
        }
        ll cs=solve(x<<1,s,lf,mx-mn);
        mx-=cs;
        int m1=0;
        rep(i,1,sz[x<<1|1])if(dp[x<<1|1][i]+(a[lf+sz[x<<1]+i]<a[lf+s]?d[x]:0)<=mx)m1=max(m1,a[lf+sz[x<<1]+i]);
        rep(i,1,sz[x<<1|1])if(a[lf+sz[x<<1]+i]==m1){
            return cs+solve(x<<1|1,i,lf+sz[x<<1],mx-(a[lf+sz[x<<1]+i]<a[lf+s]?d[x]:0))+(a[lf+sz[x<<1]+i]<a[lf+s]?d[x]:0);
        }
    }else{
        ll mn=1e18;
        rep(i,1,sz[x<<1])if(a[lf+i]>a[lf+s]){
            mn=min(mn,dp[x<<1][i]);
        }
        ll cs=solve(x<<1|1,s-sz[x<<1],lf+sz[x<<1],mx-mn);
        mx-=cs;
        int m1=0;
        rep(i,1,sz[x<<1])if(a[lf+i]>a[lf+s]&&dp[x<<1][i]<=mx){
            m1=max(m1,a[lf+i]);
        }
        rep(i,1,sz[x<<1])if(a[lf+i]==m1){
            return cs+solve(x<<1,i,lf,mx);
        }
    }
}
void Solve(){
    scanf("%d%lld",&n,&k);
    rept(i,1,1<<n)scanf("%lld",&d[i]);
    rep(i,1,1<<n)scanf("%d",&a[i]);
    dfs(1,0);
    mx=0;
    rep(i,1,1<<n)if(dp[1][i]<=k&&a[i]>a[mx])mx=i;
    solve(1,mx,0,k);
    puts("");
}
signed main(){
    scanf("%d",&T);
    while(T--)Solve();
    return 0;
}

标签:lf,子树,nn,省选,题解,mx,mathcal,联考,dp
From: https://www.cnblogs.com/zifanoi/p/18064499

相关文章

  • [省选联考 2024] 魔法手杖 题解
    首先有个很显然的\(\mathcalO(nk^2)\)的做法,即二分答案,然后trie树上判断。对于trie树上一颗子树内的判定,考虑当前二分的\(\text{mid}\)这一位是\(1\)还是\(0\)以及\(x\)这一位填什么。对于\(1\)的情况,如果填\(0\),那么右儿子仍然合法,左儿子中的数必须要放到......
  • [省选联考 2024] 季风 题解
    \(\large\textbf{Statement.}\)求出最小的非负整数\(m\),使得:\[\left|x-\sum_{i=0}^{m-1}x_{i\bmodn}\right|+\left|y-\sum_{i=0}^{m-1}y_{i\bmodn}\right|\lemk\]\(\large\textbf{Solution.}\)考虑枚举\(m\bmodn\),然后求和就转化成了一段前缀和加上整个序列和的若......
  • Gym-101915D 题解
    D给定一张图,分为左右各\(P\)个点,左右各自内部是一个完全图,左右之间有\(m\)条边。求这个图的最大团。\(P\le20,m\leP^2\)。对于每个右部点,求出一个长度为\(20\)的二进制数,第\(i\)位是\(1\)表示它与左部第\(i\)点有连边。枚举右部点的子集\(S\),将它们的二进制数......
  • Acwing166 数独题解 - DFS剪枝优化
    166.数独-AcWing题库题意数独是一种传统益智游戏,你需要把一个9×9的数独补充完整,使得数独中每行、每列、每个3×3的九宫格内数字1∼9均恰好出现一次。请编写一个程序填写数独。思路搜索+剪枝(优化搜索顺序、位运算)优化搜索顺序:很明显,我们肯定是从当前能填合法......
  • [ABC219E] Moat 题解
    [ABC219E]Moat题解思路解析一眼看到输入数据只有\(4\)行\(4\)列,直接想到状压枚举。可以直接枚举所有护城河所包含起来的格子,判断是否连通以及判断是否包含住了所有村庄。判断连通我选择用洪水填充,随便选一个包含着的格子,若可以通过当前格移动到所有被包含格就说明连通。以......
  • ABC344G 题解
    ABC344G题解给定平面上\(n\)个点和\(q\)条直线,问对于每条线,有多少点在它上方。形式化的,对于直线\(y=ax+b\)统计有多少点\((x,y)\)满足\(y\geax+b\),即\(-ax+y\geb\)。故我们可以将所有点按照\(-ax+y\)排序,从而利用二分简单的得出结果。但是我们显然不可能暴力进......
  • 洛谷 P1099 题解
    洛谷P1099【NOIP2007提高组】树网的核题意简述给定一棵带边权无根树和一个正整数\(s\)。在这棵树的任意直径上截取一段长度不超过\(s\)的路径\(F\),使离\(F\)最远的点到\(F\)的距离最小,求出这个距离。思路记\(\delta(a,b)\)为\(a,b\)之间的路径。对于任意......
  • 省选联考 2024 重塑时光
    首先原问题显然是一个\(\text{DAG}\)计数的形式,施加枚举\(0\)度点集合\(S\)容斥的技巧是自然的。考虑\(k\)刀将其切割成\(t\)段后最终找到一种标号使得存在一种重排方案使其合法的方案数。段内的方案计算是容易的,要求它们所有关系顺序即可,可以快速求出构成一个段的集合......
  • abc344_D - String Bags 题解
    一个月没有碰oi,感觉水平已经退化到负的了。来复健一下。D-StringBagslink题意:给你\(n\)组字符串组,按\(1\)~\(n\)的顺序,对于每组字符串组,可从中至多选一个字符串。求能否用所选串按顺序拼接成指定串,以及选取字符串的最小个数。然后读完题发现是个\(01\)背包;对于第......
  • AT_abc344_e 题解
    本文同步发表于洛谷。赌狗天天输的一集。赛时各种【数据删除】原因导致没做出来。大意给你一个长度为\(N\)的序列\(A=(A_1,\ldots,A_N)\)。保证\(A\)中的元素是不同的。你要处理\(Q\)个操作。每个操作是以下两种类型之一:1xy:在\(A\)中元素\(x\)后面紧接着插入......