暑期集训7/20

题目连接:https://vjudge.net/contest/172124#overview

A题:

题目要求:火车按顺序1-n进站,让你判断出站顺序是否正确

思路:stack

代码:

#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <stack>
#define rep(i,a,b) for(int i=a;i<b;++i)
using namespace std;
stack<int> st;
int a[1005];
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        while(1)
        {
            int flag=1;
            scanf("%d",&a[1]);
            if(a[1]==0) break;
            st.push(1);
            rep(i,2,n+1) scanf("%d",a+i);
            int u=2,v=1;
            while(1)
            {
                if(a[v]==st.top())
                {
                    st.pop();
                    if(v==n) break;
                    v++;
                    if(!st.size())
                    {
                        if(u==n+1)
                        {
                            flag=0;break;
                        }
                        st.push(u++);
                    }
                }else
                {
                    if(u==n+1)
                    {
                        flag=0;break;
                    }
                    st.push(u++);
                }
            }
            if(flag) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
        cout<<endl;
    }
    return 0;
}

B题:

题目要求:判断每个括号是不是都要匹配

思路:stack

代码:

#include<iostream>
    #include<string>
    #include<stack>
    using namespace std;
    int main()
    {
        int n;
        cin>>n;
        cin.get();
        while(n--)
        {
            stack<char> s;
            string str;
            int flag=0;
            getline(cin,str);
            for(int i=0;i<str.size();i++)
            {
                if(str[i]=='['||str[i]=='(') s.push(str[i]); //如果是[ 或者 ( 就把它推进st里
                else if(!s.empty()&&s.top()=='('&&str[i]==')') s.pop(); 如果st不为空,且能匹配的话
                else if(!s.empty()&&s.top()=='['&&str[i]==']') s.pop();
                else flag=1;如果st为空,这时候来了一个s[i],这样就永远匹配不了了
            }
            if(!flag&&!s.size()) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
         }
         return 0;
    }

C题:
题目要求:就是求逆序数;

那什么是逆序数? 比如3 2 1,1和2就是逆序数

题目要求统计,逆序数变回正序要移动多少位置

那就是归并排序,归并排序的思想,把一一个数组分开成2份,一直重复,知道剩下的数列很小,用正常比较就可以很快的出来排序,然后在一步步并回去,求出最后的结果

代码:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <stack>
#define rep(i,a,b) for(int i=a;i<b;i++)
#define ll long long
using namespace std;
int  a[500005];
int temp[500005];
ll ans;
void bihe(int fi,int mid,int en)
{
    int i=fi,j=mid+1,k=fi;
    while(i<=mid&&j<=en)//合并
    {
        if(a[i]>a[j])
        {
            ans+=j-k;
            temp[k++]=a[j++];
        }
        else
        {
            temp[k++]=a[i++];
        }
    }
    while(i<=mid) temp[k++]=a[i++];//如何前半段还有剩余就说明这些是剩下的(也就是其它的大的元素)我们就把它并到后面
    while(j<=en) temp[k++]=a[j++];//如何后半段还有剩余就说明这些是剩下的(也就是其它的大的元素)我们就把它并到后面
    rep(i,fi,en+1) a[i]=temp[i];
}
void bifen(int fi,int en)
{
    if(fi<en)
    {
        int mid = (fi+en)/2;
        bifen(fi,mid);把前半个分
        bifen(mid+1,en);把后半个分
        bihe(fi,mid,en);
    }
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        ans=0;
        rep(i,1,n+1) scanf("%d",a+i);
        bifen(1,n);//把1-n分开
        cout<<ans<<endl;
    }
    return 0;
}

D题:

E题:
F题:

题目要求:求一个人到他的所有亲戚家的最近距离

思路:数据很小,直接暴力

代码:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <cmath>
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
int r[505];
int num[30005];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(num,0,sizeof(num));
        int n;
        scanf("%d",&n);
        rep(i,0,n) scanf("%d",r+i),num[r[i]]++;//num[r[i]]表示这个街道的亲戚数量
        int ans=inf;
        rep(i,0,n)
        {
            int sum=0;
            rep(j,0,n)
            {
                if(i==j) continue;
                sum+=num[r[i]]*abs(r[j]-r[i]);
            }
            //cout<<sum<<endl;
            ans=min(ans,sum);
        }
        cout<<ans<<endl;
    }
    return 0;
}

G题:

sstream的使用,还有set_....的应用
H题:
题目要求:求组合成的合唱团(9个人,3个小队)的分数最高,要求每个小队的队员都不同

思路:暴力

