NOIP 2015 蒟蒻做题记录

7 篇文章 0 订阅
6 篇文章 0 订阅

昨天做了noip 2015 的题。因为之前做过几道,最开始做的很快,也都A了。可是子串斗地主运输计划什么的这些没做过的题还是把我恶心的不行QAQ我这个大蒟蒻还是没有A掉。。所以说先写一下应该得到的暴力分吧。
蒟蒻暴力没有打ci的理想分数:100+100+30+100+30+20=380…
【可是你们都是神犇】

神奇的幻方:

一种很神奇的N*N矩阵:它由数字1,2,3,……,N*N构成,且每行、每列及两条对角线上的数字之和都相同。

当N为奇数时,我们可以通过以下方法构建一个幻方:

首先将1写在第一行的中间。

之后,按如下方式从小到大依次填写每个数K(K=2,3,…,N*N):

1.若(K−1)在第一行但不在最后一列,则将K填在最后一行,(K−1)所在列的右一列;

2.若(K−1)在最后一列但不在第一行,则将K填在第一列,(K−1)所在行的上一行;

3.若(K−1)在第一行最后一列,则将K填在(K−1)的正下方;

4.若(K−1)既不在第一行,也不在最后一列,如果(K−1)的右上方还未填数,则将K填在(K−1)的右上方,否则将K填在(K−1)的正下方。

现给定N请按上述方法构造N*N的幻方。

输入输出格式

输入格式:
输入文件只有一行,包含一个整数N即幻方的大小。

输出格式:
输出文件包含N行,每行N个整数,即按上述方法构造出的N*N的幻方。相邻两个整数之间用单个空格隔开。

输入输出样例

输入样例#1:
3
输出样例#1:
8 1 6
3 5 7
4 9 2

暴力模拟,for循环+if语句练习。直接记录上个填的数的行列,然后模拟就可以了。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int maps[50][50];

