題解:P11008 『STA - R7』異或生成序列

badn發表於2024-10-06

Solution

序列 \(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)\) xor \(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)\) 滿足如下條件時,此時序列 \(b'\) 第一個數字不可行 。:

\(f'(i)\) = \(f(i)\) \(xor\) \(b'_1 = n+1\)\(0\)

因此可以一邊輸入 \(b\) 序列一邊存異或字首和,每一位篩掉一個如此的 \(b'_1\)

(這實際上把一部分不可行搜尋給提前篩掉,剩下的不可行搜尋仍繼續判斷並剪枝,這樣便能大大降低複雜度,將那些把搜尋程式碼卡到趨近 \(O(n^2)\) 的資料削弱成\(O(n \log n)\)。不寫快讀不加\(O2\), \(800ms\) 險勝)

綜上,我們便完成(水過)了這一題。

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;
}

快讀壓行程式碼

加了快讀的程式碼( \(600\)\(ms\) 險勝,壓行,碼風稍怪,輕噴):

#include<bits/stdc++.h>
using namespace std;
#define to(x,y) for(int x=1;x<=y;++x)
#define fr(x,y) for(int x=0;x<y;++x)

inline void wrt(int x) {if (x < 0) {x = -x;putchar('-');}if (x > 9) wrt(x / 10);putchar(x % 10 + '0');}
inline int read(){int x=0,f=1;char ch=getchar();while(ch<48||ch>57){if(ch=='-')f=-1;ch=getchar();}while(ch>=48&&ch<=57)x=x*10+ch-48,ch=getchar();return x*f;}

const int N=2e6+1;
int t,n,fl,b[N],p[N],mp[N],sum;
unordered_map<int,int> vis;

void dfs(int x,int dep){
	if(x==0||x>n||mp[x])return;
	if(dep==n){
		to(i,n-1)wrt(p[i]),putchar(' ');
		wrt(x),putchar('\n');
		fl=0;
		return;
	}
	p[dep]=x,++mp[x];
	dfs(b[dep]^x,dep+1);
	--mp[x];
}
int main() {
    for(t=read();t--;){
    	vis.clear();
        n=read();
        fl=1;sum=0;
        to (i,n - 1)b[i]=read(),sum^=b[i],++vis[sum];
        to(i,n){
        	if(!fl)break;
        	if(!vis[i]){
        		dfs(i,1);
			}
		}
    }
    return 0;
}