4 条题解
-
0
首先注意到每次只能操作长度为 的整数次幂的块,所以说假设我们排序的方案是从块长小的到块长大的来做的话,长块内的元素必须是已经排好序的,才能保证最后的序列是有序的。
那么我们提出一个引理:单次交换两个长度为 的块,至多使两个长度为 的块从无序变成有序。
这个证明是显然的,由于这交换的两个长为 的块要么处于同一个长度为 的块中,要么分别处于两个不同的长为 的块中,交换至多改变两个长为 的块,所以显然成立。
于是我们就有一个可以判断无解的方法,扫一遍整个序列,如果出现了超过两个无序的长为 的块,那么必然无法使最后的序列变得有序。
然后我们继续思考,假设我现在已经得到了一个合法的操作序列,那么先进行交换长块的操作并不会影响小块的有序性,所以我们可以再提出一个引理:如果某个操作序列合法,则该操作序列的全排列都合法。
假设我们已知某个操作序列交换了 次,则答案会累加 次。
这时我们又注意到,对于一次交换,至多只有两种可能使得交换合法,这一点你只需要枚举一下序列长度为 的时候的所有情况就能证明了,于是,对于所有的 种操作,至多有 种合法的操作序列。
然后我们惊人的发现 是可以直接爆搜出来的,于是你就切掉了这一题。
最后我们做一下时间复杂度分析,第 种操作至多会被搜到 次,进行第 次操作需要把长度为 的序列扫一次,于是我们搜索的时间复杂度就为:
$$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
首先我们发现 比较小,只有 ,并且每种情况只能使用一次,那么我们考虑 的爆搜
首先我们发现如果想使用第 中的改变方法,那么我们需要保证任意的从 开始的连续 的序列是连续的且相邻两个差一
例如: 如果想使用操作
7 8 5 6 1 2 4 3
我们发现其中的 4 3 是不满足要求的,那么我们要先使用操作 把4 3调换过来
以此类推,如果发现这种不合法的情况在一个序列里出现大于 中,那么一定不可能满足要求了,所以return
时间复杂度
代码非常不友善
#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
#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
难绷,考场上想当然了,痛失 。
首先大洋里和小阳历都是阶乘,然后我就开始猜,是不是操作的顺序没有影响,然后好像确实是的。
于是我们就只要判断每种操作需不需要进行即可。
然后我就开始想如何判断每种操作可不可行。我们可以考虑从小到大枚举操作种类,然后我们每一次去考虑在里面选择两个进行交换,换完了以后一定任意两个 , 都是相邻递增的两个数,然后我们把它们浓缩成一个数字,递归进行即可。
我们看看每一次需不需要进行交换,如果需要我们就统计一下,到了最后浓缩成一个数字时答案就加上这个东西的阶乘即可。
然后我赛场上认为它答案一定是阶乘,于是就只写了最后统计一次,痛失 。其实我们每一次搜索到一个答案进行统计就对了。
我做这个题做得很感性,大家可以参考 的数学证明。
代码很丑,仅供参考。
#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
- 上传者