BUPT Spring Ranking Contest For 13 Round #3 DS

比赛地址:http://vjudge.net/contest/view.action?cid=47024#overview


这次的题目说难其实不难,但是有些纯粹是模板题的那种东西如果你的数据结构学得不好,那明显是做不来的。我承认这里面大部分都不算是自己写的,毕竟模板这种东西好啊!!

顺便庆祝一下,肖老爷以及李雪夫妇在陕西邀请赛中获得第一╮(╯▽╰)╭!


A题(CodeForces 361A):水题,这题没做出来真是打脸啊( ̄ε(# ̄)☆╰╮( ̄▽ ̄///)其实我一开始也在想什么construct algorithms,后来突然发现

解题报告:

只要在矩阵的对角线上取值为k便得到了答案。

#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>

using namespace std;


int main()
{
    int n,i,j,k;
    scanf("%d%d",&n,&k);
    for(i=0;i<n;i++,printf("\n"))
    {
        for(j=0;j<n;j++)
        {
            if(i==j)
            {
                printf("%d ",k);
            }
            else
            {
                printf("0 ");
            }
        }
    }
    return 0;
}



B题(POJ 2823):有人说可以用线段树搞定这个东西.....其实一个队列就行了啊.....

解题报告:

维护两个单调队列(一个递增,一个递减)且长度不大于k,然后你就只要每次取队列头(top)就是答案。其实吧,用数组和queue都行,只是我STL用得不是很顺畅,所以就选择了用一般的数组。可以用getint()这样自己写的读取函数优化(详见丁神博客)

<pre name="code" class="cpp">#include<iostream>
#include<cstdio>
using namespace std;

const int MAXN=1000010;

struct node
{
    int num;
    int i;
}q1[MAXN],q2[MAXN];

int maxn[MAXN],minn[MAXN];

int main()
{
    int n,k,i;
    while(scanf("%d %d",&n,&k)!=EOF)
    {
        int top1,top2,tail1,tail2;
        top1=top2=tail1=tail2=0;
        int p;
        for(i=0;i<n;i++)
        {
            scanf("%d",&p);
            while(top1<tail1&&q1[tail1-1].num<p) tail1--;//递减队列
            q1[tail1].i=i,q1[tail1++].num=p;
            while(i-q1[top1].i>=k) top1++;
            while(top2<tail2&&q2[tail2-1].num>p) tail2--;//递增队列
            q2[tail2].i=i,q2[tail2++].num=p;
            while(i-q2[top2].i>=k) top2++;
            if(i>=k-1) maxn[i-k+1]=q1[top1].num,minn[i-k+1]=q2[top2].num;
        }

        printf("%d",minn[0]);
        for(i=1;i<=n-k;i++)
            printf(" %d",minn[i]);
        printf("\n%d",maxn[0]);
        for(i=1;i<=n-k;i++)
            printf(" %d",maxn[i]);
        printf("\n");
    }
    return 0;
}



C题(POJ 1836):( ̄ε(# ̄)☆╰╮( ̄▽ ̄///),呵呵,写了两个小时,wa了六次的结果是,题目看错了!!!!然后看懂了以后七分钟就搞定了.....

解题报告:

上面这个就是长官想要达到的效果。然后你只要从两端求出最长上升子序列,然后比较一下点I左边和右边的最长上升子序列的和的最大值,然后再用总人数减去该最大值,就是所求答案。

#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <cmath>

using namespace std;

int  dp1[1010],dp2[1010];
float num[1010];

int main()
{
	int i,j,n,max;
	while(scanf("%d",&n)!=EOF)
	{
		memset(dp1,0,sizeof(dp1));
		memset(dp2,0,sizeof(dp2));
		memset(num,0,sizeof(num));
		max=0;
		for(i=1;i<=n;i++)
			scanf("%f",&num[i]);
		for(i=1;i<=n;i++)
		{
			dp1[i]=1;
			for(j=i-1;j>=1;j--)
				if(num[i]>num[j]&& dp1[i]<dp1[j]+1)
					dp1[i]=dp1[j]+1;
		}
		for(i=n;i>=1;i--)
		{
			dp2[i]=1;
			for(j=i+1;j<=n;j++)
				if(num[i]>num[j]&& dp2[i]<dp2[j]+1)
					dp2[i]=dp2[j]+1;
		}
		for(i=1;i<=n;i++)
			for(j=i+1;j<=n;j++)
                if(dp1[i]+dp2[j]>=max)
                    max=dp1[i]+dp2[j];
		printf("%d\n",n-max);
	}
	return 0;

}



D题(POJ 2155):这题嘛,比赛的时候我是百度的(因为最后没有时间了,我就YY了一下)。但是下面贴的代码是我自己后来写的。

解题报告:

二维的树状数组,但是跟新方向和求和方向与一般的不同。update(x,y)是从点(x,y)到点(0,0)进行取反。getsum(x,y)是从该点到(n,n)点进行求和,当然当然,你也可以向前求和,然后再用(n,n)减去前面的几块(容斥原理)。

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

const int MAXN=1010;
int c[MAXN][MAXN],n;

inline int lowbit(int x)
{
	return x&(-x);
}

void update(int x,int y)
{
	for(int i=x;i>0;i-=lowbit(i))
		for(int j=y;j>0;j-=lowbit(j))
			c[i][j]^=1;
}

int getsum(int x,int y)
{
	int sum=0;
	for(int i=x;i<MAXN;i+=lowbit(i))
		for(int j=y;j<MAXN;j+=lowbit(j))
			sum+=c[i][j];
	return sum%2;
}


int main()
{
	int T,q;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d %d\n",&n,&q);
		memset(c,0,sizeof(c)); 
		while(q--)
		{
			char ch=getchar();
			if(ch=='Q')
			{
				int x,y;
				scanf("%d %d",&x,&y);
				printf("%d\n",getsum(x,y));
			}	
			else
			{
				int x1,y1,x2,y2;
				scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
                update(--x1,--y1);
                update(x1,y2);
                update(x2,y1);
                update(x2,y2);
			}
			getchar();
		}
		puts("");
	}
	return 0;
}



E题(POJ 1470):模板题,然后我华华丽丽狂敲了一段RMQ+LCA

解题报告:

百度一下LCA的算法,大概有离线的tarjan,在线的,还有RMQ算法。至于理解大家可以去百度一下吧~

#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <cmath>

using namespace std;

const int N=1008;
int vis[N],val[N],p[N];
int first[N*2],node[N*2],dep[N*2],dp[N*2][25];
int mi[25];//移位运算
vector<int>t[N];//邻接表
int lc[N],in[N],n;

void dfs(int &index,int u,int d)
{
    index++;//时间搓,全部遍历一次并记录所有节点的父亲,为查找公共祖先做准备
    node[index]=u;
    dep[index]=d;
    vis[u]=1;
    first[u]=index;
    for(int i=0;i<t[u].size();i++)
    {
        if(!vis[t[u][i]])
        {
            dfs(index,t[u][i],d+1);
            index++;
            node[index]=u;
            dep[index]=d;
        }
    }
}

void rmq_init(int n)//RMQ预处理
{
    int K=(int)(log((double)n)/log(2.0));
    for(int i=1;i<=n;i++)
        dp[i][0]=i;
    for(int j=1;j<=K;j++)
        for(int i=1; i-1+mi[j]<=n;i++)
        {
            int a=dp[i][j-1];
            int b=dp[i+mi[j-1]][j-1];
            if(dep[a]<dep[b])
                dp[i][j] = a;
            else
                dp[i][j] = b;
        }
}

int rmq(int x,int y)
{
    int k=(int)(log((double)(y-x+1))/log(2.0));
    int a=dp[x][k];
    int b=dp[y+1-mi[k]][k];
    if(dep[a]<dep[b])//最近公共祖先的深度
        return a;
    else
        return b;
}

int lca(int a,int b)
{
    int x=first[a];
    int y=first[b];
    int k;
    if(x>y)//可用swap,但是我以前用了超时,所以再也不敢用了。
    {
        k=rmq(y,x);
        return node[k];
    }
    else
    {
        k=rmq(x,y);
        return node[k];
    }
}

int main()
{
    for(int i=0;i<20;i++)
        mi[i]=1<<i;
//    freopen("data.txt","r",stdin);
    while(scanf("%d",&n)!=EOF)
    {
        memset(in,0,sizeof(in));
        for(int i=0;i<=n;i++)
            t[i].clear();
        for(int i=0;i<n;i++)
        {
            int a,b,m;
            scanf("%d:(%d)",&a,&m);
            for(int j=0;j<m;j++)
            {
                scanf("%d",&b);
                t[a].push_back(b);
                t[b].push_back(a);
                in[b]++;
            }

        }
        memset(vis,0,sizeof(vis));
        int tot=0,m;
        for(int i=1;i<=n;i++)
            if(!in[i])
                dfs(tot,i,1);
        rmq_init(tot);
        scanf("%d",&m);
        getchar();
        memset(lc,0,sizeof(lc));
        for(int i=0;i<m;i++)
        {
            int a,b;
            scanf(" (%d %d)",&a,&b);
            lc[lca(a,b)]++;
        }
        for(int i=1;i<=n;i++)
            if(lc[i])
                printf("%d:%d\n",i,lc[i]);
    }
    return 0;
}



F题(POJ 2442):

解题报告:

维护一个最小堆(优先队列),大小为K。读入数据,然后每一层都升序排序。然后就是最上面一层压入堆,然后每一层,先将第一个元素选出和原优先队列的元素求和,这样才能保证是最小的,然后再比较新的一层的其他元素和原序列和的sum,判断是否可以压入优先队列,如果不能够压入优先队列,则跳出(因为后面的元素更大,更不可能压入优先队列)。将所有数处理完后,维护的最小堆就是所求答案。



#include<cstdio>
#include<queue>
#include<algorithm>

using namespace std;

const int MAXN=2010;

int a[MAXN],b[MAXN],n,m;

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&m,&n);
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        m--;
        sort(a,a+n);
        priority_queue< int>q;
        while(m--)
        {
            for(int i=0;i<n;i++)
                scanf("%d",&b[i]);
            sort(b,b+n);
            for(int i=0;i<n;i++)//第一行作为比较
                q.push(a[i]+b[0]);
            for(int i=1;i<n;i++)
                for(int j=0;j<n;j++)
                {
                    if(a[j]+b[i]>q.top())
                        break;
                    q.pop(),q.push(a[j]+b[i]);
                }
            for(int i=0;i<n;i++)
            {
                a[i]=q.top();
                q.pop();
            }
            sort(a,a+n);
        }
        for(int i=0;i<n;i++)
        {
            printf("%d",a[i]);
            if(i!=n-1)
                printf(" ");
            else
                printf("\n");
        }
    }
    return 0;
}