int main()
{
    int n;scanf("%d",&n);
    maps[1][n/2+1]=1;
    int h=1,l=n/2+1;
    for(int i=2;i<=n*n;i++)
    {
        if(h==1&&l!=n)
        {
            maps[n][l+1]=i;
            h=n;
            l++;
            continue;
        }
        else if(l==n&&h!=1)
        {
            maps[h-1][1]=i;
            h--;
            l=1;
            continue;
        }
        else if(h==1&&l==n)
        {
            maps[h+1][l]=i;
            h++;
            continue;
        }
        else if(h!=1&&l!=n)
        {
            if(!maps[h-1][l+1])
            {
                maps[h-1][l+1]=i;
                h--;
                l++;
            }
            else
            {
                maps[h+1][l]=i;
                h++;
            }
            continue;
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
            cout<<maps[i][j]<<' ';
        puts("");
    }
    return 0;
}

信息传递:

题目描述

有n个同学(编号为1到n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学。

游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?

输入输出格式

输入格式:
输入共2行。

第1行包含1个正整数n表示n个人。

第2行包含n个用空格隔开的正整数T1,T2,……,Tn其中第i个整数Ti示编号为i

的同学的信息传递对象是编号为Ti的同学,Ti≤n且Ti≠i

数据保证游戏一定会结束。

输出格式:
输出共 1 行,包含 1 个整数,表示游戏一共可以进行多少轮。

输入输出样例

输入样例#1:
5
2 4 2 3 1
输出样例#1:
3
说明

当进行完第 3 轮游戏后, 4 号玩家会听到 2 号玩家告诉他自己的生日,所以答案为 3。当然,第 3 轮游戏后, 2 号玩家、 3 号玩家都能从自己的消息

来源得知自己的生日,同样符合游戏结束的条件。

对于 30%的数据, n ≤ 200;

对于 60%的数据, n ≤ 2500;

对于 100%的数据, n ≤ 200000。

据说dfs即可过,我写的Tarjan找长度不为1的最小环。正确性显然。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;

const int SZ=200000+10;

int first[SZ],nxt[SZ<<1];
struct edge{
    int from,to,cost;
}es[SZ<<1];
int tot=0;

void build(int ff,int tt)
{
    es[++tot]=(edge){ff,tt};
    nxt[tot]=first[ff];
    first[ff]=tot;
}

int dfn[SZ],low[SZ],scc[SZ];
int cnt=0,tim=0;
int ans=0x3f3f3f3f;
stack<int>s;
void dfs(int u)
{
    low[u]=dfn[u]=++tim;
    s.push(u);
    for(int i=first[u];i!=-1;i=nxt[i])
    {
        int v=es[i].to;
        if(!dfn[v])
        {
            dfs(v);
            low[u]=min(low[u],low[v]);
        } 
        else if(!scc[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        cnt++;
        int cnt2=0;
        while(true)
        {
            int x=s.top();
            s.pop();
            scc[x]=cnt;
            cnt2++;
            if(x==u)
            {
                if(cnt2>1) ans=min(ans,cnt2);
                break;
            }
        }
    }
}

int main()
{
    memset(first,-1,sizeof(first));
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int tt;
        scanf("%d",&tt);
        build(i,tt);
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i]) dfs(i);
    }
    cout<<ans;
    return 0;
}

斗地主:

直接上链接吧。懒得上图QAQ链接啊

我这种蒟蒻当然打完表就GG了啊~【不知道会不会补这个坑QAQ】

打表代码如下【好尴尬QAQ一点都不优美好么>_<】:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int a[50],b[50];

int main()
{
    int T,n;cin>>T>>n;
    while(T--)
    {
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(int i=1;i<=n;i++)
            scanf("%d%d",&a[i],&b[i]);
        sort(a+1,a+n+1);
        int ans=0;
        if(n==2)
        {
            if(a[1]==a[2]) ans=1;
            else ans=2;
            cout<<ans<<'\n';
            continue;
        }
        else if(n==3)
        {
            if(a[1]==a[2]&&a[2]==a[3]) ans=1;
            if(a[1]==a[2]&&a[2]!=a[3]) ans=2;
            if(a[1]!=a[2]&&a[2]==a[3]) ans=2;
            if(a[1]!=a[2]&&a[2]!=a[3]) ans=3; 
            cout<<ans<<'\n';
            continue;
        }
        else if(n==4)
        {
            if(a[1]==a[2]&&a[2]==a[3]&&a[3]==a[4]) ans=1;
            if(a[1]==a[2]&&a[2]==a[3]&&a[3]!=a[4]) ans=1;
            if(a[1]!=a[2]&&a[2]==a[3]&&a[3]==a[4]) ans=1;
            if(a[1]==a[2]&&a[3]==a[4]&&a[2]!=a[3]) ans=2;
            if(a[1]==a[2]&&a[2]!=a[3]&&a[3]!=a[4]) ans=3;
            if(a[1]!=a[2]&&a[2]==a[3]&&a[3]!=a[4]) ans=3;
            if(a[1]!=a[2]&&a[2]!=a[3]&&a[3]==a[4]) ans=3;
            if(a[1]!=a[2]&&a[2]!=a[3]&&a[3]!=a[4]) ans=4;
            cout<<ans<<'\n';
            continue; 
        }
    }
    return 0;
}
/*
1 3
9 3
1 2
9 4

*/

day1 100+100+30结束~

跳石头:

题目背景

一年一度的“跳石头”比赛又要开始了!

题目描述

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终 点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达 终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳 跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能 移走起点和终点的岩石)。

输入输出格式

输入格式:
输入文件名为 stone.in。

输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终 点之间的岩石数,以及组委会至多移走的岩石数。

接下来 N 行,每行一个整数,第 i 行的整数 Di(0 < Di < L)表示第 i 块岩石与 起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同 一个位置。

输出格式:
输出文件名为 stone.out。 输出文件只包含一个整数,即最短跳跃距离的最大值。

输入输出样例

输入样例#1:
25 5 2
2
11
14
17
21
输出样例#1:
4
说明

输入输出样例 1 说明:将与起点距离为 2 和 14 的两个岩石移走后,最短的跳跃距离为 4(从与起点距离 17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点)。

另:对于 20%的数据,0 ≤ M ≤ N ≤ 10。 对于50%的数据,0 ≤ M ≤ N ≤ 100。

