ACM Steps_Chapter Five_Section3

KiKi's K-Number

/*
树状数组
这道题的操作有三种,插入和删除并到一个函数里完成 ,查询的操作要用到二分的方法。
*/
#include <cstdio>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int M = 100005;
int num[M];
int lowbit( int x )
{
    return x&(-x);    
}

void add( int x , int val )
{
    while( x < M )
    {
           num[x] += val;
           x += lowbit(x);
    }
}



int sum( int x )
{
    int ret = 0;
    while( x > 0 )
    {
           ret += num[x];
           x -= lowbit(x);
    }
    return ret;
}

int find( int pos , int k )
{
    int neww = sum(pos);
    int l , r , m;
    l = pos + 1;
    r = M - 1;
    int ans = M;
    int total = 0;
    while( l <= r )
    {
           m = ( l+r ) / 2;
           total = sum(m) - neww;
           if( total >= k )
           {
               r = m - 1;
               if( m < ans )
                   ans = m;
           }
           else
               l = m + 1;
    }
    return ans;
}

int main( )
{
    int n;
    int c,x,y;
    while( scanf("%d",&n) != EOF )    
    {
           memset( num , 0 , sizeof(num) );
           while( n-- ){
                  scanf("%d",&c);
                  if( c == 0 ){
                      scanf("%d",&x);
                      add( x , 1 ); 
                  }
                  else if( c == 1 ){
                       scanf("%d",&x);
                       if( sum(x) - sum(x-1) == 0 )
                           printf("No Elment!\n");
                       else
                           add( x , -1 );
                  }
                  else if( c == 2 ){
                       scanf("%d%d",&x,&y);
                       int ret = find( x , y );
                       if( ret == M )
                           printf("Not Find!\n");
                       else
                           printf("%d\n",ret);
                  }
           }
               
    }
    return 0;
}

敌兵布阵

#include<cstdio>
#include<iostream>

using namespace std;

#define maxn 50005
int tree[maxn<<2];

void build(int left,int right,int root)
{
    if (left == right)
    {
        scanf("%d",&tree[root]);
        return ;
    }
    int mid = (left + right) >> 1;
    build(left , mid , root << 1);
    build(mid + 1 , right , root << 1 | 1);
    tree[root]=tree[root<<1]+tree[root<<1|1];
}
int find(int from,int to,int left,int right,int root)
{
    if(from<=left&&right<=to)
    {
        return  tree[root];
    }
    int mid=(left+right)>>1;
    int sum=0;
    if(from<=mid)sum+=find(from,to,left,mid,root<<1);
    if(to>mid)sum+=find(from,to,mid+1,right,root<<1|1);
    return sum;
}

void update(int p,int add,int left,int right,int root)
{
    if(left==right)
    {
        tree[root]+=add;
        return ;
    }
    int mid=(left+right)>>1;
    if(p<=mid)update(p,add,left,mid,root<<1);
    else update(p,add,mid+1,right,root<<1|1);
    tree[root]=tree[root<<1]+tree[root<<1|1];
}

int main()
{
    //freopen("in.txt","r",stdin);
    int cas;
    char s[10];
    scanf("%d",&cas);
    for(int i=1;i<=cas;i++)
    {
        int n;
        printf("Case %d:\n",i);
        scanf("%d",&n);
        build(1,n,1);
        while(scanf("%s",s))
        {
            int x,y;
            if(s[0]=='E')break;
            scanf("%d%d",&x,&y);
            if(s[0]=='A')update(x,y,1,n,1);
            else if(s[0]=='Q')printf("%d\n",find(x,y,1,n,1));
            else update(x,-y,1,n,1);
        }
    }
    return 0;
}

Median Filter

/*
我们按“S”型枚举每一个点,如左图:即右->下->左->下->右....

用一个集合存储当前子正方形内的数,下一次查找时,插入新的一列,删除旧的列。
如右图:当前枚举点150(三角标记),黑色正方形的值保存一个集合中,求出中位数。
当枚举的点有150移动到125时,我们把蓝色椭圆内的点插入,把黄色椭圆内的点删掉,
此时集合中的元素即为红色正方形内的元素,可求出新的中位数。不断如此操作,直到结束。

首先分析一下复杂度:子正方形边长L=2*r+1,N=L*L枚举每个点,需要(500-L)* (500-L)

       下面主要的问题是如何快速求中位数?

       如果每次排序找中位数,需要Nlog(N),复杂度为:(500-L)* (500-L)* Nlog(N),
	   9<=N<250000,这么大的复杂度会超时。所以我们在查中位数时尽量降低复杂度。

       树状数组和线段树都可以实现查找第k大数。线段树插入、查找均为log(N),
	   树状数组取决于二进制中1的个数,所以实际比log(N)还要快。

       若用线段树,每次O(L*lgN)的插入,O(L*lgN)的删除,O(lgN)的查找。但写完后超时,
	   好像线段树系数很大,实现的时候不能完全达到log

       用树状数组,每次O(L *lgN)的插入,O(L*lgN)的删除,O((lgN)^2)的查找,
	   C++1.5秒可过。在查找时如果不用二分查找,也可优化为O(lgN)的查找,效率会更高。
	   */
	   #include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int rr, c[1111111];