G题(POJ 2299):这题在寒假就刷得不爱刷了

解题报告:

可以写一个归并排序,然后求逆序数。

可以写一个离散化(想了一下才想起来)+树状数组

#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>

using namespace std;

const int MAXN=500010;
typedef long long ll;

struct Node
{
    int x,v;
}a[MAXN];
int c[MAXN],p[MAXN];
int n;

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

void updata(int i,int v)
{
    for(;i<=n;i+=lowbit(i))
        c[i]+=v;
    return;
}

int getsum(int i)
{
    int sum=0;
    for(;i;i-=lowbit(i))
        sum+=c[i];
    return sum;
}

bool cmp(Node a,Node b)
{
    return a.v<b.v;
}

int main()
{
    while(~scanf("%d",&n)&&n)
    {
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i].v);
            a[i].x=i+1;
        }
        sort(a,a+n,cmp);
        int id=0;
        p[a[0].x]=++id;
        for(int i=1;i<n;i++)
        if(a[i].v==a[i-1].v)
            p[a[i].x]=id;
        else
            p[a[i].x]=++id;
        ll ans=0;
        memset(c,0,sizeof(c));
        for(int i=1;i<=n;i++)
        {
            updata(p[i],1);
            ans+=(ll)i-(ll)getsum(p[i]);
        }
        printf("%lld\n",ans);
    }

    return 0;
}
/*
5
9 1 0 5 4
3
1 2 3
0
*/