对于 100%的数据,0 ≤ M ≤ N ≤ 50,000,1 ≤ L ≤ 1,000,000,000。

最短跳跃的最大值!二分!

二分答案,每次验证的时候枚举石头,看看这个石头和上个石头之间的距离。如果他们之间的距离小于当前的答案就把这块石头搬走,并且记录一下当前搬走了几块石头,如果大于等于当前的答案,就把上个石头更新一下继续枚举。最后看当前共搬走了几块石头,如果需要搬走的石头数大于了规定的数目,就说明当前枚举的答案太大了,让答案左移,反之右移。如果等于规定的数目,我们应该让当前的答案尽可能的大,所以我们也右移答案。这样时间复杂度为O(nlogn)。最后注意一下一共是有n+1块石头!嗯…这是一道比较不错的二分入门练习题~所以写的多了点…

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int a[50000+10];
int L,n,k;

bool check(int x)
{
    int last=0;
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]-last<x) cnt++;
        else last=a[i];
    }
    if(cnt<=k) return true;
    else return false;
}

int main()
{
    scanf("%d%d%d",&L,&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    a[++n]=L;
    int l=0,r=1000000000+2;
    while(r-l>1)
    {
        int mid=(l+r)>>1;
        if(check(mid)) l=mid;
        else r=mid;
    }
    cout<<l<<endl;
    return 0;
}

子串:

题目背景

题目描述

有两个仅包含小写英文字母的字符串 A 和 B。现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一 个新的字符串,请问有多少种方案可以使得这个新串与字符串 B 相等?注意:子串取出 的位置不同也认为是不同的方案。

输入输出格式

输入格式:
输入文件名为 substring.in。

第一行是三个正整数 n,m,k,分别表示字符串 A 的长度,字符串 B 的长度,以及问

题描述中所提到的 k,每两个整数之间用一个空格隔开。 第二行包含一个长度为 n 的字符串,表示字符串 A。 第三行包含一个长度为 m 的字符串,表示字符串 B。

输出格式:
输出文件名为 substring.out。 输出共一行,包含一个整数,表示所求方案数。由于答案可能很大,所以这里要求[b]输出答案对 1,000,000,007 取模的结果。[/b]

输入输出样例

输入样例#1:
6 3 1
aabaab
aab
输出样例#1:
2
输入样例#2:
6 3 2
aabaab
aab
输出样例#2:
7
输入样例#3:
6 3 3
aabaab
aab
输出样例#3:
7

对于第 1 组数据:1≤n≤500,1≤m≤50,k=1;

对于第 2 组至第 3 组数据:1≤n≤500,1≤m≤50,k=2; 对于第 4 组至第 5 组数据:1≤n≤500,1≤m≤50,k=m; 对于第 1 组至第 7 组数据:1≤n≤500,1≤m≤50,1≤k≤m; 对于第 1 组至第 9 组数据:1≤n≤1000,1≤m≤100,1≤k≤m; 对于所有 10 组数据:1≤n≤1000,1≤m≤200,1≤k≤m。

字符串+dp==GG【微笑】

【所以我这个蒟蒻只打了30分做法,而且在uoj上还挂了一个点,在洛谷上就可以】

代码如下【又尴尬了QAQ】:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MOD=1000000007;

char c[500+10],ch[50+10];
string s,ss;
string str1,str2,str3;

int main()
{
    int n,m,k;scanf("%d%d%d",&n,&m,&k);
    scanf("%s%s",c,ch);
    s=c,ss=ch;
    int cnt=0;
    if(k==1)
    {
        while(true)
        {
            int pos=s.find(ss);
            if(pos>n||pos<0) break;
            s.erase(pos,1);
            cnt++;
        }
        cout<<cnt<<endl;
    }
    else if(k==2)
    {
        for(int i=1;i<ss.size();i++)
        {
            for(int j=0;j<s.size()-i;j++)
            {
                for(int k=i+j;k<=s.size()-ss.size()+i;k++)
                {
                //  cout<<s.substr(j,i)<<' '<<s.substr(k,ss.size()-i)<<endl;
                    if(s.substr(j,i)+s.substr(k,ss.size()-i)==ss)
                        cnt++;
                    cnt%=MOD;                       
                }
            }
        }
        cout<<cnt%MOD<<endl;
    }
    return 0;
}

/*
6 3 2 
aabaab 
aab
*/

运输计划:

题目背景

公元 2044 年,人类进入了宇宙纪元。

题目描述

L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球。

小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物

流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰。

为了鼓励科技创新,L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小 P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。

在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后, 这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的 物流公司的阶段性工作就完成了。

如果小 P 可以自由选择将哪一条航道改造成虫洞,试求出小 P 的物流公司完成阶段 性工作所需要的最短时间是多少?

输入输出格式

输入格式:
输入文件名为 transport.in。

第一行包括两个正整数 n、m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。

接下来 n-1 行描述航道的建设情况,其中第 i 行包含三个整数 ai, bi 和 ti,表示第

i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。

接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j个 运输计划是从 uj 号星球飞往 vj 号星球。

输出格式:
输出 共1行,包含1个整数,表示小P的物流公司完成阶段性工作所需要的最短时间。

输入输出样例

输入样例#1:
6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5

数据范围看这里!

我们有次胡策题T3QAQ
当时naive的我并不知道这是运输计划…考试的时候费了好长时间才打出了20分做法QAQ
20%的数据暴力lca的过程中维护一下u到v的最大值,然后用u到v的距离减去这个值就可以了。
然而我这份代码年代久远,都忘了怎么乱搞的了。。。

代码如下【QAQ】:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cstdlib>
using namespace std;

const int SZ=200000+10;

int first[SZ],nxt[SZ<<1],dis[SZ],fa[SZ],deep[SZ];
int father[SZ][30];
struct edge{
    int from,to,cost;
}es[SZ<<1];
int tot,mx=0;
int f[SZ],t[SZ];

void init()
{
    memset(dis,0,sizeof(dis));
    memset(deep,0,sizeof(deep));
    memset(fa,0,sizeof(fa));
    memset(father,0,sizeof(father));
    tot=0;
}

void build(int ff,int tt,int dd)
{
    es[++tot]=(edge){ff,tt,dd};
    nxt[tot]=first[ff];
    first[ff]=tot;
}

void dfs(int u,int f)
{
    deep[u]=deep[f]+1;
    fa[u]=f;
    for(int i=first[u];i!=-1;i=nxt[i])
    {
        int v=es[i].to;
        if(v==f)
            continue;
        else
        {
            dis[v]=dis[u]+es[i].cost;
            dfs(v,u);
        }
    }
}

int haha=0;
int lca(int x,int y)
{
    int mark=0,mark2=0,cha=0;
    if(deep[x]<deep[y])
        swap(x,y);
    while(deep[x]!=deep[y])
    {
        mark=dis[x];
        x=fa[x];
        cha=mark-dis[x];
        haha=max(cha,haha);
    }
    mark-=cha;
    cha=0;
    int cha2=0;
    mark2=dis[y];
    while(x!=y)
    {
        x=fa[x];
        cha=mark-dis[x];
        mark=dis[x];
        haha=max(cha,haha);
        y=fa[y];
        cha=mark2-dis[y];
        mark2=dis[y];
        haha=max(cha,haha);
    }
    return x;
}

int main()
{
    init();
    memset(first,-1,sizeof(first));
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int ff,tt,dd;
        scanf("%d%d%d",&ff,&tt,&dd);
        build(ff,tt,dd);
        build(tt,ff,dd);
    }
    for(int i=1;i<=n;i++)
        if(!deep[i])
            dfs(i,i);
    int maxx=0,rec=0;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&f[i],&t[i]);
        lca(f[i],t[i]);
        if(maxx<haha)
            maxx=haha,rec=i;
    }
    int mxx=0,ans=0;
    for(int i=1;i<=m;i++)
    {
        int x=lca(f[i],t[i]);
        if(i==rec)
            ans=dis[f[i]]+dis[t[i]]-2*dis[x]-haha;
        else
            ans=dis[f[i]]+dis[t[i]]-2*dis[x];
        mx=max(mx,ans);
    }
    printf("%d\n",mx);
    return 0;
}

day2 100+30+20GG…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值