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\n",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\n",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);
}

 

posted @ 2018-05-02 14:33  暴力都不会的蒟蒻  阅读(...)  评论(...编辑  收藏