题解:P11008

题面

P11008

![[../../题面/洛谷题面/P11008|P11008]]

分析

序列 \(p\)\(1\) ~ \(n\) 的排列,因此考虑搜索回溯.

\(\sum n \le 2 \times 10^6\) 得知 \(O(n^2)\) 会炸,考虑剪枝.

坚信深搜过百万的蒟蒻...

\(b\) 序列为长度 \(n-1\) 的序列:

\[{b_1,b_2,b_3 \cdots b_n-1 } \]

将其前面插入一个元素 \(x\) ,得到长度为 \(n\) 的序列 \(b'\) :

\[ { x,b_1,b_2,b_3 \cdots b_n-1 } \]

\(f(i)\) 表示原 \(b\) 序列从第 \(1\) 位到第 \(i\) 位的异或前缀和,\(f'(i)\)表示后 \(b'\) 序列从第 \(1\) 到第 \(i+1\) 位的异或前缀和,则有:

\[f'(i)=f(i)\quad xor\quad b'_1 \]

\(b_{1}'\) 可行时,根据题目中异或运算性质将其做异或前缀和便得到了所要求的 \(p\) 序列,此时 \(p_i=f'(i)\),这样保证了 \(b_i=p_i\) \(xor\) \(p_{i+1}\) .

此时有一个浅显的可行性剪枝,因为 \(1 \le f'(i) \le n\) , 所以当存在一个 \(b'_1\) 使任意一个 \(f'(i)\) 满足 \(f'(i) = 0\)\(f'(i)\gt n\) 时,此时序列 \(b'\) 第一个数字不可行 :

因此可以一边输入 \(b\) 序列一边存异或前缀和,每一位筛掉一个如此的 \(b'_1\) .

综上,我们便完成(水过)了这一题.

Code

#include<bits/stdc++.h>
using namespace std;  
const int N=2e6+1;  
int t,n,fl,b[N],p[N],mp[N],sum;  
//mp存是否填过该数字
unordered_map<int,int> vis;  
//vis存可行性,true表示不可行,map的初始化比数组稍快亿点点,不用TLE
void dfs(int x,int dep){  
	if(x<1||x>n||mp[x])return;//可行性剪枝,当P[i]大于n 或 小于1 或 数字重复时不可行
	  if(dep==n){//可行
		for(int i=1;i<n;++i)printf("%d ",p[i]);//输出答案  
		printf("%d\n",x);  
		fl=0;//已经输出答案,打上标记避免再次搜索
		return;  
	}
	p[dep]=x;  
	//搜索回溯
	++mp[x];  
	dfs(b[dep]^x,dep+1);  
	--mp[x];  
}
int main() {  
	scanf("%d",&t);  
      while(t--){  
    	scanf("%d",&n);  
    	vis.clear();//初始化
        fl=1,sum=0;  
          for(int i=1;i<n;++i){  
        	scanf("%d",b+i);  
        	sum^=b[i];//做异或前缀和
			++vis[sum];//剪枝:第一个数字的选择中筛掉0^sum即sum(序列p中仅有1~n,没有0)
//        	++vis[(n+1)^sum]; 剪枝:第一个数字的选择中筛掉(n+1)^sum(序列p中仅有1~n,没有n+1)
//          做其中一个剪枝就行,两个剪枝一起做速度更慢
		}
          for(int i=1;i<=n;++i){  
        	if(!fl) break;//如果可行
        	if(!vis[i]) dfs(i,1);//如果且尚未输出答案,搜索第一个数为i的情况
		}
    }
    return 0;
}
posted @ 2025-08-20 20:55  badn  阅读(5)  评论(0)    收藏  举报