int lowbit(int x)
{
    return x & (-x);
}
void add(int i, int val)
{
    while(i <= rr){
        c[i] += val;
        i += lowbit(i);
    }
}
int sum(int i)
{
    int s = 0;
    while(i > 0){
        s += c[i];
        i -= lowbit(i);
    }
    return s;
}
int mat[555][555];
int ans[555][555];
int mm;
int Bin()
{
    int l = 1, r = rr;
    while(l < r){
        int m = (l + r) >> 1;
        if(sum(m) >= mm) r = m;
        else l = m + 1;
    }
    return l - 1;
}

int main()
{
    //freopen("input.txt", "r", stdin);
    //freopen("output.txt", "w", stdout);
    int n, r;
    while(scanf("%d %d", &n, &r) == 2){
        if(!n && !r) {
            break;
        }
        r = (r << 1) + 1;
        mm = (r * r + 1) >> 1;
        rr = -1;
        for(int i = 0; i < n; i++)
            for(int j = 0; j < n; j++){
                scanf("%d", &mat[i][j]);
                rr = max(rr, mat[i][j] + 1);
            }
        memset(c, 0, sizeof(c));
        for(int i = 0; i < r; i++){
            for(int j = 0; j < r - 1; j++){
                add(mat[i][j] + 1, 1);
            }
        }
        for(int i = 0; ; ){
            for(int j = r - 1; j < n; j++){
                for(int k = 0; k < r; k++)
                    add(mat[i + k][j] + 1, 1);
                ans[i][j - (r - 1)] = Bin();
                for(int k = 0; k < r; k++)
                    add(mat[i + k][j - (r - 1)] + 1, -1);
            }
            for(int j = n - 1; j >= n - (r - 1); j--)
                add(mat[i][j] + 1, -1);
            i++;
            if(i + (r - 1) >= n) break;
            for(int j = n - 1; j >= n - (r - 1); j--)
                add(mat[i + (r - 1)][j] + 1, 1);

            for(int j = n - r; j >= 0; j--){
                for(int k = 0; k < r; k++)
                    add(mat[i + k][j] + 1, 1);
                ans[i][j] = Bin();
                for(int k = 0; k < r; k++)
                    add(mat[i + k][j + (r - 1)] + 1, -1);
            }
            for(int j = 0; j < (r - 1); j++)
                add(mat[i][j] + 1, -1);
            i++;
            if(i + (r - 1) >= n) break;
            for(int j = 0; j < (r - 1); j++)
                add(mat[i + (r - 1)][j] + 1, 1);
        }
        for(int i = 0; i <= n - r; i++){
            for(int j = 0; j <= n - r; j++){
                //if(j) printf(" ");
                printf("%d ", ans[i][j]);
            }
            printf("\n");
        }
    }
    return 0;
}

How far away ?

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define MAX 40000

struct edge{
	int v,w;
};
vector<edge> mp[MAX];
vector<edge> query[MAX];
bool flag[MAX];
int pre[MAX],father[MAX],path[MAX];

int find(int x){
	return x==pre[x]?	x:pre[x]=find(pre[x]);
}

void LCA(int k)
{
	int i,j;
	
	for(i=0;i<mp[k].size();i++)
	{
		int a=mp[k][i].v;
		if(!flag[a])
		{
			flag[a]=1;
			path[a]=path[k]+mp[k][i].w;
			LCA(a);
			pre[a]=k;
			for(j=0;j<query[a].size();j++)
			{
				int b=query[a][j].v;
				if(flag[b]&&father[query[a][j].w]==-1)
				{
					if(a==b)	father[query[a][j].w]=0;
					else	father[query[a][j].w]=path[a]+path[b]-2*path[find(b)];
				}
			}
		}
	}
}


int main()
{
	int i,j,k,T,n,m,a,b;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		
		for(i=1;i<=n;i++){
			mp[i].clear();
			query[i].clear();
			flag[i]=0;
			father[i]=-1;
			pre[i]=i;
			path[i]=0;
		}
		
		int a,b,c;
		edge X;
		for(i=1;i<n;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			X.v=b;X.w=c;
			mp[a].push_back(X);
			X.v=a;
			mp[b].push_back(X);
		}
		for(i=1;i<=m;i++)
		{
			scanf("%d%d",&a,&b);
			X.v=b;X.w=i;
			query[a].push_back(X);
			X.v=a;
			query[b].push_back(X);
		}
		flag[1]=1;
	//	path[1]=0;		
		LCA(1);
		for(i=1;i<=m;i++)
			printf("%d\n",father[i]);
	}
	return 0;
}

