noip 2014总结

生活大爆炸版石头剪刀布

题目描述

石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头。如果两个人出拳一样,则不分胜负。在《生活大爆炸》第二季第8 集中出现了一种石头剪刀布的升级版游戏。
升级版游戏在传统的石头剪刀布游戏的基础上,增加了两个新手势:
斯波克:《星际迷航》主角之一。
蜥蜴人:《星际迷航》中的反面角色。
这五种手势的胜负关系如表一所示,表中列出的是甲对乙的游戏结果。

noip 2014总结_第1张图片

现在,小A 和小B 尝试玩这种升级版的猜拳游戏。已知他们的出拳都是有周期性规律的,但周期长度不一定相等。例如:如果小 A以“石头 - 布- 石头- 剪刀- 蜥蜴人- 斯波克”长度为6 的周期出拳,那么他的出拳序列就是“石头- 布- 石头- 剪刀- 蜥蜴人- 斯波克- 石头- 布- 石头- 剪刀- 蜥蜴人- 斯波克- ……”,而如果小B 以“剪刀- 石头- 布- 斯波克- 蜥蜴人”长度为5 的周期出拳,那么他出拳的序列就是“剪刀- 石头- 布- 斯波克- 蜥蜴人- 剪刀- 石头- 布-斯波克- 蜥蜴人- ……”
已知小A 和小B 一共进行N 次猜拳。每一次赢的人得1 分,输的得0 分;平局两人都得0 分。现请你统计N 次猜拳结束之后两人的得分。

输入输出格式

输入格式:
输入文件名为rps.in
第一行包含三个整数:N ,NA,NB,分别表示共进行 N 次猜拳、小 A 出拳的周期长度,小B 出拳的周期长度。数与数之间以一个空格分隔。
第二行包含NA个整数,表示小 A 出拳的规律,第三行包含NB个整数,表示小 B 出拳的规律。其中,0 表示“剪刀”,1 表示“石头”,2 表示“布”,3 表示“蜥蜴人”, 4 表示“斯波克”。数与数之间以一个空格分隔。

输出格式:
输出文件名为**rps.out **。
输出一行, 包含两个整数,以一个空格分隔,分别表示小A 、小B 的得分。

输入输出样例
输入样例#1:

10 5 6
0 1 2 3 4
0 3 4 2 1 0

输出样例#1:

6 2

输入样例#2:

9 5 5
0 1 2 3 4
1 0 3 2 4

输出样例#2:

4 4

说明

对于100%的数据,0 < N ≤ 200 ,0 < NA ≤ 200 , 0 < NB ≤ 200 。

思路

用数组存下不同情况的贡献(见代码)
然后对每一个情况判断

代码

#include
using namespace std;
int n,na,nb,sa,sb,a[190],b[190],f[5][5];

