zoukankan      html  css  js  c++  java
  • Lost Cow

    1~n,乱序排列,告诉每个位置的前面的数字中比它小的数的个数,求每个位置的数字是多少
    Sample Input
    5 //五头牛
    1 //对于第2头牛来说,前面有1头比它小
    2
    1
    0
    Sample Output
    2
    4
    5
    3
    1

    Sol:查找第a[i]+1小的数字,可以权值线段树或树状数组.

    下面这个是暴力程序,注意我们要选择的那个位置必须是“1”,代表尚未使用过的。。这是整个全篇所有程序的要点。。。

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    int a[8010],f[8010],ans[8010];
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=2;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=n;i>=1;i--)
        {
            int sum=0;
            for(int j=1;j<=n;j++)
            {
                if(!f[j])sum++;
                if(sum==a[i]+1)
                {
                    ans[i]=j;
                    f[j]=1;
                    break;
                }
            }
        }
        for(int i=1;i<=n;i++)
            printf("%d
    ",ans[i]);
        return 0;
    }
    

      下面是树状数组,二分位置的。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int a[10000],c[10000],h[10000],n;
    
    int ask(int x)
    {
    	int ans=0;
    	for(;x;x-=x&-x) ans+=c[x];
    	return ans;
    }
    
    void add(int x,int y)
    {
    	for(;x<=n;x+=x&-x) c[x]+=y;
    }
    
    int query(int x)
    {
    	int l=1,r=n,loc=n;
    	while(l<=r) //二分查找位置
    	{
    		int mid=(l+r)/2;
    		if(ask(mid)==x) 
    		   {
    		    	if (mid<loc)
    			        loc=mid;
    			    r=mid-1;
    					
    			}
    		else
    	     	if (ask(mid)<x)
    	            l=mid+1; 
    	        else 
    		         r=mid-1;
    	}
    //	cout<<x<<"          "<<loc<<endl;
    	return loc;
    }
    
    int main()
    {
    	cin>>n;
    	for(int i=2;i<=n;i++) scanf("%d",&a[i]);
    //	a[1]=0;
    	memset(c,0,sizeof(c));
    	for(int i=1;i<=n;i++) add(i,1);
    	for(int i=n;i;i--)
    	{
    	    h[i]=query(a[i]+1);
    	    add(h[i],-1);
    	}
    	for(int i=1;i<=n;i++) cout<<h[i]<<endl;
    	//system("pause");
    }
    

    不用二分的话,直接在Bit中找第K小的,并且位置要尽量靠左时,

    不能直接去找,而是应该找第 K-1小的,然后位置再后移动1位。

    因为在BIT中是先加大区间,再加小区间,例如我们对1100这一段,我们要找到前缀和为2的,其实前2个数字的前缀和就为2了

    但在BIT中会先加区间为4的。

     

      假设给定数列如下:1111111100110011

    STEP 1:保证找到一个位置,其前缀为a[i],并且这个位置越靠后越好,也就是说再多1位,总和就超过a[i]了,如果这个位置靠前的话,则后面一位可能是0,再多加1位,也不能超过a[i]。

    为什么不能跟从前一样直接找第一个位置,其前缀和为a[i]+1呢?

    因为我们加的区间是从大到小,例如0010,前四个数字之和为1,前3个数字之和也为1.

    用现在这种倍增的加法,无法保证找到最靠前的位置。


    设a[i]=10
    此时我们找的ans是最靠后一个位置,其前缀的为10
    此时会找到倒数第3个位置。
    程序是这样做到的
    先试图去加c[16]=12,发现不能加
    先试图去加c[8]=8,发现能加,就加上
    再试图去加第8个位置后面连续4个数字即c[12]=2,发现能加,就加上
    再试图去加第12个位置后面连续2个数字,c[14]=0,发现能加,也加上
    再加图去加第14个位置后面连续1个数字,c[15]=1,发现不能加了,
    于是第14位就是我们要的,其数字和为10.

    整个过程跟求Lca倍增法非常类似

    如果是从前二分找位置的话,找的是某个位置其前缀和为11.
    这个位置是第一个前缀和为11的,即倒数第2个位置


    于是我们此时找到的结果要加1

    这个条件ans + (1<<p) <=n
    STEP 2:
    是保证要加的数字个数<=n,例如n=14时
    我们让其先加8个,再加4个,2个,1个。这样是可以加过头的

    也有可能N=16

    我们一开始就加了16,然后再加的位置就是C[24]了,这个位置根本是不存在的。

    #include<bits/stdc++.h> 
    using namespace std;       
     
    inline void read(int &x)
    {
    int k=0;char f=1;
    char c=getchar();
    for(;!isdigit(c);
    c=getchar())
    if(c=='-')
    f=-1;
    for(;isdigit(c);
    c=getchar())
    k=k*10+c-'0';
    x=k*f;
    }
    
    const int maxn=1e5+34;
     
    int n;
    int a[maxn],b[maxn],c[maxn*2],h[maxn];
     
    int lowbit(int x){return x&-x;}
    int ask(int x){
        int ans=0;
        for(;x;x-=lowbit(x))ans+=c[x];
        return ans;
    }
    void add(int x,int y){
        for(;x<=n;x+=lowbit(x))c[x]+=y;
    }
     
    int main(){
        scanf("%d",&n);
        for(int i=2;i<=n;i++)
    	  {
    	          scanf("%d",&a[i]);
    	  }
        for(int i=1;i<=n;i++)
    	   {
    	         b[i]=1;
    			 add(i,1);
    	   }
    			
             
        int lim=(int)log2(n);
        for(int i=n;i>=1;i--)
        {
            int ans=0,sum=0;
            
            for(int p=lim;p>=0;p--)
            {
                if(ans + (1<<p) <=n && sum+c[ans+(1<<p)]<=a[i])
    
                //ans+1<<p代表要加哪一段的数字
    	    {
                    sum+=c[ans+(1<<p)];
                    ans+=(1<<p);
                }
            }
            h[i]=ans+1;
            add(ans+1,-1);
        }
        for(int i=1;i<=n;i++)printf("%d
    ",h[i]);
     
    }
    

      

     

      

     

      

    #include<bits/stdc++.h>
    using namespace std;
    int n,a[8010],ans[8010];
    int sum[80010];
    void insert(int p,int l,int r,int x,int val) 
    {
    if(l==r) 
    {
        sum[p]+=val;
        return;
    }
    int mid=(l+r)/2;
    if(x<=mid) 
       insert(p*2,l,mid,x,val);
    else 
        insert(p*2+1,mid+1,r,x,val);
    sum[p]=sum[p*2]+sum[p*2+1];
    }
    int kth(int p,int l,int r,int x) 
    {
    if(l==r) return l;
    int mid=(l+r)/2;
    if(sum[p*2]<x) 
       return kth(p*2+1,mid+1,r,x-sum[p*2]);
    else 
       return kth(p*2,l,mid,x);
    }
    int main() 
    {
    scanf("%d",&n);
    for(int i=2;i<=n;i++) 
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) 
       a[i]++;
    for(int i=1;i<=n;i++) 
        insert(1,1,n,i,1);
    for(int i=n;i>=1;i--) 
    {
    ans[i]=kth(1,1,n,a[i]);
    insert(1,1,n,ans[i],-1);
    }
    for(int i=1;i<=n;i++) 
        printf("%d
    ",ans[i]);
    }
    

      

     这个程序,更好看一点吧。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int maxn = 8e3 + 10;
    int tree[maxn*8], p[maxn], res[maxn];
    
    void build(int l, int r, int dex)
    {
    	if(l == r) tree[dex] = 1;
    	else{
    		int mid = (l+r)>>1;
    		build(l, mid, dex*2), build(mid+1, r, dex*2+1);
    		tree[dex] = tree[dex*2] + tree[dex*2+1];
    	}
    }
    int query(int l, int r, int dex, int k)
    {
    	if(l == r) return l;
    	int mid = (l+r)>>1;
    	if(k <= tree[dex*2]) return query(l, mid, dex*2, k);
    	else return query(mid+1, r, dex*2+1, k - tree[dex*2]);
     } 
    void update(int l, int r, int dex, int x)
    {
    	if(l <= x && r >= x)
    	{
    		tree[dex]--;  //这个区间的数字个数要减少一个
    		if(l != r)
    		{
    			int mid = (l+r)>>1;
    			update(l, mid, dex*2, x), update(mid+1, r, dex*2+1, x);
    		}
    	}
     } 
    
    int main()
    {
    	int n;
    	while(scanf("%d", &n) != EOF){
    		for(int i = 2; i <= n; i++) scanf("%d", &p[i]);
    		p[0] = 0;
    		build(1, n, 1);
    		for(int i = n; i; i--){
    			res[i] = query(1, n, 1, p[i]+1);
    			update(1, n, 1, res[i]);
    		}
    		for(int i = 1; i <= n; i++) printf("%d
    ", res[i]);		
    	}
    }
    

      

  • 相关阅读:
    C#-------------枚举
    C#-------------类型构造器
    C#-内存天下
    线程在C#中的使用
    C#遗忘笔记--品味类型
    二分查找算法(C#实现)
    Linq 表达式树
    javascript中的对象
    this绑定的顺序
    弹出对话框
  • 原文地址:https://www.cnblogs.com/cutemush/p/13330421.html
Copyright © 2011-2022 走看看