Connections between cities

/*
这里要用到LCA,我们的距离为两个子节点到根的距离和减去最近祖先的距离的2倍;
*/
#include<cstdio>
#include<iostream>
#include<string.h>
#include<vector>
using namespace std;
class node
{
public:
   int en;
   int len;    
};

vector<node>tree[10024],Qes[10024];
int dist[10024],set[10024],visit[10024],ans[1000024];
void init( int n )
{
   for(int i=0;i<=n;i++)
   {
      dist[i]=0;
      set[i]=i;
      visit[i]=0;
//      indegree[i]=0;
      tree[i].clear(  );
      Qes[i].clear( );
    }    
}
inline int find( int x )
{
   return set[x]==x?x:set[x]=find( set[x] );    
}

inline void LCA( int num,int d,int root )
{
    dist[num]=d;
    set[num]=num;
    visit[num]=root;//标记为那棵树的根 
    int size=tree[num].size();
    for( int i=0; i<size; i++ )
    {
       if( !visit[tree[num][i].en] )
       {
          LCA( tree[num][i].en,tree[num][i].len+d ,root);
          set[tree[num][i].en]=num;    
        }    
    }
    size=Qes[num].size( );
    for( int i=0;i<size; i++ )
    {
       if( visit[Qes[num][i].en] )
       {
           if( visit[Qes[num][i].en]==root )//如果被标记过并且是同一棵树,就找最近祖先 
           {
               ans[Qes[num][i].len]=dist[num]+dist[Qes[num][i].en]-2*dist[find( Qes[num][i].en )];        
           }
           else ans[Qes[num][i].len]=-1;//要注意为-1,不能为0同点为0 
       }
    }         
}
int main(  )
{
    int n,m,Case,x,y,dis,N;
    while( scanf( "%d %d %d",&N,&n,&m )==3 )
    {
       init( N );
       for( int i=1; i<= n ;i++ )
       {
          scanf( "%d %d %d",&x,&y,&dis );
          node T;
          T.en = y;
          T.len = dis;
          tree[x].push_back( T );
//          indegree[y]++;
          T.en=x;
          tree[y].push_back( T );
       }    
       for( int i=1;i <= m ;i++ )
       {  
          scanf( "%d %d",&x,&y );
          node T;
          T.en=y;
          T.len=i;
    //      ans[i]=-1;
          Qes[x].push_back( T );
          T.en=x;
          Qes[y].push_back( T );     
       }
       for( int i=1;i<=N; i++ )
       {
             if( visit[i]==0 )//多棵树的情况,因为同一棵树任何一个节点都可以做根节点 
             {
                LCA( i,0,i );        
             }        
       }
       for( int i=1;i<=m ; i++ )
       {
             if( ans[i]==-1 )
             {
                 printf( "Not connected\n" );        
             }    
             else printf( "%d\n",ans[i] );
       }
    }
   return 0;    
}

Interviewe

/*
RMQ
*/
 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 using namespace std;
 
 const int N=200010, A=1001;
 int a[N];
 int n,k;
 int st[N][20],lg2[N];
 void ST(int *a,int n)
 {
     lg2[0]=-1;
     for(int i=1;i<=n;i++)
         lg2[i]=lg2[i-1]+(i&(i-1)?0:1);
     for(int i=0;i<n;i++) st[i][0]=a[i];
     for(int j=1;j<=lg2[n];j++)
         for(int i=0;lg2[n-i]>=j;i++)
             st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
 }
 int RMQ(int x,int y)
 {
     int k=lg2[y-x+1];
     return max(st[x][k],st[y-(1<<k)+1][k]);
 }
 bool ok(int c)
 {
     int t=n/c, s=0;
     for(int i=0;i<c;i++) s+=RMQ(t*i,t*i+t-1);
     return s>k;
 }
 int main()
 {
     while(scanf("%d%d",&n,&k),n>=0 || k>=0)
     {
         int s=0;
         for(int i=0;i<n;i++)
         {
             scanf("%d",&a[i]);
             s+=a[i];
         }
         if(s<=k) {printf("-1\n"); continue;}
         ST(a,n);
         int l=1, r=n;
         while(l<r)
         {
             int m=(l+r)/2;
             if(ok(m)) r=m;
             else l=m+1;
         }
         printf("%d\n",l);
     }
     return 0;
 }

Check Corners