H题(POJ 3468):模板题,这个好多书上都用来教学

解题报告:

我用的是两个树状数组,其实是丁神的想法,我自己就不太明白,所以在这里不是很推荐http://blog.csdn.net/winoros/article/details/26352089(证明见丁神博客)。我还是偏向于线段树的处理。

自己百度一个模板吧。


#include <algorithm>
#include <iostream>
#include <iomanip>
#include <cstring>
#include <climits>
#include <complex>
#include <fstream>
#include <cassert>
#include <cstdio>
#include <bitset>
#include <vector>
#include <deque>
#include <queue>
#include <stack>
#include <ctime>
#include <set>
#include <map>
#include <cmath>

using namespace std;

const int MAXN=100010;
typedef long long ll;

ll a[MAXN],s[MAXN],ss[MAXN];
int n;

inline int lowbit(int x)
{
    return x&(-x);
}

void updata(int i,int v,ll *c)
{
    for(;i<=n;i+=lowbit(i))
        c[i]+=v;
    return;
}

ll getsum(int i,ll *c)
{
    ll sum=0;
    for(;i;i-=lowbit(i))
        sum+=c[i];
    return sum;
}

ll query(int l,int r)
{
    ll ret=0;
    ret+=a[r]+getsum(r,s)*(r+1)-getsum(r,ss);
    ret-=a[l]+getsum(l,s)*(l+1)-getsum(l,ss);
    return ret;
}

void add(int l,int r,int x)
{
    updata(l+1,x,s);
    updata(l+1,x*(l+1),ss);
    updata(r+1,-x,s);
    updata(r+1,-x*(r+1),ss);
}

int main()
{
    int q;
    scanf("%d %d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        a[i]+=a[i-1];
    }
    getchar();
    while(q--)
    {
        char ch=getchar();
        if(ch=='Q')
        {
            int l,r;
            scanf("%d %d",&l,&r);
            ll ans=query(l-1,r);
            printf("%lld\n",ans);
        }
        else
        {
            int l,r,v;
            scanf("%d %d %d",&l,&r,&v);
            add(l-1,r,v);
        }
        getchar();
    }
    return 0;
}




你可能感兴趣的:(数据结构)