4 条题解

  • 0
    @ 2025-3-19 16:09:06

    首先注意到每次只能操作长度为 22 的整数次幂的块,所以说假设我们排序的方案是从块长小的到块长大的来做的话,长块内的元素必须是已经排好序的,才能保证最后的序列是有序的。

    那么我们提出一个引理:单次交换两个长度为 2i12^{i-1} 的块,至多使两个长度为 2i2^i 的块从无序变成有序。

    这个证明是显然的,由于这交换的两个长为 2i12^{i-1} 的块要么处于同一个长度为 2i2^i 的块中,要么分别处于两个不同的长为 2i2^i 的块中,交换至多改变两个长为 2i2^i 的块,所以显然成立。

    于是我们就有一个可以判断无解的方法,扫一遍整个序列,如果出现了超过两个无序的长为 2i2^i 的块,那么必然无法使最后的序列变得有序。

    然后我们继续思考,假设我现在已经得到了一个合法的操作序列,那么先进行交换长块的操作并不会影响小块的有序性,所以我们可以再提出一个引理:如果某个操作序列合法,则该操作序列的全排列都合法。

    假设我们已知某个操作序列交换了 xx 次,则答案会累加 Axx=x!A_x^x=x! 次。

    这时我们又注意到,对于一次交换,至多只有两种可能使得交换合法,这一点你只需要枚举一下序列长度为 44 的时候的所有情况就能证明了,于是,对于所有的 nn 种操作,至多有 2n2^n 种合法的操作序列。

    然后我们惊人的发现 2n2^n 是可以直接爆搜出来的,于是你就切掉了这一题。

    最后我们做一下时间复杂度分析,第 ii 种操作至多会被搜到 2i12^{i-1} 次,进行第 ii 次操作需要把长度为 2ni+12^{n-i+1} 的序列扫一次,于是我们搜索的时间复杂度就为:

    $$O\left(\sum_{i=1}^n2^{i-1}\times 2^{n-i+1}\right)=O(n2^n) $$

    在本题的数据范围下跑的飞快。

    代码

    #include <iostream>
    #include <algorithm>
    #define ll long long
    #define IT int
    using namespace std;
    const ll N=(1LL<<12)+1;
    ll ans;
    IT a[13][N],n;
    void solve(ll step,ll x);
    inline void nxt(ll step,ll x,ll ps1,ll ps2,ll sp1,ll sp2){
    	if(a[step][ps1]+1==a[step][ps2]&&a[step][sp1]+1==a[step][sp2]){
    		a[step+1][ps2>>1]=(a[step][ps2]>>1);
    		a[step+1][sp2>>1]=(a[step][sp2]>>1);
    		solve(step+1,x+1);
    	}
    }
    void solve(ll step,ll x){
    	if(step==n){
    		ll ret=1;
    		for(ll i=2;i<=x;i++) ret*=i;
    		ans+=ret;
    		return;
    	}
    	ll al=(1LL<<(n-step)),cnt=0,apos=-1,bpos=-1;
    	for(ll i=1;i<=al;i+=2){
    		if(a[step][i]+1==a[step][i+1]&&(a[step][i]&1)) a[step+1][(i+1)>>1]=(a[step][i+1]>>1);
    		else{
    			cnt++;
    			if(apos==-1) apos=i;
    			else bpos=i;
    		}
    	}
    	if(cnt>2) return;
    	if(cnt==2){
    		IT ps1=apos,ps2=apos+1;
    		IT sp1=bpos,sp2=bpos+1;
    		
    		swap(a[step][ps1],a[step][sp1]);
    		nxt(step,x,ps1,ps2,sp1,sp2);
    		swap(a[step][ps1],a[step][sp1]);
    		
    		swap(a[step][ps1],a[step][sp2]);
    		nxt(step,x,ps1,ps2,sp1,sp2);
    		swap(a[step][ps1],a[step][sp2]);
    		
    		swap(a[step][ps2],a[step][sp1]);
    		nxt(step,x,ps1,ps2,sp1,sp2);
    		swap(a[step][ps2],a[step][sp1]);
    		
    		swap(a[step][ps2],a[step][sp2]);
    		nxt(step,x,ps1,ps2,sp1,sp2);
    		swap(a[step][ps2],a[step][sp2]);
    	}
    	if(cnt==1){
    		a[step+1][(apos+1)>>1]=(a[step][apos]>>1);
    		solve(step+1,x+1);
    	}
    	if(!cnt) solve(step+1,x);
    }
    int main(){
    	ios::sync_with_stdio(false);
    	cin.tie(0),cout.tie(0);
    	cin>>n;
    	for(ll i=1;i<=(1LL<<n);i++) cin>>a[0][i];
    	solve(0,0);
    	cout<<ans;
    	return 0;
    } 
    
    • -1
      @ 2025-3-20 11:29:15

      首先我们发现 nn 比较小,只有 1212 ,并且每种情况只能使用一次,那么我们考虑 2122^{12} 的爆搜

      首先我们发现如果想使用第 ii 中的改变方法,那么我们需要保证任意的从 11 开始的连续 2i12^{i-1} 的序列是连续的且相邻两个差一

      例如: 如果想使用操作 22

      7 8 5 6 1 2 4 3

      我们发现其中的 4 3 是不满足要求的,那么我们要先使用操作 11 把4 3调换过来

      以此类推,如果发现这种不合法的情况在一个序列里出现大于 22 中,那么一定不可能满足要求了,所以return

      时间复杂度 O(n×2n)O(n\times 2^n)

      代码非常不友善

      #include<bits/stdc++.h>
      #define int long long
      //#define int __int128
      #define endl "\n"
      #define mkp(a,b) make_pair(a,b)
      #define pii pair<int,int>
      //#pragma GCC optimize(2)
      #define N (1<<12)+5
      using namespace std;
      bool Test_MLE_start;
      int __=1,n,ans=0,m;
      int a[N],fac[N],t[N],p[N];
      namespace FastIO{
      //	char buf[1<<20],*p1,*p2;
      //	#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
      	template<typename T>inline void reads(T &x){
      		char c=getchar();
      		int sum=0,f=1;
      		while(!isdigit(c)){
      			if(c=='-') f=-1;
      			c=getchar();
      		}
      		while(isdigit(c)){
      			sum=(sum<<3)+(sum<<1)+(c^48);
      			c=getchar();
      		}
      		x=sum*f;
      	}
      	template<typename T>inline void reads_f(T &x){
      		char c=getchar();
      		int f=1,sum1=0,sum2=0,cnt=0;
      		double sum3=0.00;
      		while(!isdigit(c)){
      			if(c=='-') f=-1;
      			c=getchar();
      		}
      		while(isdigit(c)&&c!='.'){
      			sum1=(sum1<<3)+(sum1<<1)+(c^48);
      			c=getchar();
      		}
      		c=getchar();
      		while(isdigit(c)){
      			sum2=(sum2<<3)+(sum2<<1)+(c^48);
      			cnt++;
      			c=getchar();
      		}
      		sum3=(sum2*pow(10,-cnt)*1.0+sum1)*f;
      		x=sum3;
      	}
      	template<typename T>inline void reads_bit(T &x){
      		char c=getchar();
      		int sum=0;
      		while(!isdigit(c))c=getchar();
      		while(isdigit(c)){
      			sum=(sum<<1)+(c^48);
      			c=getchar();
      		}
      		x=sum;
      	}
      	template<typename T>inline void writes(T x){
      		if(x<0) putchar('-'),writes(-x);
      		if(x>9) writes(x/10);
      		putchar(x%10+48);
      	}
      }using namespace FastIO;
      inline void files(){
      	freopen("std.in","r",stdin);
      	freopen("std.out","w",stdout);
      }
      inline void clr(){
      //	Don't forget!
      
      }
      bool check(){
      	for(int i=1;i<=n;i++) if(t[i]!=a[i]) return 0;
      	return 1;
      }
      void changes(int l1,int r1,int l2,int r2){
      	for(int i=l1,j=l2;i<=r1,j<=r2;i++,j++) swap(a[i],a[j]);
      }
      void dfs(int idx,int cnt){
      //	cout<<idx<<" "<<cnt<<":";
      //	for(int i=1;i<=n;i++) cout<<a[i]<<" ";
      //	cout<<endl;
      //	for(int i=1;i<=m;i++) cout<<p[i]<<" ";
      //	cout<<endl;
      	if(idx>m){
      		if(check()) ans+=fac[cnt];
      		return;
      	}
      
      	int ret=0,len=(1<<(idx)),s1=0,s2=0;
      	for(int i=1;i+len-1<=n;i+=len){
      		int now=a[i];
      		for(int j=i+1;j<=i+len-1;j++){
      			if(a[j]-a[j-1]!=1){
      				ret++;
      				break;
      			}
      		}
      	}
      	if(ret>2) return;
      	for(int i=1;i+len-1<=n;i+=len){
      		int now=a[i];
      		for(int j=i+1;j<=i+len-1;j++){
      			if(a[j]-a[j-1]!=1){
      				if(!s1) s1=i;
      				else s2=i;
      				break;
      			}
      		}
      	}
      	int e1=s1+len-1,e2=s2+len-1,mid1=(s1+e1)>>1,mid2=(s2+e2)>>1;
      //	cout<<s1<<" "<<mid1<<" "<<e1<<" "<<s2<<" "<<mid2<<" "<<e2<<endl;
      	if(ret==2){
      		p[idx]=1;
      		changes(s1,mid1,s2,mid2);
      		dfs(idx+1,cnt+1);
      		changes(s1,mid1,s2,mid2);
      		p[idx]=-1;
      		
      		p[idx]=1;
      		changes(s1,mid1,mid2+1,e2);
      		dfs(idx+1,cnt+1);
      		changes(s1,mid1,mid2+1,e2);
      		p[idx]=-1;
      		
      		p[idx]=1;
      		changes(mid1+1,e1,s2,mid2);
      		dfs(idx+1,cnt+1);
      		changes(mid1+1,e1,s2,mid2);
      		p[idx]=-1;
      		
      		p[idx]=1;
      		changes(mid1+1,e1,mid2+1,e2);
      		dfs(idx+1,cnt+1);
      		changes(mid1+1,e1,mid2+1,e2);
      		p[idx]=-1;
      	}
      	else{
      		p[idx]=1;
      		changes(s1,mid1,mid1+1,e1);
      		dfs(idx+1,cnt+1);
      		changes(s1,mid1,mid1+1,e1);
      		p[idx]=-1;
      	}
      	p[idx]=0;
      	dfs(idx+1,cnt);
      	p[idx]=-1;
      }
      inline void solve(){
      //	Press your code here
      	reads(n),m=n,n=(1<<n);
      	for(int i=1;i<=m;i++) p[i]=-1;
      	fac[1]=1;
      	for(int i=2;i<=12;i++) fac[i]=fac[i-1]*i;
      	for(int i=1;i<=n;i++){
      		reads(a[i]);
      		t[i]=a[i];
      	}
      	sort(t+1,t+n+1);
      	dfs(1,0);
      	printf("%lld\n",ans);
      }
      bool Test_MLE_end;
      signed main(){
      //	printf("Memory:%lf Mb\n",(&Test_MLE_end-&Test_MLE_start-1)/1024.0/1024.0);
      //	files();
      //	reads(__);
      	cin.tie(0);cout.tie(0);
      	ios::sync_with_stdio(false);
      	while(__--) clr(),solve();
      	return 0;
      }
      
      • -2
        @ 2025-3-20 10:27:50
        #include <bits/stdc++.h>
        using namespace std;
        #define ll long long
        
        namespace syr
        {
        	const ll N = (1<<13);
        	ll n, ans;
        	ll p[15], a[N];
        	ll check (ll x) { //判断之前的是否都合法 
        		for (ll i=1; i<=(1<<(n-x)); i++) {
        			ll id = (i-1)*(1<<x)+1, len=(1<<(x-1));
        			if (a[id]+len != a[id+len]) return 0;
        		}
        		return 1;
        	}
        	void swapp (ll x, ll y, ll k) { //换以x开头的块和以y开头的块,长度为k 
        		for (ll i=1; i<=k; i++)
        			swap(a[x+i-1], a[y+i-1]);
        	}
        	void dfs (ll x, ll sum) {
        		if (x && !check(x)) return;
        		if (x==n) {
        			ans += p[sum];
        			return;
        		}
        		dfs(x+1, sum); //不换 
        		ll tmp[5], tot=0;
        		for (ll i=1; i<=(1<<(n-x)); i+=2) { //判断有多少需要换的 
        			ll id = (i-1)*(1<<x)+1;
        			if (a[id]+(1<<x) == a[id+(1<<x)]) continue;
        			if (tot==4) return;
        			tmp[++tot] = i;
        			tmp[++tot] = i+1;
        		}
        		if (!tot) return; //不需要换 
        		for (ll i=1; i<=tot; i++) {
        			for (ll j=i+1; j<=tot; j++) {
        				ll idi = (tmp[i]-1)*(1<<x)+1;
        				ll idj = (tmp[j]-1)*(1<<x)+1;
        				swapp(idi, idj, (1<<x));
        				dfs(x+1, sum+1); //换 
        				swapp(idi, idj, (1<<x));
        			}
        		}
        	}
        	void work()
        	{
        		p[0] = 1;
        		for (ll i=1; i<=12; i++) //阶乘,次数为i次的全排列 
        			p[i] = p[i-1]*i;
        		cin>>n;
        		for (ll i=1; i<=(1<<n); i++) cin>>a[i];
        		dfs(0, 0);
        		cout<<ans<<'\n';
        	}
        }
        
        int main()
        {
        	cin.tie(0)->sync_with_stdio(0);
        	syr::work();
        	return 0;
        }
        
        • -2
          @ 2025-3-20 8:45:30

          难绷,考场上想当然了,痛失 30pts30pts

          首先大洋里和小阳历都是阶乘,然后我就开始猜,是不是操作的顺序没有影响,然后好像确实是的。

          于是我们就只要判断每种操作需不需要进行即可。

          然后我就开始想如何判断每种操作可不可行。我们可以考虑从小到大枚举操作种类,然后我们每一次去考虑在里面选择两个进行交换,换完了以后一定任意两个 2k12k-1,2k2k 都是相邻递增的两个数,然后我们把它们浓缩成一个数字,递归进行即可。

          我们看看每一次需不需要进行交换,如果需要我们就统计一下,到了最后浓缩成一个数字时答案就加上这个东西的阶乘即可。

          然后我赛场上认为它答案一定是阶乘,于是就只写了最后统计一次,痛失 30pts30pts 。其实我们每一次搜索到一个答案进行统计就对了。

          我做这个题做得很感性,大家可以参考 xixisuperxixisuper 的数学证明。

          代码很丑,仅供参考。

          #include<bits/stdc++.h>
          using namespace std;
          int a[20][100005],b[100005],ans;
          long long p[205];
          inline void nextt(int depth,int lenth){
          	for(int i=1; i<=lenth-1; i+=2){
          		a[depth+1][i/2+1]=a[depth][i]/2+1;
          	}
          }int cnt=0;
          long long res=0;
          bool dfs(int depth,int lenth){
          	if(lenth==1){
          		res+=p[ans];
          		return 1;
          	}
          	int cnt=0;
          	vector<int>vec;
          	for(int i=1; i<=lenth-1; i+=2){
          		if(a[depth][i]!=a[depth][i+1]-1){
          			vec.push_back(i);
          		}
          	}
          	if(vec.size()>2)return 0;
          	if(vec.size()==1){
          		for(int i=1; i<=lenth-1; i+=2){
          			if(i!=vec[0])a[depth+1][i/2+1]=a[depth][i]/2+1;
          			else a[depth+1][i/2+1]=a[depth][i+1]/2+1;
          		}
          		return dfs(depth+1,lenth>>1);
          	}else if(vec.size()==2){
          		for(int s=0; s<4; s++){
          			swap(a[depth][vec[0]+(s&1)],a[depth][vec[1]+(s/2)]);
          			if(a[depth][vec[0]]+1==a[depth][vec[0]+1]&&a[depth][vec[1]]+1==a[depth][vec[1]+1]){
          				nextt(depth,lenth);
          				dfs(depth+1,lenth>>1);
          			}swap(a[depth][vec[0]+(s&1)],a[depth][vec[1]+(s/2)]);
          		}return 0;
          	}else{
          		ans--;
          		nextt(depth,lenth);
          		dfs(depth+1,lenth>>1);ans++;
          		return 0;
          	}
          }
          int main(){
          	int n;scanf("%d",&n);ans=n;
          	p[0]=1;
          	for(int i=1; i<=n; i++)p[i]=p[i-1]*1ll*i;
          	for(int i=1; i<=(1<<n); i++)scanf("%d",&a[0][i]);
          	dfs(0,(1<<n));
          	cout<<res;
          	return 0;
          } 
          
          
          • 1

          信息

          ID
          92
          时间
          1000ms
          内存
          256MiB
          难度
          7
          标签
          (无)
          递交数
          34
          已通过
          9
          上传者