/*
本题碉堡了,内存限制好紧,再大一点都不行,还只能用int,开始用short WA了好多次。。。。。
 
题意:给一个矩阵,然后给Q个询问,每个询问有四个数,分
别代表询问的子矩阵的左上角和右下角,然后找出子矩阵的最大值输出,然后再把这个值与
子矩阵的四个角的值比较,如果有至少一个等于这个最大值就输出“yes”,否则输出“no”。
*/
#include <stdio.h>
#include <iostream>
#include <math.h>

using namespace std;

const int N = 302;

int n, m;

int val[N][N];

int dpmax[N][N][9][9];

void ST()
{
    int i, j, r, c, k1, k2;
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            dpmax[i][j][0][0]=val[i][j];
    k1=(int)(log(double(n))/log(2.0));
    k2=(int)(log(double(m))/log(2.0));

    for(i=0;i<=k1;i++)
    {
        for(j=0;j<=k2;j++)
        {
            if(i==0&&j==0)  continue;
            for(r=1;r+(1<<i)-1<=n;r++)
            {
                for(c=1;c+(1<<j)-1<=m;c++)
                {
                    if(i==0)
                        dpmax[r][c][i][j]=max(dpmax[r][c][i][j-1],dpmax[r][c+(1<<(j-1))][i][j-1]);
                    else
                        dpmax[r][c][i][j]=max(dpmax[r][c][i-1][j],dpmax[r+(1<<(i-1))][c][i-1][j]);
                }
            }
        }
    }
}

int query(int r1, int c1, int r2, int c2)
{
    int kr=(int)(log(double(r2-r1+1))/log(2.0));
    int kc=(int)(log(double(c2-c1+1))/log(2.0));

    int t1=dpmax[r1][c1][kr][kc];
    int t2=dpmax[r2-(1<<kr)+1][c1][kr][kc];
    int t3=dpmax[r1][c2-(1<<kc)+1][kr][kc];
    int t4=dpmax[r2-(1<<kr)+1][c2-(1<<kc)+1][kr][kc];

    return max(max(t1,t2),max(t3,t4));
}

int main()
{
    int i, j, k;
    int r1,c1,r2,c2;
    while(~scanf("%d%d",&n,&m))
    {
        for(i=1;i<=n;i++)
           for(j=1;j<=m;j++)
              scanf("%d",&val[i][j]);
        ST();
        scanf("%d",&k);
        while(k--)
        {
            scanf("%d%d%d%d",&r1,&c1,&r2,&c2);
            int ret=query(r1,c1,r2,c2);
            printf("%d ",ret);
            if(val[r1][c1]==ret||val[r1][c2]==ret||val[r2][c1]==ret||val[r2][c2]==ret) puts("yes");
            else   puts("no");
        }
    }
    return 0;
}

Cow Sorting

/*
话说这道题要用的三个树状数组,不容易啊。我刚开始想的时候想明白了用公式怎么算,
却想不出来怎么转化到树状数组上,总感觉有些地方实现不了,原来竟然是用三个树状数组。。。
这让只写过一个树状数组的孩纸情何以堪?
          具体来说,有一个num数组,里面记录的是插入a[i]后,
		  在a[i]之前插入且比a[i]小的数的个数;还有一个totalsum数组,
		  记录的是插入第i个数后,前i-1个数的总和;还有一个smallersum数组,
		  记录的是插入a[i]后,在a[i]之前插入且比a[i] 小的数的总和。
		  这样最后就可以算出来了。
		  */
		  #include <cstdio>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int M = 100010;
__int64 num[M] , totalsum[M] , smallsum[M];

__int64 lowbit( __int64 x )
{
        return x&(-x);        
}

void add( __int64 x[] , __int64 xx , __int64 yy )
{
     while( xx < M )
     {
            x[xx] += yy;
            xx +=lowbit(xx);
     }
}

__int64 sum( __int64 x[] , __int64 xx )
{
        __int64 ss = 0;
        while( xx > 0 )        
        {
               ss += x[xx];
               xx -= lowbit(xx);       
        }
        return ss;
}

int main( )
{
    __int64 n;
    while( ~scanf("%I64d",&n) )
    {
           memset( num , 0 , sizeof(num) );
           memset( totalsum , 0 , sizeof(totalsum) );
           memset( smallsum , 0 , sizeof(smallsum) );
           __int64 x , ans = 0 , count = 0;
           for( __int64 i=1 ; i<=n ; i++ ){
                scanf("%I64d",&x);
                add( num , x , 1 );
                add( totalsum , i , x );
                add( smallsum , x , x );
                count = sum( num , x-1 );
                ans +=  (i-count-1)*x;
                ans += sum(totalsum,i-1);
                ans -= sum(smallsum,x-1);
           }
           printf("%I64d\n",ans);
    }
    return 0;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值