題目描述:
有1~n的數,已知每乙個位置的逆序對,求原序列。
輸入格式:
第一行輸入乙個正整數n。
第二行輸入n個正整數,表示pi(逆序對數字首和)
輸出格式:
輸出一行,共有n個數,表示原排列ai
資料範圍:
對於前10%的資料:n<=10。
對於前30%的資料:n<=1000。
對於100%的資料:n<=100000。
解析:
一題多解,共有三種方法:二分+樹狀陣列o(n (log n)^2)、區間線段樹o(n log n),權值線段樹(n log n)。
先說說題解做法(複雜度反而最高.....)二分+樹狀陣列。首先做差得到每乙個位置的逆序對數。我們考慮正著從第乙個位置來做會很複雜,於是我們就可以從第n個位置往前處理,確定最後乙個數後倒著確定每乙個數。我們先把每乙個數個數賦為1,存入樹狀陣列。對於每乙個位置,我們二分符合條件的數,然後用將這個數的個數賦為0,用樹狀陣列做單點修改,這樣以後就不會再用這個數。
權值線段樹的做法跟題解做法一樣,不斷找第ki大的數,只是用權值線段樹會少乙個log,複雜度更低。
區間線段樹的做法是找規律,我們不難發現每次都是將最大的數放入當前數列中最後乙個逆序對數為0的位置,於是我們就可以用線段樹尋找最後乙個對數為0的位置,將它修改為inf,再將該位置後面的位置的對數全部減1,這樣就不用再考慮這個位置,然後重複這個操作。
** (二分+樹狀陣列)
#include using namespace std;
const int inf=1e7;
const int max=101000;
int n,pos;
int s[max];
int num[max],sum[max];
int ans[max],way[max];
int tree[max];
inline int get_int()
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}inline int lowbit(int x)
inline void add(int x,int num)
}inline int search(int pos)
return s;
}int main()
;shu tree[max*4];
inline int get_int()
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}inline void build(int root,int l,int r)
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
}inline void q(int root,int l,int r,int id)
int mid=(l+r)>>1;
if(tree[root<<1|1].sum>=id&&tree[root<<1|1].sum>0) q(root<<1|1,mid+1,r,id);
else if(tree[root<<1].sum!=0) q(root<<1,l,mid,id-tree[root<<1|1].sum);
tree[root].sum=tree[root<<1].sum+tree[root<<1|1].sum;
}int main()
for(register int i=1;i<=n;i++) cout<#include using namespace std;
const int inf=1e7;
const int max=101000;
int n,pos;
int add[max*4],s[max];
int num[max],sum[max];
struct shu;
shu tree[max*4];
inline int get_int()
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}inline void build(int root,int l,int r)
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
tree[root].minn=min(tree[root<<1].minn,tree[root<<1|1].minn);
}inline void pushdown(int root)
inline void q(int root,int l,int r)
pushdown(root);
int mid=(l+r)>>1;
if(tree[root<<1|1].minn==0) q(root<<1|1,mid+1,r);
else q(root<<1,l,mid);
tree[root].minn=min(tree[root<<1].minn,tree[root<<1|1].minn);
}inline void change(int root,int l,int r,int l,int r)
int mid=(l+r)>>1;
if(l<=mid) change(root<<1,l,mid,l,r);
if(r>mid) change(root<<1|1,mid+1,r,l,r);
tree[root].minn=min(tree[root<<1].minn,tree[root<<1|1].minn);
}int main()
for(register int i=1;i<=n;i++) cout
}
NOIP模擬(11 03)T2 排列
排列 題目背景 11.03 noip 模擬t2 分析 線段樹 唯一一道可做題 我們從n 1 來確定每個數的位置,顯然,我們放入 n的時候,他一定在乙個逆序對數為 0的位置,因為它的前面不可能有比它大的,其次,如果 0位有多個,它一定在最後乙個 0位上,因為如果在前面的 0位上,後面的 0位上的數字一...
2020 9 3 NOIP模擬賽 T1 排列
題目鏈結 這是noip良心模擬賽的第一題 求對於任意的 i 滿足 p i i not k 的排列數。n,k le 10 5 首先 k 0 直接錯排數。k 1 考慮容斥。假設我們能算出來 g i 表示欽定 i 個位置不合法的方案數,那麼答案應該是 sum n 1 ig i n i 注意不是二項式反演,...
NOIP校內模擬 T1 排列樹(樹上的組合數)
假設當前節點now的子樹大小為size now的方案數是他的所有兒子內部如何分配的方案數相乘得到的 這個可以遞迴計算 不過對於那麼多兒子之間 他們分配走的標號可能是不同的 比如now將把2,3,4,5分配給他的子樹,那有可能是2,3 4,5 也有可能是2,4 3,5這樣分 所以還得套個組合數 c s...