代码:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define ll long long
using namespace std;
int n;
struct node
{
    int x,y,z,v;
}ans[105];
int g[10];
bool judge(int i,int j,int k)
{
    memset(g,0,sizeof(g));
    g[ans[i].x]++;
    g[ans[i].y]++;
    g[ans[i].z]++;
    g[ans[j].x]++;
    g[ans[j].y]++;
    g[ans[j].z]++;
    g[ans[k].x]++;
    g[ans[k].y]++;
    g[ans[k].z]++;
    rep(i,1,10) if(g[i]>1) return false;
    return true;
}
bool cmp(node a,node b)
{
    return a.v>b.v;
}
int main()
{
    int cnt=1;
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        rep(i,0,n) scanf("%d %d %d %d",&ans[i].x,&ans[i].y,&ans[i].z,&ans[i].v);
        int mx=-1;
        sort(ans,ans+n,cmp);
        int flag=0;
        rep(i,0,n)
        {
            rep(j,i+1,n)
            {
                rep(k,j+1,n)
                {
                    if(judge(i,j,k))
                    {
                        mx=max(mx,ans[i].v+ans[j].v+ans[k].v);
                    }
                }
            }
        }
        printf("Case %d: ",cnt++);
        cout<<mx<<endl;
    }
    return 0;
}

I题:

J题:

题目:求学生信仰的宗教的种类

思路:并查集

代码:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <stack>
#define rep(i,a,b) for(int i=a;i<b;i++)
#define ll unsigned long long
using namespace std;
stack<char> st;
int f[50005];
int n;
ll m;
int ans[50005];
int fd(int a)
{
    while(f[a]!=a) a=f[a];
    return a;
}
void combine(int a,int b)
{
    int na=fd(a),nb=fd(b);
    if(na!=nb) f[na]=nb;
}
void init()
{
    rep(i,1,n+1) f[i]=i;
}
int main()
{
/*ll tt=50000*50000/2;
cout<<tt;*/
    int cnt=1;
    while(cin>>n>>m)
    {
    if(n==0&&m==0) break;
       ll num=0;
        memset(ans,0,sizeof(ans));
        init();
        int a,b;
        rep(i,0,m)
        {
            scanf("%d %d",&a,&b);
            combine(a,b);
        }
        rep(i,1,n+1)
        {
            int x=fd(i);
            if(!ans[x])
            {
                num++;
                ans[x]=1;
            }
        }
        printf("Case %d: ",cnt++);
        cout<<num<<endl;
    }
    return 0;
}

K题:
思路:并查集

代码:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <stack>
#define rep(i,a,b) for(int i=a;i<b;i++)
using namespace std;
stack<char> st;
int f[30005];
int n,m;
int ans[30005];
int fd(int a)
{
    while(f[a]!=a) a=f[a];
    return a;
}
void combine(int a,int b)
{
    int na=fd(a),nb=fd(b);
    f[na]=nb;
}
void init()
{
    rep(i,1,n+1) f[i]=i;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(ans,0,sizeof(ans));
        scanf("%d %d",&n,&m);
        init();
        int a,b;
        rep(i,0,m)
        {
            scanf("%d %d",&a,&b);
            combine(a,b);
        }
        rep(i,1,n+1)
        {
            int x=fd(i);
            ans[x]++;
        }
        sort(ans+1,ans+n+1);
        cout<<ans[n]<<endl;
    }
    return 0;
}

L题:

设置虚拟节点,然后这样进行2操作的时候,可以防止移动一整颗树的情况

代码:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <string>
#define de(x) cout<<#x<<"="<<x<<endl;
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define repd(i,a,b) for(int i=a;i>=(b);--i)
#define ll long long
#define mt(a,b) memset(a,b,sizeof(a))
const int maxn = 100010;
using namespace std;
int n,m;
int fa[maxn*2], num[maxn*2], sum[maxn*2];
void init()
{
    int j=n+1;
    rep(i,0,n+1) fa[i]=j,fa[j]=j,num[j]=1,sum[j++]=i;//每个节点新弄一个虚拟父亲,虚拟父亲保存这子节点的信息 
}
int fd(int x)
{
    while(x!=fa[x]) x=fa[x];
    return x;
}
void combine(int a,int b)
{
    int na=fd(a),nb=fd(b);
    if(na==nb) return;
    fa[na]=nb;
    sum[nb]+=sum[na];
    num[nb]+=num[na];
}
void frto(int a,int b)
{
    int na=fd(a),nb=fd(b);
    if(na==nb) return;
    fa[a]=nb; //这边不是na,因为我们的虚拟节点是用来保存信息的(一个以它为根的集合的数据) 
    num[nb]++;num[na]--;
    sum[nb]+=a;sum[na]-=a;
}
int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        init();
        int cas;
        rep(i,0,m)
        {
            scanf("%d",&cas);
            int a,b; 
            if(cas==1)
            {
                scanf("%d %d",&a,&b);
                combine(a,b);
            }
            if(cas==2)
            {
                scanf("%d %d",&a,&b);
                frto(a,b);
            }
            if(cas==3)
            {
                scanf("%d",&a);
                int na=fd(a);
                printf("%d %d\n",num[na],sum[na]);
            }
        }
    }
    return 0;
}

 