void work() 
{
    for(int i=na;i

无线网络发射器选址

题目描述

随着智能手机的日益普及,人们对无线网的需求日益增大。某城市决定对城市内的公共场所覆盖无线网。
假设该城市的布局为由严格平行的129 条东西向街道和129 条南北向街道所形成的网格状,并且相邻的平行街道之间的距离都是恒定值 1 。东西向街道从北到南依次编号为0,1,2…128 , 南北向街道从西到东依次编号为0,1,2…128 。
东西向街道和南北向街道相交形成路口,规定编号为x 的南北向街道和编号为y 的东西向街道形成的路口的坐标是(x , y )。 在 某 些 路口存在一定数量的公共场所 。
由于政府财政问题,只能安装一个大型无线网络发射器。该无线网络发射器的传播范围
一个以该点为中心,边长为2*d 的正方形。传播范围包括正方形边界。
例如下图是一个d = 1 的无线网络发射器的覆盖范围示意图。

noip 2014总结_第2张图片

现在政府有关部门准备安装一个传播参数为d 的无线网络发射器,希望你帮助他们在城市内找出合适的安装地点,使得覆盖的公共场所最多。
输入输出格式
输入格式:
输入文件名为wireless.in。
第一行包含一个整数d ,表示无线网络发射器的传播距离。
第二行包含一个整数n ,表示有公共场所的路口数目。
接下来n 行,每行给出三个整数x , y , k , 中间用一个空格隔开,分别代表路口的坐标( x , y )
以及该路口公共场所的数量。同一坐标只会给出一次。

输出格式:
输出文件名为wireless.out 。
输出一行,包含两个整数,用一个空格隔开,分别表示能覆盖最多公共场所的安装地点 方案数,以及能覆盖的最多公共场所的数量。

输入输出样例
输入样例#1:

1  
2  
4 4 10  
6 6 20  

输出样例#1:

1 30

说明

对于100%的数据,1≤d≤20,1≤n≤20, 0≤x≤128,0≤y≤128,0

思路

对于每一个(x,y,k),在(x-d~x+d , y-d~y+d)的坐标中的ans都加上k;
表示在(x-d~x+d , y-d~y+d)的范围内放置路由器,都能覆盖到(x,y),
将他的值加上(x,y)处的公共场所数
最后循环找最大值。

代码

#include
using namespace std;
int d,a[129][129];
void fd(int x,int y,int z) {
    for (int i=-d; i<=d; i++)
        for (int j=-d; j<=d; j++)
          if (i+x>=0&&j+y>=0&&i+x<129&&j+y<129)
            a[x+i][y+j]+=z;
    return;
}
int main() 
{
    memset(a,0,sizeof(a));
    int x,y,n,z,ans=0,sum=0;
    scanf("%d%d",&d,&n);
    for (int i=1; i<=n; i++) {
        scanf("%d%d%d",&x,&y,&z);
        fd(x,y,z);
    }
    for (int i=0; i<=128; i++)
        for (int j=0; j<=128; j++) {
            if (a[i][j]==ans) 
                ++sum;
            if (a[i][j]>ans) {
                ans=a[i][j];
                sum=1;
            }
    }
    printf("%d %d",sum,ans);
    return 0;
}

联合权值

题目描述

无向连通图G 有n 个点,n - 1 条边。点从1 到n 依次编号,编号为 i 的点的权值为W i ,每条边的长度均为1 。图上两点( u , v ) 的距离定义为u 点到v 点的最短距离。对于图G 上的点对( u, v) ,若它们的距离为2 ,则它们之间会产生Wu×Wv 的联合权值。
请问图G 上所有可产生联合权值的有序点对中,联合权值最大的是多少?所有联合权值之和是多少?

输入输出格式

输入格式:
输入文件名为link .in
第一行包含1 个整数n 。
接下来n - 1 行,每行包含 2 个用空格隔开的正整数u 、v ,表示编号为 u 和编号为v 的点之间有边相连。
最后1 行,包含 n 个正整数,每两个正整数之间用一个空格隔开,其中第 i 个整数表示图G 上编号为i 的点的权值为W i 。

输出格式:
输出文件名为**link .out **。
输出共1 行,包含2 个整数,之间用一个空格隔开,依次为图G 上联合权值的最大值和所有联合权值之和。由于所有联合权值之和可能很大,输出它时要对10007 取余。

输入输出样例

输入样例#1:

5  
1 2  
2 3
3 4  
4 5  
1 5 2 3 10 

输出样例#1:

20 74

说明

noip 2014总结_第3张图片

本例输入的图如上所示,距离为2 的有序点对有 (1,3) 、( 2,4) 、( 3,1) 、( 3,5) 、( 4,2) 、( 5,3)
其联合权值分别为2 、15、2 、20、15、20。其中最大的是20,总和为74。
【数据说明】

对于30% 的数据,1 < n≤ 100 ;
对于60% 的数据,1 < n≤ 2000;
对于100%的数据,1 < n≤ 200 , 000 ,0 < wi≤ 10, 000 。

思路

枚举每一个点;
然后枚举可以连到他的点;
然后对着些点直接统计答案就好

代码

#include
#include
using namespace std;
const int N=2e5+5,mo=10007;
struct cs{int to,nxt;}a[N*2];
int head[N],ll,v[N];
int n,ans,x,y,maxans;
void init(int x,int y){
    a[++ll].to=y;
    a[ll].nxt=head[x];
    head[x]=ll;
}
void work(int x){
    int sum=0,ma=0,m=0;
    for(int k=head[x];k;k=a[k].nxt){
        if(v[a[k].to]>ma){
        m=ma;   
        ma=v[a[k].to];
        }else
        if(v[a[k].to]>m)m=v[a[k].to];
        ans=(ans+sum*v[a[k].to])%mo;
        sum=(sum+v[a[k].to])%mo;
    }
    maxans=max(maxans,ma*m);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i

寻找道路

题目描述

在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件:
1 .路径上的所有点的出边所指向的点都直接或间接与终点连通。
2 .在满足条件1 的情况下使路径最短。
注意:图G 中可能存在重边和自环,题目保证终点没有出边。
请你输出符合条件的路径的长度。

输入输出格式

输入格式:
输入文件名为road .in
第一行有两个用一个空格隔开的整数n 和m ,表示图有n 个点和m 条边。
接下来的m 行每行2 个整数x 、y ,之间用一个空格隔开,表示有一条边从点x 指向点y 。
最后一行有两个用一个空格隔开的整数s 、t ,表示起点为s ,终点为t 。

输出格式:
输出文件名为**road .out **。
输出只有一行,包含一个整数,表示满足题目᧿述的最短路径的长度。如果这样的路径不存在,输出- 1 。

输入输出样例
输入样例#1:

3 2  
1 2  
2 1  
1 3  

输出样例#1:

-1

输入样例#2:

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

输出样例#2:

3

说明

解释1:

noip 2014总结_第4张图片

如上图所示,箭头表示有向道路,圆点表示城市。起点1 与终点3 不连通,所以满足题
目描述的路径不存在,故输出- 1 。

解释2:

noip 2014总结_第5张图片

如上图所示,满足条件的路径为1 - >3- >4- >5。注意点2 不能在答案路径中,因为点2连了一条边到点6 ,而点6 不与终点5 连通。
数据范围

对于30%的数据,0 对于60%的数据,0 对于100%的数据,0

思路

  • 只需要在满足条件上的点进行最短路即可
  • 那么这么找满足条件的点呢,
  • 先在循环中就标记每个点的出度,
  • 我们现在在终点进行一次bfs,经过一个点那么它的num数组++,也就是标记一下它的入度,
  • 那么spfa时满足条件的点就是出度等于入度的点
    很简单

代码

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int INF=100000111;
int n,m,s,t,tail,vis[350001],head1[350001],head2[350001],sign[350001],inqueue[350001],chu[350001],num[350001];
struct node {
    int u,v,next;
};
node r1[350001],r2[350001];
queueq;
void add(int x,int y) {
    r1[++tail].u=x;
    r1[tail].v=y;
    r1[tail].next=head1[x];
    head1[x]=tail;
    r2[tail].u=y;
    r2[tail].v=x;
    r2[tail].next=head2[y];
    head2[y]=tail;
}
void bfs(int sx) {
    q.push(sx);
    sign[sx]=1;
    while (!q.empty()) {
        int sx=q.front();
        q.pop();
        for (int i=head2[sx];i;i=r2[i].next) {
            int nex=r2[i].v;
            num[nex]++; 
            if (!sign[nex]) {
                sign[nex]=1;
                q.push(nex);
            }
        }
    }
}
void spfa(int sx,int sy) {
    for (int i=1;i<=n;i++)
        vis[i]=INF;
    q.push(sx);
    inqueue[sx]=1;
    vis[sx]=0;
    while (!q.empty()) {
        int sx=q.front();
        q.pop();
        for (int i=head1[sx];i;i=r1[i].next) {
            int nex=r1[i].v;
            if (vis[sx]+1 < vis[nex] && num[nex]==chu[nex]) {
                vis[nex]=vis[sx]+1;
                if (!inqueue[nex]) {
                    inqueue[nex]=1;
                    q.push(nex);
                }
            }
        }
    }
    if (vis[sy] == INF) printf("-1"); 
    else printf("%d",vis[sy]);
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++) {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        chu[x]++;
    }
    scanf("%d%d",&s,&t);
    bfs(t);
    spfa(s,t);
    return 0;
}

飞扬的小鸟

原题

Flappy Bird 是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。

  • 为了简化问题,我们对游戏规则进行了简化和改编:

游戏界面是一个长为 n,高为 m 的二维平面,其中有 k 个管道(忽略管道的宽度)。
小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
小鸟每个单位时间沿横坐标方向右移的距离为 1,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度 X,每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度 Y。小鸟位于横坐标方向不同位置时,上升的高度 X 和下降的高度 Y 可能互不相同。
小鸟高度等于 0 或者小鸟碰到管道时,游戏失败。小鸟高度为 m 时,无法再上升。
现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;否则,输出小鸟最多可以通过多少个管道缝隙。

输入输出

输入格式

第 1 行有 3个整数 n,m,,分别表示游戏界面的长度,高度和水管的数量,每两个整数之间用一个空格隔开;

接下来的 nn 行,每行 2 个用一个空格隔开的整数 X 和 Y,依次表示在横坐标位置 0∼n−10∼n−1 上玩家点击屏幕后,小鸟在下一位置上升的高度 X,以及在这个位置上玩家不点击屏幕时,小鸟在下一位置下降的高度 Y。

接下来 kk 行,每行 33 个整数 P,L,HP,L,H,每两个整数之间用一个空格隔开。每行表示一个管道,其中 PP 表示管道的横坐标,LL 表示此管道缝隙的下边沿高度,HH 表示管道缝隙上边沿的高度(输入数据保证 PP 各不相同,但不保证按照大小顺序给出)。

输出格式

共两行。

第一行,包含一个整数,如果可以成功完成游戏,则输出 11,否则输出 00。

第二行,包含一个整数,如果第一行为 11,则输出成功完成游戏需要最少点击屏幕数,否则,输出小鸟最多可以通过多少个管道缝隙。

样例一

input

10 10 6
3 9
9 9
1 2
1 3
1 2
1 1
2 1
2 1
1 6
2 2
1 2 7
5 1 5
6 3 5
7 5 8
8 7 9
9 1 3

output

1
6

样例二

input

10 10 4
1 2
3 1
2 2
1 8
1 8
3 2
2 1
2 1
2 2
1 2
1 0 2
6 7 9
9 1 4
3 8 10

output

0
3

限制与约定

对于 30%的数据:5≤n≤10,5≤m≤10,k=05≤n≤10,5≤m≤10,k=0,保证存在一组> 最优解使得同一单位时间最多点击屏幕 33 次;
对于 50%的数据:5≤n≤20,5≤m≤105≤n≤20,5≤m≤10,保证存在一组最优解使得同一单位时间最多点击屏幕 33 次;
对于 70%的数据:5≤n≤1000,5≤m≤1005≤n≤1000,5≤m≤100;
对于 100%的数据:5≤n≤10000,5≤m≤1000,0≤k

时间限制:1s1s
空间限制:128MB

说明

【输入输出样例说明】
如下图所示,蓝色直线表示小鸟的飞行轨迹,红色直线表示管道。

noip 2014总结_第6张图片

【数据范围】
对于30% 的数据:5 ≤ n ≤ 10,5 ≤ m ≤ 10,k = 0 ,保证存在一组最优解使得同一单位时间最多点击屏幕3 次;
对于50% 的数据:5 ≤ n ≤ 2 0 ,5 ≤ m ≤ 10,保证存在一组最优解使得同一单位时间最多点击屏幕3 次;
对于70% 的数据:5 ≤ n ≤ 1000,5 ≤ m ≤ 1 0 0 ;
对于100%的数据:5 ≤ n ≤ 100 0 0 ,5 ≤ m ≤ 1 0 00,0 ≤ k < n ,0

思路

首先想到设f[i][j]表示到第i行第j列所需要的最少点击屏幕次数。转移方程为

f[ i ][ j ]=min{f[ i-1 ][ j - k*x[i-1] ] + k} (1<= k <= j/x) 上升——①
f[ i ][ j ]=min{f[ i-1 ][ j + y[i-1] } ( j + y[i-1] <= m) 下降

显然,下降可以O(1)转移,主要问题在上升的转移。

我们将上升的方程变一下:

f[ i ][ j - x[i-1] ]=min{f[ i-1 ][ (j - x[i-1]) - (k-1)*x[i-1] ] + k -1} ——②

这是 f[ i ][ j - x[i-1] ] 的转移。

由 ② 化简可得:

f[ i ][ j - x[i-1] ]=min{f[ i-1 ][ j - kx[ i-1] ] + k -1}——③
由①③消去f[ i-1 ][ j - k
x[ i-1] ]+k可得
f[ i ][ j ]= f[ i ][ j - x[ i-1 ] ]+1

于是就可以O(n*m)的时间内出解

代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define rg register
using namespace std;
#define ll long long

inline int gi()
{
    rg bool b=0;
    rg int r=0;
    char c=getchar();
    while(c<'0' || c>'9')
    {
        if(c=='-') b=!b;
        c=getchar();
    }
    while(c>='0' && c<='9')
    {
        r=r*10+c-'0';
        c=getchar();
    }
    if(b) return -r;
    return r;
}

const int inf = 2100000000, N = 10005, M = 1005;
int n,m,q,x[N],y[N],f[N][M];
bool b[N];
struct data
{
    int up,down;
} da[N];

int main()
{
    freopen ("birda.in","r",stdin);
    freopen ("birda.out","w",stdout);
    int i,p,j,k,cnt,ans;
    n=gi(), m=gi(), q=gi();
    for (i=0; i=da[i].up; j--) f[i][j]=inf;
    }
    cnt=q,ans=inf;
    for (i=n; i>=1; i--)
    {
        for (j=1; j<=m; j++) ans=min(ans,f[i][j]);  //若 ans 有值则代表能到达。 
        if (ans < inf) break;
        if (da[i].up <= m) cnt--;  //  da[i].up <= m 才是真水管 
    }
    if (cnt == q) printf("1\n%d\n",ans);
    else printf("0\n%d\n",cnt);
    return 0;
}

解方程

题目描述

已知多项式方程:

a0+a1x+a2x^2 +...+anx^n=00
求这个方程在[1, m ] 内的整数解(n 和m 均为正整数)

输入输出格式

输入格式:
输入文件名为equation .in

输入共n + 2 行。
第一行包含2 个整数n 、m ,每两个整数之间用一个空格隔开。
接下来的n+1 行每行包含一个整数,依次为a0,a1,a2..an

输出格式:
输出文件名为equation .out
第一行输出方程在[1, m ] 内的整数解的个数。
接下来每行一个整数,按照从小到大的顺序依次输出方程在[1, m ] 内的一个整数解。

输入输出样例

输入样例#1:

2 10 
1
-2
1

输出样例#1:

1
1

输入样例#2:

2 10
2
-3
1

输出样例#2:

2
1
2

说明

对于30%的数据:0 对于50%的数据:0 对于70%的数据:0 对于100%的数据:0

思路

秦九韶算法
其实我们可以把左边的式子当成一个算式来计算,从1到m枚举,只要结果是0,那么当前枚举到的值就是这个等式的解了。可以通过编写一个bool函数来判断算式的值是不是0~

代码

#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int p=1000000007;//取模比较方便qwq为了出现奇怪的错误建议多模几个质数 
bool t=true;//用来判断是否有解 
int n,m,ans,cnt,sum=0;//cnt记录解的个数;sum用来计算多项式的结果 
int A[103],key[1000003];
//A[]记录式中的a0,a1,a2(注意是以0为起点)
//key记录每个解的值 
ll read()//读入优化(似乎不加会T两个点w) 
{
    ll sum=0,fg=1;
    char c=getchar();
    while(c < '0' || c > '9')
    {
        if(c=='-') fg=-1;//如果读到负号则记录 
        c=getchar();
    }
    while(c >='0' && c <='9')
    {
        sum=((sum*10)+c-'0')%p;
        //注意因为A[]可能很大,所以读入时就要进行取模操作 
        c=getchar();
    }
    return sum*fg;
    //如果是负数(fg==-1,即读到了负号)那么返回的值为负数 
}
void print(int x)//输出优化(这个可以不加qwq) 
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
    {
        print(x/10);
    }
    putchar(x%10+'0');
}
bool calc(ll x)
{
    sum=0;//一定要清零!!!(不然只有10分呜呜呜) 
    for(ll i=n;i>=0;i--)
    {
        sum=((A[i]+sum)*x)%p;
        //这里套用秦九韶算法求多项式的值 
    }
    return !sum;//如果答案是0说明x值为该多项式的解,返回1(true) 
}
int main()
{
    n=read();
    m=read();
    for(ll i=0;i<=n;i++)
    {
        A[i]=read();
    }
    for(ll i=1;i<=m;i++)
    {
        if(calc(i))//如果返回的是1(true)则说明有解 
        { 
            t=false; 
            ans++;//记录答案个数 
            key[++cnt]=i;//记录每个解的值 
        }
    }
    if(t)
    {
        cout<

你可能感兴趣的:(noip 2014总结)