zoukankan      html  css  js  c++  java
  • 2018集训队日常训练1

     

    5385: 树的遍历 分享至QQ空间

    Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByte
    Total Submit: 22            Accepted:16

    Description

     

    给定一棵二叉树的后序遍历和中序遍历,请你输出其层序遍历的序列。这里假设键值都是互不相等的正整数。

    Input

     

    输入第一行给出一个正整数N(<=30),是二叉树中结点的个数。第二行给出其后序遍历序列。第三行给出其中序遍历序列。数字间以空格分隔。

    Output

     

    在一行中输出该树的层序遍历的序列。数字间以1个空格分隔,行首尾不得有多余空格。

    Sample Input

    7
    2 3 1 5 7 6 4
    1 2 3 4 5 6 7

    Sample Output

     4 1 6 3 5 7 2

    如题意所述,我们可以直接得到其前序遍历结果,所以hash一下就好了,特别大了而且成链就写一个大数吧,hash一下就好的

    #include <bits/stdc++.h>
    using namespace std;
    map<int,int>M;
    int s[31],c[31],l;
    void la(int l,int r,int st,int ed,int f)
    {
        if(l<=r&&st<=ed)
        {
            M[f]=c[ed];
            for(int i=l; i<=r; i++)
                if(c[ed]==s[i])
                {
                    la(l,i-1,st,st+i-1-l,2*f+1),la(i+1,r,st+i-l,ed-1,2*f+2);
                    return ;
                }
        }
    }
    int main()
    {
        cin>>l;
        for(int i=0;i<l;i++)cin>>c[i];
        for(int i=0;i<l;i++)cin>>s[i];
        la(0,l-1,0,l-1,0);
        int f=0;
        for(auto X:M)
        {
            if(f)cout<<" ";
            cout<<X.second,f=1;
        }
        return 0;
    }

    taozi的队列代码

    #include<bits/stdc++.h>
    using namespace std;
    int post[1000],in[1000],Left[1000],Right[1000];
    int n;
    int build(int L1,int R1,int L2,int R2)
    {
        if(L1>R1)return 0;
        int root=post[R1];
        int pos=L2;
        while(in[pos]!=root)pos++;
        int cnt=pos-L2;
        Left[root]=build(L1,L1+cnt-1,L2,pos-1);
        Right[root]=build(L1+cnt,R1-1,pos+1,R2);
        return root;
    }
    void level()
    {
        queue<int>q;
        q.push(post[n]);
        int f=0;
        while(!q.empty())
        {
            int u=q.front();q.pop();
            if(!f)printf("%d",u),f=1;
            else printf(" %d",u);
            if(Left[u])q.push(Left[u]);
            if(Right[u])q.push(Right[u]);
        }
    }
    int main()
    {
        cin>>n;
        for(int i=1;i<=n;i++)
            cin>>post[i];
        for(int i=1;i<=n;i++)
            cin>>in[i];
        build(1,n,1,n);
        level();
        return 0;
    }

    5445: 中位数 分享至QQ空间

    时间限制(普通/Java):2000MS/6000MS     内存限制:250000KByte
    总提交: 15            测试通过:2

    描述

     

    给定两个序列,都已经从小到大排序,求两个序列合并后的中位数。

    所谓中位数是指:当排序后的序列元素个数是奇数时取中间值,否则去中间两个数的平均数。

    你能想出O(log(m+n))复杂度的算法吗?

    输入

     

    多组数据。每组数据的:

    第一行为两个整数n和m,(1<=n, m<=10000000)。

    第二行由n个从小到大排序的整数。

    第三行由m个从小到大排序的整数。

    以EOF结束。

    输出

     

    输出中位数,保留2位小数。

    样例输入

     

    2 1
    1 3
    2
    2 2
    1 2
    3 4

    样例输出

     

    提示

    因本题输入数据规模较大,请使用类似以下代码输入一个整数(输入挂):

    int Scan()

    {

        int res = 0, ch, flag = 0;

        if((ch = getchar()) == '-')             //判断正负

            flag = 1;

        else if(ch >= '0' && ch <= '9')           //得到完整的数

            res = ch - '0';

        while((ch = getchar()) >= '0' && ch <= '9' )

            res = res * 10 + ch - '0';

        return flag ? -res : res;

    }

     枚举pos看他是第几个数,当然也会相等,所以这个还是特判一下的

    为什么这样是可以的呢,因为你每个数在两个数组均会有一个位置的,我只要遍历每个数组就可以找到

    mi代表当前位置,其实个数是mi+1

    pos-1代表可以插入到的位置,前一个可能是相等,也可能是不等

    #include<bits/stdc++.h>
    using namespace std;
    const int N=10000005;
    int a[N],b[N],n,m;
    long long L,R,zws;
    int la()
    {
        while(L<=R)
        {
            int mi=(L+R)>>1;
            int pos=upper_bound(a,a+n,b[mi])-a;
            if(pos&&a[pos-1]==b[mi])
            {
                if(mi+pos==zws)return mi;
            }
            if(mi+pos+1<zws)
            {
                if(mi==m-1)return -1;
                L=mi+1;
            }
            else if(mi+pos+1>zws)
            {
                if(mi==0)return -1;
                R=mi-1;
            }
            else
            {
                return mi;
            }
        }
        return -1;
    }
    int lb()
    {
        while(L<=R)
        {
            int mi=(L+R)>>1;
            int pos=upper_bound(b,b+m,a[mi])-b;
            if(pos&&b[pos-1]==a[mi])
            {
                if(mi+pos==zws)
                    return mi;
            }
            if(mi+pos+1<zws)
            {
                if(mi==n-1)return -1;
                L=mi+1;
            }
            else if(mi+pos+1>zws)
            {
                if(mi==0)return -1;
                R=mi-1;
            }
            else
            {
                return mi;
            }
        }
        return -1;
    }
    int Scan()
    {
    
        int res = 0, ch, flag = 0;
        if((ch = getchar()) == '-')
    
            flag = 1;
    
        else if(ch >= '0' && ch <= '9')
    
            res = ch - '0';
    
        while((ch = getchar()) >= '0' && ch <= '9' )
    
            res = res * 10 + ch - '0';
    
        return flag ? -res : res;
    
    }
    
    int main()
    {
        while(~scanf("%d%d",&n,&m))
        {
            for(int i=0; i<n; i++)a[i]=Scan();
            for(int i=0; i<m; i++)b[i]=Scan();
            L=0,R=m-1,zws=(n+m+1)/2;
            int t=la();
            if(t==-1)
                L=0,R=n-1,t=lb(),t=a[t];
            else t=b[t];
            if((n+m)%2==0)
            {
                zws++;
                L=0,R=m-1;
                int t1=la();
                if(t1==-1)
                    L=0,R=n-1,t1=lb(),t1=a[t1];
                else t1=b[t1];
                t+=t1;
            }
            else t+=t;
            printf("%.2f
    ",t/2.);
        }
        return 0;
    }

    5201: 数字游戏 分享至QQ空间

    Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByte
    Total Submit: 125            Accepted:17

    Description

     

    栋栋正在和同学们玩一个数字游戏。
    游戏的规则是这样的:栋栋和同学们一共n个人围坐在一圈。栋栋首先说出数字1。接下来,坐在栋栋左手边的同学要说下一个数字2。再下面的一个同学要从上一个同学说的数字往下数两个数说出来,也就是说4。下一个同学要往下数三个数,说7。依次类推。
    为了使数字不至于太大,栋栋和同学们约定,当在心中数到 k-1 时,下一个数字从0开始数。例如,当k=13时,栋栋和同学们报出的前几个数依次为:
      1, 2, 4, 7, 11, 3, 9, 3, 11, 7。
    游戏进行了一会儿,栋栋想知道,到目前为止,他所有说出的数字的总和是多少。

    Input

     

    输入的第一行包含三个整数 n,k,T,其中 n 和 k 的意义如上面所述,T 表示到目前为止栋栋一共说出的数字个数。

    Output

     

    输出一行,包含一个整数,表示栋栋说出所有数的和。

    Sample Input

     

    Sample Output

     3 13 3

    Hint

     17

    样例说明

      栋栋说出的数依次为1, 7, 9,和为17。

    数据规模和约定

      1 < n,k,T < 1,000,000;

    可以枚举要加的值

    #include<bits/stdc++.h>
    using namespace std;
    
    int main()
    {
        long long n,k,T,res=0,last=1;
        cin>>n>>k>>T;
        for(int i=1;i<T;i++)
        {
            res+=(last+(2*i*n-n+1)*n/2)%k;
            last=(last+(2*i*n-n+1)*n/2)%k;
        }
        cout<<res+1;
        return 0;
    }

    5202: 网络寻路 分享至QQ空间

    时间限制(普通/Java):1000MS/3000MS     内存限制:65536KByte
    总提交: 1            测试通过:1

    描述

     

    X 国的一个网络使用若干条线路连接若干个节点。节点间的通信是双向的。某重要数据包,为了安全起见,必须恰好被转发两次到达目的地。该包可能在任意一个节点产生,我们需要知道该网络中一共有多少种不同的转发路径。

    源地址和目标地址可以相同,但中间节点必须不同。

    如下图所示的网络。

    5202.jpg

    1 -> 2 -> 3 -> 1 是允许的

    1 -> 2 -> 1 -> 2 或者 1 -> 2 -> 3 -> 2 都是非法的。

     

    输入

     

    输入数据的第一行为两个整数N M,分别表示节点个数和连接线路的条数(1<=N<=10000; 0<=M<=100000)。

    接下去有M行,每行为两个整数 u 和 v,表示节点u 和 v 联通(1<=u,v<=N , u!=v)。

    输入数据保证任意两点最多只有一条边连接,并且没有自己连自己的边,即不存在重边和自环。

     

    输出

     

    输出一个整数,表示满足要求的路径条数。

    样例输入

     

    3 3
    1 2
    2 3
    1 3

    样例输出

     

    6

    提示

    样例输入2

    4 4
    1 2
    2 3
    3 1
    1 4

    样例输出2

    10 

    可以dfs去枚举,也就是去枚举两边的点让他们不形成环

    但是注意目的地可以和源地址相同,那么合法的就有以下两种情况

    case1

    case2

    和当前节点的度有关,即这条边除了这条路带来的度的乘积

    #include<bits/stdc++.h>
    using namespace std;
    const int N=100005;
    int d[N],u[N],v[N],n,m;
    int main()
    {
        long long ans=0;
        scanf("%d%d",&n,&m);
        for(int i=0; i<m; i++)scanf("%d%d",&u[i],&v[i]),d[u[i]]++,d[v[i]]++;
        for(int i=0; i<m; i++)if(d[u[i]]>1&&d[v[i]]>1)ans+=(d[u[i]]-1)*1LL*(d[v[i]]-1)*2;
        printf("%I64d
    ",ans);
        return 0;
    }

    5204: 小朋友排队 分享至QQ空间

    Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByte
    Total Submit: 18            Accepted:2

    Description

     

    n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位置相邻的两个小朋友。
    每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。
    如果某个小朋友第一次被要求交换,则他的不高兴程度增加1,如果第二次要求他交换,则他的不高兴程度增加2(即不高兴程度为3),依次类推。当要求某个小朋友第k次交换时,他的不高兴程度增加k。
    请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。
    如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。

    Input

     

    输入的第一行包含一个整数n,表示小朋友的个数。
    第二行包含 n 个整数 H1 H2 … Hn,分别表示每个小朋友的身高。

    Output

     

    输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。

    Sample Input

     

    3
    3 2 1

    Sample Output

     9

    Hint

    样例说明

      首先交换身高为3和2的小朋友,再交换身高为3和1的小朋友,再交换身高为2和1的小朋友,每个小朋友的不高兴程度都是3,总和为9。

    数据规模和约定

      对于10%的数据, 1<=n<=10;
      对于30%的数据, 1<=n<=1000;
      对于50%的数据, 1<=n<=10000;
      对于100%的数据,1<=n<=100000,0<=Hi<=1000000。

     前置技能逆序对,即交换相邻两个数字的最小次数

    再考虑这个问题,就是问你一个数要交换几次,这个自己可以推一下

    其实就是把数倒过来正序对的个数比如 3 2 1逆序对贡献是0 1 2

    倒过来1 2 3正序对贡献0 1 2,即0+2 1+1 2+0所求次数

    他卡我其他求逆序对的方式,只能归并排序过的

    #include <stdio.h>
    #include <bits/stdc++.h>
    using namespace std;
    const int N=100005;
    int w[N],sum[N],n;
    struct T
    {
        int x,num;
    } a[N],T[N];
    void la(int l,int r)
    {
        if(r-l==1)return;
        int m=l+r>>1,tm=l+r>>1,tl=l,i=l;
        la(l,m),la(m,r);
        while(tl<m||tm<r)
        {
            if(tm>=r||(tl<m&&a[tl].x<=a[tm].x))
                T[i++]=a[tl++],T[i-1].num+=tm-m;
            else
                T[i++]=a[tm++],T[i-1].num+=m-tl;
        }
        for(int i=l; i<r; i++)a[i]=T[i];
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=0; i<n; i++)
            scanf("%d",&a[i].x),a[i].num=0;
        la(0,n);
        __int64 ans=0;
        for(int i=0; i<n; i++)ans+=a[i].num*1LL*(a[i].num+1)/2;
        printf("%I64d",ans);
        return 0;
    }

    5205: 最大子阵 分享至QQ空间

    Time Limit(Common/Java):4000MS/12000MS     Memory Limit:65536KByte
    Total Submit: 21            Accepted:8

    Description

     

    给定一个n*m的矩阵A,求A中的一个非空子矩阵,使这个子矩阵中的元素和最大。
    其中,A的子矩阵指在A中行和列均连续的一块。

    Input

     

    输入的第一行包含两个整数n, m,分别表示矩阵A的行数和列数。
    接下来n行,每行m个整数,表示矩阵A。

    Output

     

    输出一行,包含一个整数,表示A中最大的子矩阵中的元素和。

    Sample Input

     

    3 3
    -1 -4 3
    3 4 -1
    -5 -2 8

    Sample Output

     10

    Hint

    样例说明

      取最后一列,和为10。

    数据规模和约定

      对于50%的数据,1<=n, m<=50;
      对于100%的数据,1<=n, m<=500,A中每个元素的绝对值不超过5000。

    想起来了要用最大字段和,但是这个dp过程还是不好想的

    你可以对行或列做下前缀和,然后再求下最大字段和

    基于行的dp

    #include<stdio.h>
    const int N=505;
    int r[N][N];
    int main()
    {
        int n,m,x;
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
                scanf("%d",&x),r[i][j]=r[i-1][j]+x;
        int ma=x,t;
        for(int i=1; i<=n; i++)
            for(int j=i; j<=n; j++)
            {
                t=0;
                for(int k=1; k<=m; k++)
                {
                    t+=r[j][k]-r[i-1][k];
                    if(t>ma)ma=t;
                    if(t<0)t=0;
                }
            }
        printf("%d",ma);
        return 0;
    }

    数据分布不均匀,基于列的dp跑起来比较慢

    #include<stdio.h>
    const int N=505;
    int r[N][N];
    int main()
    {
        int n,m,x;
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; i++)
            for(int j=1; j<=m; j++)
                scanf("%d",&x),r[i][j]=r[i][j-1]+x;
        int ma=x,t;
        for(int i=1; i<=m; i++)
            for(int j=i; j<=m; j++)
            {
                t=0;
                for(int k=1; k<=n; k++)
                {
                    t+=r[k][j]-r[k][i-1];
                    if(t>ma)ma=t;
                    if(t<0)t=0;
                }
            }
        printf("%d",ma);
        return 0;
    }

    5206: 约数倍数选卡片 分享至QQ空间

    Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByte
    Total Submit: 1            Accepted:1

    Description

     

    闲暇时,福尔摩斯和华生玩一个游戏:
    在N张卡片上写有N个整数。两人轮流拿走一张卡片。要求下一个人拿的数字一定是前一个人拿的数字的约数或倍数。例如,某次福尔摩斯拿走的卡片上写着数字“6”,则接下来华生可以拿的数字包括:
      1,2,3, 6,12,18,24 ....
    当轮到某一方拿卡片时,没有满足要求的卡片可选,则该方为输方。
    请你利用计算机的优势计算一下,在已知所有卡片上的数字和可选哪些数字的条件下,怎样选择才能保证必胜!
    当选多个数字都可以必胜时,输出其中最小的数字。如果无论如何都会输,则输出-1。

    Input

     

    输入数据为2行。第一行是若干空格分开的整数(每个整数介于1~100间),表示当前剩余的所有卡片。
    第二行也是若干空格分开的整数,表示可以选的数字。当然,第二行的数字必须完全包含在第一行的数字中。

    Output

     

    程序则输出必胜的招法!!

    Sample Input

     

    2 3 6
    3 6

    Sample Output

     3

    Hint

    样例输入2

    1 2 2 3 3 4 5
    3 4 5

    样例输出2

    4

    博弈题目,但是题目数据应该不是很多,直接dfs就过了

    大概思路是这样的,因为我要选择必胜,即某个选择可以导致对手出现必败态

    还要输出最小的数字,所以需要对b数组进行排序

    然后需要预处理一下一个数的约数和倍数,假如这个数量级很大了,貌似就很难过了,倍数还有约数要很好的处理下,可以把有些数组直接赋值过去

    #include<bits/stdc++.h>
    using namespace std;
    #define dbg(x) cout<<#x<<" = "<< (x)<< endl
    const int N=105;
    int num[N];
    vector<int> b,f[N];
    int dfs(int x)
    {
        for(int i=f[x].size()-1; i>=0; i--)
        {
            int pp=f[x][i],t;
            if(num[pp])
            {
                num[pp]--,t=dfs(pp),num[pp]++;
                if(t)return 0;
            }
        }
        return 1;
    }
    void la()
    {
        for(auto X:b)
        {
            if(num[X])
            {
                num[X]--;
                if(dfs(X))
                {
                    cout<<X;
                    return;
                }
                num[X]++;
            }
        }
        cout<<-1;
    }
    int main()
    {
        int x;
        string s;
        getline(cin,s);
        stringstream ss(s);
        while(ss>>x)num[x]++;
        getline(cin,s);
        stringstream st(s);
        while(st>>x)b.push_back(x);
        sort(b.begin(),b.end());
        for(int i=1; i<N ; i++)
            if(num[i])
                for(int j=1; j<N ; j++)
                    if(num[j]&&(i%j==0||j%i==0))f[i].push_back(j);
        la();
        return 0;
    }

    5200: 买不到的数目 分享至QQ空间

    Time Limit(Common/Java):1000MS/3000MS     Memory Limit:65536KByte
    Total Submit: 57            Accepted:43

    Description

     

    小明开了一家糖果店。他别出心裁:把水果糖包成4颗一包和7颗一包的两种。糖果不能拆包卖。

    小朋友来买糖的时候,他就用这两种包装来组合。当然有些糖果数目是无法组合出来的,比如要买 10 颗糖。

    你可以用计算机测试一下,在这种包装情况下,最大不能买到的数量是17。大于17的任何数字都可以用4和7组合出来。

    本题的要求就是在已知两个包装的数量时,求最大不能组合出的数字。

    Input

     

    两个正整数,表示每种包装中糖的颗数(都不多于1000)

    Output

     

    一个正整数,表示最大不能买到的糖数。

    如果这个数不存在,则输出-1。

    Sample Input

     4 7

    Sample Output

     17

    Hint

    样例输入2

    3 5

    样例输出2

    7

    题目不严谨,因为有1的时候是不存在这个数的,而且你很快会发现只有互质的时候才存在,否则是不存在的

    但是是一个很好的数论

     证明以下2个命题
    1. (x-1)(y-1)-1 不能被表示为 ax+by的形式
    2. 大于等于(x-1)(y-1)都能被表示为 ax+by的形式
    当时题目数据是满足 x>1,y>1,gcd(x,y)==1的,现在可能改了hhh
    #include <stdio.h>
    int main()
    {
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%d",a*b-a-b);
    }
  • 相关阅读:
    vim实用技巧
    《C程序设计语言》学习笔记
    《鸟哥的Linux私房菜:服务器搭建篇》第一部分学习笔记
    入职培训学习心得
    [NOI2013]树的计数
    bzoj1779 [Usaco2010 Hol]Cowwar 奶牛战争(网络流)
    P2944 [USACO09MAR]地震损失2Earthquake Damage 2(网络流)
    bzoj3218 a + b Problem(网络流+主席树)
    P4542 [ZJOI2011]营救皮卡丘(Floyd+网络流)
    P4843 清理雪道(上下界网络流)
  • 原文地址:https://www.cnblogs.com/BobHuang/p/8980104.html
Copyright © 2011-2022 走看看