M题;

题目:让你求连通1-n的最短路中的最长的一条子路

思路:K。。。。。。最短路,把边排序,然后一直从最短的边合并

代码:

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define ll long long
using namespace std;
int f[505];
int n;
struct node
{
    int x,y,len;
}road[20005];
bool cmp(node a,node b)
{
    return a.len<b.len;
}
int fd(int x)
{
    while(x!=f[x]) x=f[x];
    return x;
}
void init()
{
    rep(i,1,n+1) f[i]=i;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int l;
        init();
        int cnt=0;
        rep(i,1,n+1)
        rep(j,1,n+1)
        {
            scanf("%d",&l);
            if(i==j||j<i) continue;
            road[cnt].x=i;road[cnt].y=j;road[cnt++].len=l;
        }
        sort(road,road+cnt,cmp);

        int  ans=0;
        rep(i,0,cnt)
        {
            int fr=road[i].x,to=road[i].y,len=road[i].len;
            /*cout << " a: ";
            rep(j,1,n+1) cout << f[j]<<" ";
            cout << endl;*/
            if(fd(fr)!=fd(to))
            {
                f[fd(fr)]=fd(to);
                ans=max(ans,len);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

N题:

题目:

思路:优先队列

代码:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include<functional>//greater这个东西的头文件
#include <queue>
#include <stack>
#define rep(i,a,b) for(int i=a;i<b;i++)
#define ll long long
using namespace std;
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int ans=0;
        int a;
        priority_queue<int,vector<int>,greater<int> > q; //优先队列从小到大排
        rep(i,0,n)
        {
            scanf("%d",&a);
            q.push(a);
        }
        int x,y;
        while(!q.empty())
        {
            x=q.top();
            //cout<<x<<endl;
            q.pop();
            int flag=0;
            if(!q.empty())
            {
                y=q.top();
                q.pop();
                x+=y;
                //cout<<x<<endl;
                flag=1;
            }
            if(flag) q.push(x),ans+=x;;
        }
        cout<<ans<<endl;
    }
    return 0;
}

O题:

线段树

代码:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <stack>
#include <iostream>
#define rep(i,a,b) for(int i=a;i<b;i++)
#define ll long long
using namespace std;
struct node
{
    int x,y,sum;
}tree[50005*4];//要4*节点的个数 
int a[50005];
void buildtree(int fi,int en,int k) //创建线段树 
{
    tree[k].x=fi;//k表示第几个线段树,每个线段树的孩子都是2*k和2*k+1 
    tree[k].y=en;
    if(fi==en)
    {
        tree[k].sum=a[fi];//如果l==r那他就是叶子节点,就直接付给他对应数组数值 
        //cout<<a[fi]<<endl;
        return;
    }
    int mid=(fi+en)/2;
    buildtree(fi,mid,k<<1);//递归创建子节点的 
    buildtree(mid+1,en,(k<<1)+1);
    tree[k].sum=tree[k<<1].sum+tree[(k<<1)+1].sum;//更新每个区间的和 
}
void add(int x,int v,int k)//更新单个节点的值,然后更新线段树 
{
    if(tree[k].x==x&&tree[k].y==x)
    {
        tree[k].sum+=v;
        return;
    }
    int mid=(tree[k].x+tree[k].y)/2;
    if(x<=mid) add(x,v,k<<1);
    else add(x,v,(k<<1)+1);
    tree[k].sum = tree[k<<1].sum + tree[(k<<1)+1].sum;
}
void sub(int x,int v,int k)
{
    if(tree[k].x==x&&tree[k].y==x)
    {
        tree[k].sum-=v;
        return;
    }
    int mid=(tree[k].x+tree[k].y)/2;
    if(x<=mid) sub(x,v,k<<1);
    else sub(x,v,(k<<1)+1);
    tree[k].sum=tree[k<<1].sum+tree[(k<<1)+1].sum;
}
int query(int l,int r,int k)//求区间的和 
{
    //cout<<l<<" "<<r<<" "<<" "<<tree[k].x<<" "<<tree[k].y<<endl;
    if(tree[k].x>=l&&tree[k].y<=r) return tree[k].sum;
    int mid=(tree[k].x+tree[k].y)/2;
    int res=0;
    if(r<=mid) res+=query(l,r,k<<1);
    else if(l>mid) res+=query(l,r,(k<<1)+1);
    else
    {
        res+=query(l,mid,k<<1);
        res+=query(mid+1,r,(k<<1)+1);
    }
    return res;
    //return 1;
}
int main()
{
    int T;
    scanf("%d",&T);
    int cnt=1;
    while(T--)
    {
        int n;
        scanf("%d",&n);
        rep(i,1,n+1) scanf("%d",a+i);
        buildtree(1,n,1);
        char s[5];
        int t1,t2;
        printf("Case %d:\n",cnt++);
        while(scanf("%s",s))
        {
            if(s[0]=='E') break;
            if(s[0]=='A')
            {
                int x,v;
                scanf("%d %d",&t1,&t2);
                add(t1,t2,1);
            }else if(s[0]=='Q')
            {
                scanf("%d %d",&t1,&t2);
                int tans=query(t1,t2,1);
                cout<<tans<<endl;
            }else if(s[0]=='S')
            {
                scanf("%d %d",&t1,&t2);
                sub(t1,t2,1);
            }
        }
    }
    return 0;
}

P题:

线段树,这把有个难点,就是区间的更新,我们用了延迟标记,代码里有详解

代码:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <string>
#define de(x) cout<<#x<<"="<<x<<endl;
#define rep(i,a,b) for(int i=a;i<(b);++i)
#define repd(i,a,b) for(int i=a;i>=(b);--i)
#define ll long long
#define mt(a,b) memset(a,b,sizeof(a))
const int maxn = 100010;
using namespace std;
struct node
{
    int x,y;
    ll sum;
}tree[maxn*4];
ll a[maxn];
ll ad[maxn*4];
void pushdown(int k)
{
    int l=tree[k].x,r=tree[k].y;
    int mid=(l+r)/2;
    ad[k<<1]+=ad[k];
    ad[(k<<1)+1]+=ad[k];
    tree[(k<<1)+1].sum+=(r-mid)*ad[k];
    tree[(k<<1)].sum+=(mid-l+1)*ad[k];
    ad[k]=0;
}
void buildtree(int l,int r,int k)
{
    tree[k].x=l;
    tree[k].y=r;
    ad[k]=0;
    if(l==r)
    {
        tree[k].sum=a[l];
        return;
    }
    int mid=(l+r)/2;
    buildtree(l,mid,k<<1);
    buildtree(mid+1,r,(k<<1)+1);
    tree[k].sum=tree[k<<1].sum+tree[(k<<1)+1].sum;
}
void add(int l,int r,int v,int k)
{
    int L=tree[k].x,R=tree[k].y;
    if(L>r||R<l) return ;
    if(l<=L&&r>=R)//就是到这里就停了,不再更新子节点了,然后我们标记一下这个点的子节点还没更新 ,就是ad{k】这个数组来标记 
    {
        ad[k]+=v;
        tree[k].sum+=(R-L+1)*v;
        return;
    }
    if(ad[k]) pushdown(k);//当我们要的区间是这个节点下面的时候,我们就要判断这个节点下面是不是要更新 
    int mid=(L+R)/2;
    if(r<=mid) add(l,r,v,k<<1);
    else if(l>mid) add(l,r,v,(k<<1)+1);
    else
    {
        add(l,mid,v,k<<1);
        add(mid+1,r,v,(k<<1)+1);
    }
    tree[k].sum=tree[k<<1].sum+tree[(k<<1)+1].sum;
}
ll query(int l,int r,int k)
{
    int L=tree[k].x,R=tree[k].y;
    if(l<=L&&r>=R)
    {
        return tree[k].sum;
    }
    if(ad[k]) pushdown(k);//当我们要的区间是这个节点下面的时候,我们就要判断这个节点下面是不是要更新 
    int mid=(L+R)/2;
    ll res=0;
    if(r<=mid) res+=query(l,r,k<<1);
    else if(l>mid) res+=query(l,r,(k<<1)+1);
    else
    {
        res+=query(l,mid,k<<1);
        res+=query(mid+1,r,(k<<1)+1);
    }
    return res;
}
int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m))
    {
        rep(i,1,n+1) scanf("%lld",a+i);
        buildtree(1,n,1);
        char ch;
        int l,r,v;
        rep(i,0,m)
        {
            cin>>ch;
            if(ch=='Q')
            {
                scanf("%d %d",&l,&r);
                ll tans=query(l,r,1);
                cout<<tans<<endl;
            }
            if(ch=='C')
            {
                scanf("%d %d %d",&l,&r,&v);
                add(l,r,v,1);
            }
        }
    }
    return 0;
}

 

posted on 2017-07-20 21:54  chinacwj1  阅读(110)  评论(0编辑  收藏  举报

导航