cf1208 D Restore Permutation (二分+树状数组)
题意
让你构造一个长度为n的序列,记为p1……pn,(这个序列是1~n的全排列的一种)
给你n个数,记为s1……sn,si的值为p1……pi-1中小于pi的数的和。
思路
显然,应该倒着来,也就是从pn 开始构造,这样的话,当要填pi 的时候,p1到pi-1就是所有的还未填的数,那么我们只需要去找哪个前缀和符合就可以了。
比如:现在还没填进去的数是 1,2,3,4,5
而我现在的si 是6,那么,对应的pi 就是4,因为这样无论1,2,3,5怎么排,都符合si 为6
(才学疏浅,不怎么会讲)
那么,选完要删除,所以要修改前缀和,考虑用树状数组,查找哪个符合,自然是二分。
代码
#include <stdio.h> #include <queue> #include <string> #include <string.h> #include <algorithm> #include <math.h> #include <set> using namespace std; typedef long long int ll; const int maxn = 2e5 + 10; const int inf = 70000000; const ll mod = 998244353; const ll seed = 131; ll s[maxn],c[maxn],ans[maxn],n; int lowbit(int x) { return x & (-x); } void insert(ll x,int i) { while(i <= n){ c[i] += x; i += lowbit(i); } } ll getsum(int i) { ll sum = 0; while(i > 0){ sum += c[i]; i -= lowbit(i); } return sum; } int vis[maxn]; int query(ll x) { int l,r,mid; ll res; l = 0;r = n; while(l <= r){ mid = (l + r) >> 1; res = getsum(mid); if(res == x){ if(vis[mid + 1]) l = mid + 1; else return mid; } else if(res < x){ l = mid + 1; } else{ r = mid - 1; } } } int main() { while(scanf("%d",&n) != EOF){ memset(vis,0,sizeof(vis)); memset(c,0,sizeof(c)); for(int i = 1;i <= n;i++){ scanf("%lld",&s[i]); insert(i,i); } for(int i = n;i >= 1;i--){ ans[i] = query(s[i]) + 1; vis[ans[i]] = 1; insert(-ans[i],ans[i]); } for(int i = 1;i <= n;i++) printf("%d ",ans[i]); puts(""); } return 0; }