0504 T3:
題目描述:
題目分析:
因爲這個題只需要在限制次數內排好,而不要求最小次數,所以我們要通過上面這個方式構造出一個可行解。
記每個數字 對應上面的位置爲 。爲了方便理解,我們先用比較簡單的情況舉例:
待排序的序列爲:
我們想要把它排成:,這樣只需要將放到前面,放到後面就排好了。
我們求出每個數字在想要的排列中對應的數組: (對於奇數,用減去原本的,後面會解釋)
首先將偶數放到前面,奇數放到後面,,得到:
然後按照的二進制表示從低位到高位基數排序:
第一輪:
對於的,則,否則。並把對應的偶數放到後面,奇數放到前面。
要移動的偶數爲,要移動的奇數爲。得到:
然後將偶數放到前面,奇數放到後面,得到:
這一輪的效果是:偶數奇數已經分別按照最終rank的二進制第0位有小到大排好了!
第二輪:
對於的,則,否則。並把對應的偶數放到後面,奇數放到前面。
要移動的偶數爲,要移動的偶數爲。得到:
然後將偶數放到前面,奇數放到後面,得到:
然後就發現已經得到我們想要的排列了!
這樣做的原理是:對於偶數的 我們將它放到右邊,然後再按順序放回來,相當於就是基數排序。並且可以解釋爲什麼上面要令7減去奇數的,這樣原來爲0的就會被放到左邊。
這裏有一個細節,就是必須要保證每次奇偶rank對應位等於1的個數是相同的,並且數組的求得也有點講究(要使的最後能一步排好),總共有種情況分類討論,可以列舉來看一看。
二進制位總共是位,最開始要奇偶分開,最後加1步,總共步。
基數排序真是太強辣!
Code:
#include<bits/stdc++.h>
#define maxn 15005
using namespace std;
int T,n,m,rk[maxn],a[maxn];
void put(string s,bool t){
printf("%d %s\n",t,s.c_str());
vector<int>x[2];
for(int i=0;i<n;i++) if(s[i]=='1') x[(a[i]&1)^t].push_back(a[i]);
x[0].insert(x[0].end(),x[1].begin(),x[1].end());
for(int i=0,j=0;i<n;i++) if(s[i]=='1') a[i]=x[0][j++];
}
int main()
{
for(scanf("%d",&T);T--;){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
puts("28");
int k=(n+1)/2,x=0;
for(int j=0;x<k;j+=2,x+=2) rk[x]=j;
for(int j=1;x<n;x+=2,j+=2) rk[x]=j;
x=(n|1)-2;
for(int j= n&1 ? 2 : 0; x>=k ; j+=2,x-=2) rk[x]=j;
for(int j=1;x>0;j+=2,x-=2) rk[x]=j;
put(string(n,'1'),0);
for(int i=0;i<13;i++){
string s(n,'0');
for(int j=0;j<n;j++) if(rk[a[j]]>>i&1) s[j]='1';
put(s,1);
put(string(n,'1'),0);
}
string s(n,'0');
for(int i=0;i<n;i++) if(a[i]!=i) s[i]='1';
put(s,1);
}
}
0505 T2
題目描述:
題目分析:
模仿上面的做法,很容易讓人想到基數排序,但是這道題要求最少的操作次數,subtask一下基數排序就會獲得0分的好成績(比如我)。
首先我們要證明答案的下界,然後構造達到這個下界的操作方法。
令,即,把中極長的連續、遞增的區間稱之爲一段。考慮任意兩段的最後一個數,我們可以證明它們的操作序列不同(在某一輪中)。
使用反證法,若那麼最後在之後顯然不行。若則存在使得,此時無論怎麼操作都不能變到和之間。得證
假設存在段,則至少存在種不同的操作序列,如果答案爲,則,這給出了答案的下界。
爲了構造得到這個下界,每次操作中把段從左到右編號(從開始),並選出所有奇數段放到偶數段之
前,這樣第 段和第 段合併,於是段數變爲,不斷這麼做就構造完了。具體實現時在第輪選擇所在段的二進制減1後第位爲0的數令它的即可,可以看出操作次數恰爲。
Code(stable_partition表示將一個序列中布爾表達式爲0的數放到左邊,爲1的放到右邊,並且同一邊的不改變原來的相對位置):
#include<bits/stdc++.h>
#define maxn 50005
using namespace std;
int n,T,a[maxn],q[maxn],id[maxn],ans,d;
bool cmp(int i){return (id[i]>>d&1)==0;}
void write(){for(int i=1;i<=n;i++) printf("%d%c",a[i],i==n?10:32);}
int main()
{
scanf("%d%d",&n,&T);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),q[a[i]]=i;
int k=0;
for(int i=1;i<=n;i++) id[i]=q[i]>q[i-1]?k:++k;
for(ans=0;1<<ans<=k;ans++);
printf("%d\n",ans);
if(T){
write();
for(d=0;d<ans;d++) stable_partition(a+1,a+1+n,cmp),write();
}
}