ACM Steps_Chapter Five_Section3

来源:互联网 发布:mac版pscc2018破解 编辑:程序博客网 时间:2024/06/05 00:10

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 50005int 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 40000struct 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;elsefather[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;}