【NOIP2014】提高组

终于在8.16晚上压哨补完了,再这么拖下去NOIP迟早药丸TAT......



Day1

T1生活大爆炸版 石头剪刀布

题目链接

日常签到模拟题的话就没什么难度了,注意耐心分好类别出错就行了。

#include<cstdio>
int n,na,nb,l1=1,l2=1;
int a[202],b[202];
int sa=0,sb=0;
int main()
{
    scanf("%d %d %d",&n,&na,&nb);
    for(int i=1;i<=na;i++)
    scanf("%d",&a[i]);
    for(int i=1;i<=nb;i++)
    scanf("%d",&b[i]);
    for(int i=1;i<=n;i++,l1++,l2++)
    {
        if(l1>na)l1=1;
        if(l2>nb)l2=1;
        if(a[l1]==b[l2])continue;
        if(a[l1]==0)
        {
            if(b[l2]==2||b[l2]==3)sa++;
            else sb++;
        }
        else if(a[l1]==1)
        {
            if(b[l2]==3||b[l2]==0)sa++;
            else sb++;
        }
        else if(a[l1]==2)
        {
            if(b[l2]==1||b[l2]==4)sa++;
            else sb++;
        }
        else if(a[l1]==3)
        {
            if(b[l2]==2||b[l2]==4)sa++;
            else sb++;
        }
        else if(a[l1]==4)
        {
            if(b[l2]==0||b[l2]==1)sa++;
            else sb++;
        }
    }
    printf("%d %d",sa,sb);
    return 0;
}

Day1 T1

T2联合权值

题目链接

注意加法原理的应用防TLE,还有就是最后联合权值之和要记得乘以2(正反都算)!

s[x]表示与x相连边的权值之和,注意运用加法原理必须先统计s1,再把权值加入s数组中(因为联合权值不能是自己和自己啊)。

其他的就没什么了吧,细节的话看代码吧:

#include<cstdio>
#include<algorithm>
#include<cstring>
const int mod=1e4+7;
const int N=2e5+5;
using namespace std;
int n,u[N],v[N];
long long s[N],s1[N];
int ma[N],ma1[N];
int w[N],maxx=0;
long long sum=0;
int read()
{
    int ans=0,f=1;char c=getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-48;c=getchar();}
    return ans*f;
}
void ji(int x,int y)
{
    if(ma[x]==0)ma[x]=w[y];
        else if(ma[x]<w[y]){ma1[x]=ma[x];ma[x]=w[y];}
            else if(ma1[x]<w[y])ma1[x]=w[y];
    s1[x]=(s1[x]+s[x]*w[y])%mod;
    s[x]+=w[y];
}
int main()
{
    n=read();
    for(int i=1;i<=n-1;i++){
        u[i]=read();v[i]=read();
    }
    for(int i=1;i<=n;i++)w[i]=read();
    for(int i=1;i<=n-1;i++){
        int x=u[i],y=v[i];
        ji(x,y);ji(y,x);
    }
    for(int i=1;i<=n;i++){
        maxx=max(maxx,ma[i]*ma1[i]);
        sum=(sum+s1[i])%mod;
    }
    printf("%d %lld\n",maxx,(sum*2)%mod);
    return 0;
}

Day1 T2

T3飞扬的小鸟

题目链接

这道题很容易看出是dp,状态转移方程也很好写,但是无脑dp会TLE。

不过我们可以发现,向上飞属于完全背包问题,而不点击让其下降属于01背包问题。

这样的话就很显然了,直接各跑一次不就得了......但是注意01背包要放在完全背包的后面(虽然我不知道为什么但我反过来确实WA了...)

还有就是上下界问题各种细节很麻烦记得处理好啊(WA了好几发QAQ)(弱

#include<cstdio>
#include<cstring>
#include<algorithm>
const int inf=0x3f3f3f3f;
using namespace std;
int n,m,k,num=0,sum=inf;
int p,L,H;
int f[10001][1001];
int x[10001],y[10001],h[10001],l[10001];
int read()
{
    int ans=0,f=1;char c=getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-48;c=getchar();}
    return ans*f;
}
int main()
{
    n=read();m=read();k=read();
    for(int i=0;i<=n-1;i++){
        x[i]=read();y[i]=read();
        h[i]=m+1;
    }
    h[n]=m+1;
    for(int i=1;i<=k;i++){
        p=read();L=read();H=read();
        l[p]=L;h[p]=H;
    }
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++)f[i][j]=inf;
        for(int j=1;j<=m;j++){
            if(j==m){
                for(int kk=j-x[i-1];kk<=j;kk++)
                {
                    f[i][j]=min(f[i][j],f[i-1][kk]+1);
                    f[i][j]=min(f[i][j],f[i][kk]+1);
                }
            }
            if(j-x[i-1]>0){
                f[i][j]=min(f[i][j],f[i-1][j-x[i-1]]+1);
                f[i][j]=min(f[i][j],f[i][j-x[i-1]]+1);//完全背包
            }
        }
        for(int j=min(h[i]-1,m-y[i-1]);j>=l[i]+1;j--){
            f[i][j]=min(f[i][j],f[i-1][j+y[i-1]]);//01背包
        }
        if(h[i]!=m+1||l[i]!=0)num++;
        for(int j=1;j<=m;j++)
        if(j<=l[i]||j>=h[i])f[i][j]=inf;
        bool ff=0;
        for(int j=1;j<=m;j++)
            if(f[i][j]<inf){ff=1;break;}
        if(!ff){printf("0\n%d",num-1);return 0;}
    }
    for(int i=1;i<=m;i++)
        sum=min(sum,f[n][i]);
    printf("1\n%d",sum);
    return 0;
}

Day1 T3



Day2

T1无线网络发射器选址

题目链接

看起来很复杂的样子然而因为数据范围太水无脑O(128^2*n)枚举即可(真良心啊这题...)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int x[25],y[25],z[25];
int d,n,mx=0,num=1;
int main()
{
    scanf("%d %d",&d,&n);
    for(int i=1;i<=n;i++){
        scanf("%d %d %d",&x[i],&y[i],&z[i]);
    }
    for(int i=0;i<=128;i++){
        for(int j=0;j<=128;j++){
            int sum=0;
            for(int k=1;k<=n;k++){
                if(abs(x[k]-i)<=d&&abs(y[k]-j)<=d)sum+=z[k];
            }
            if(mx<sum){num=1;mx=sum;}
            else if(mx==sum)num++;
        }
    }
    printf("%d %d",num,mx);
    return 0;
}

Day2 T1

T2寻找道路

题目链接

这道题不就是把边全部建成反向的,然后从终点开始bfs,标记走到的点,然后1~n扫一遍没有被标记的点,把与其直接相连的点标记为-1。

若此时起点s的标记为0或-,则直接输出-1。

不然的话就再从终点跑一次bfs,因为边权都为1就没什么必要跑最短路了,拓展到起点就输出并退出即可。

//很简单的一道搜索题,要是今年也有这么简单的题就好了(tan90°)。

#include<cstdio>
#include<cstring>
#include<algorithm>
const int N=2e5+5;
using namespace std;
int n,m,x,y,s,t,sum=0;
int first[10005],q[N],fa[N];
int ok[10005];
bool pp[10005];
struct point{
    int to,next;
}e[N];
int read()
{
    int ans=0,f=1;char c=getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){ans=ans*10+c-48;c=getchar();}
    return ans*f;
}
void add(int u,int v)
{
    sum++;e[sum].to=v;e[sum].next=first[u];first[u]=sum;
}
void bfs()
{
    int head=0,tail=1;
    q[0]=t;ok[t]=1;
    while(head!=tail){
        int x=q[head];head++;if(head>=200000)head=0;
        for(int i=first[x];i;i=e[i].next){
            int y=e[i].to;
            if(!ok[y]){
                ok[y]=1;
                q[tail]=y;
                tail++;if(tail>=200000)tail=0;
            }
        }
    }
}
void print(int x)
{
    int tot=1;
    int w=fa[x];
    while(w>=0){
        tot++;
        w=fa[w];
    }
    printf("%d\n",tot);
}
void bfs1()
{
    int head=0,tail=1;
    q[0]=t;pp[t]=1;fa[t]=-1;
    while(head!=tail){
        int x=q[head];head++;if(head>=200000)head=0;
        for(int i=first[x];i;i=e[i].next){
            int y=e[i].to;
            if(y==s){print(x);return;}
            if(ok[y]<0)continue;
            if(!pp[y]){
                fa[y]=x;
                pp[y]=1;
                q[tail]=y;
                tail++;if(tail>=200000)tail=0;
            }
        }
    }
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++){
        x=read();y=read();
        add(y,x);
    }
    s=read();t=read();
    bfs();

    for(int i=1;i<=n;i++){
        if(!ok[i]){
            for(int j=first[i];j;j=e[j].next){
                ok[e[j].to]=-1;
            }
        }
    }
    if(ok[s]<=0){printf("-1");return 0;}
    bfs1();
    return 0;
}

Day2 T2

 T3解方程

题目链接

算是一道数论题?反正我自己是没想到怎么写的,于是求救了CZL和YYL dalao,最后历经又WA又TLE的才过了的...(弱

读入的ai可能很大,所以我们要对其进行取余操作(我们知道当f(x)=0时,f(x)%p=0,反之不一定成立,但我们可以通过选择比较不容易出错的几个大素数来使得正确的概率大大提高)。

ps:假设P是所有模数组成的集合,如果lcm(P)>该多项式的最大值,正确率可以达到100%。但是这道题显然无法这样做...

读入时用读入优化一并预处理了就好,t[i][j]表示ai取余mod[j]的值。

还有最好用秦九韶公式优化一下,其求多项式的值时间复杂度是O(n)的(也可以用快速幂取模)。

70分做法就是直接枚举1~m,判断这个数是否满足系数取余任意一个p都能使得f(x)=0,这样的复杂度是O(n*m*所选素数个数),最后三个点会超时。

70分代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int mod[6]={0,8191,8117,9973,9511};
int n,m,tot=0;
long long t[105][5];
int an[1000005];
void read(int p)
{
    int f=1;char c=getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){
        for(int i=1;i<=4;i++)
            t[p][i]=(t[p][i]*10%mod[i]+c-48)%mod[i];
        c=getchar();
    }
    if(f==-1)for(int i=1;i<=4;i++)t[p][i]*=-1;
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=0;i<=n;i++)read(i);
    for(int i=1;i<=m;i++){
        bool ff=0;
        for(int j=1;j<=4;j++){
            int v=t[n][j];
            for(int k=n-1;k>=0;k--)
                v=((v*i)%mod[j]+t[k][j])%mod[j];
                if(v!=0){ff=1;break;}
        }
        if(!ff)an[++tot]=i;
    }
    printf("%d\n",tot);
    for(int i=1;i<=tot;i++)
    printf("%d\n",an[i]);
    return 0;
}

Day3 T3(70分)

满分做法就是每次只枚举到mod[p](因为若多项式的结果为0,那么取x和取x%p并不会影响结果),这样可以大幅降低时间复杂度。

100分代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int mod[6]={0,8191,8117,9973,9511};
int n,m,tot=0;
int t[105][5],pr[30005][5];
int an[1000005];
void read(int p)
{
    int f=1;char c=getchar();
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){
        for(int i=1;i<=4;i++)
            t[p][i]=(t[p][i]*10%mod[i]+c-48)%mod[i];
        c=getchar();
    }
    if(f==-1)for(int i=1;i<=4;i++)t[p][i]*=-1;
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=0;i<=n;i++)read(i);
    for(int i=1;i<=4;i++){
            for(int j=1;j<mod[i];j++){
            int v=t[n][i];
            for(int k=n-1;k>=0;k--)
                v=((v*j)%mod[i]+t[k][i])%mod[i];
            pr[j][i]=v;
        }
    }
    for(int i=1;i<=m;i++){
        bool ff=0;
        for(int j=1;j<=4;j++)
        if(pr[i%mod[j]][j]!=0){ff=1;break;}
        if(!ff)an[++tot]=i;
    }
    printf("%d\n",tot);
    for(int i=1;i<=tot;i++)
    printf("%d\n",an[i]);
    return 0;
}

Day2 T3(100分)

时间: 2024-10-14 11:45:49

【NOIP2014】提高组的相关文章

[NOIP2014] 提高组 洛谷P2038 无线网络发射器选址

题目描述 随着智能手机的日益普及,人们对无线网的需求日益增大.某城市决定对城市内的公共场所覆盖无线网. 假设该城市的布局为由严格平行的129 条东西向街道和129 条南北向街道所形成的网格状,并且相邻的平行街道之间的距离都是恒定值 1 .东西向街道从北到南依次编号为0,1,2…128 , 南北向街道从西到东依次编号为0,1,2…128 . 东西向街道和南北向街道相交形成路口,规定编号为x 的南北向街道和编号为y 的东西向街道形成的路口的坐标是(x , y ). 在 某 些 路口存在一定数量的公共

NOIP2014提高组总结

-by mps 尽管今年没参加NOIP2014提高组,但是做了一下,还是有感受的,在这里写出我500分的思路(满分以后会更改,毕竟能力有限......) Day 1 T1 生活大爆炸版石头剪子布 [题目大意] 石头剪子布大家都玩过,只不过这题加了“斯波克”和“蜥蜴人”,事实上还是蛮简单的,有基本逻辑推理常识和基本代码处理能力即可AC,放在PJ都是第一题的难度... 一般有三种做法: 文艺青年:写个矩阵来表示得失,注意要判断两次(甲对乙及乙对甲) 普通青年:16个if嵌套 二B青年:25个if无嵌

【学术篇】luogu1351 [NOIP2014提高组] 联合权值

一道提高组的题..... 传送门:题目在这里.... 现在都懒得更自己的blog了,怕是太颓废了_ (:з」∠) _ 好久没做题了,手都生了.(好吧其实是做题方面手太生了) 这题我都不想讲了,把代码一贴就算了呗.. 但还是要说说的.... 首先,题目里说:"无向连通图G 有n 个点,n - 1 条边." 我们可以知道这是一棵树(怕不是废话..),这样遍历的时候就能保证是O(n)级别了.. 找最大值 很简单,遍历树的时候找一下与每个点相连的点的最大值和次大值一乘就完了...显然这么贪心是

[NOIP2014] 提高组 洛谷P2296 寻找道路

题目描述 在有向图G 中,每条边的长度均为1 ,现给定起点和终点,请你在图中找一条从起点到终点的路径,该路径满足以下条件: 1 .路径上的所有点的出边所指向的点都直接或间接与终点连通. 2 .在满足条件1 的情况下使路径最短. 注意:图G 中可能存在重边和自环,题目保证终点没有出边. 请你输出符合条件的路径的长度. 输入输出格式 输入格式: 输入文件名为road .in. 第一行有两个用一个空格隔开的整数n 和m ,表示图有n 个点和m 条边. 接下来的m 行每行2 个整数x .y ,之间用一个

[NOIP2014] 提高组 洛谷P1941 飞扬的小鸟

题目描述 Flappy Bird 是一款风靡一时的休闲手机游戏.玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙.如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败. 为了简化问题,我们对游戏规则进行了简化和改编: 游戏界面是一个长为n ,高为 m 的二维平面,其中有k 个管道(忽略管道的宽度). 小鸟始终在游戏界面内移动.小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成. 小鸟每个单位时间沿横坐标方向右移的距离为1 ,竖直移动

[NOIP2014] 提高组 洛谷P1351 联合权值

题目描述 无向连通图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 行,

NOIP2014提高组解方程

其实没有太难 但是不知道的话想不到 考场上大概有50分吧 1 #include <iostream> 2 #include <stdio.h> 3 #include <queue> 4 5 using namespace std; 6 7 int mod[7 + 2] = { 19 , 101 , 11261 , 19997 , 22877 , 21893 , 14843 }; 8 int n , m , ans[1000000 + 2]; 9 char a[100 +

[NOIP2014提高组]解方程

题目:BZOJ3751.洛谷P2312.UOJ#20.Vijos P1910.codevs3732. 题目大意:已知多项式方程: 求这个方程在[1, m]内的整数解(n 和 m 均为正整数). 解题思路:因为$0=0$(废话),能得出$0+x·p\equiv 0(mod\ p)$. 也就是当方程右边为0时,方程左边mod p为0. 但方程左边mod p等于0时,方程右边不一定等于0. 但是也不一定不等于0. 所以我们如果多引入几个p(最好是素数),对其进行测试,发现都为0的话,那我们就可以认为它

[NOIP2014] 提高组 洛谷P2312 解方程

题目描述 已知多项式方程: a0+a1x+a2x^2+..+anx^n=0 求这个方程在[1, m ] 内的整数解(n 和m 均为正整数) 输入输出格式 输入格式: 输入文件名为equation .in. 输入共n + 2 行. 第一行包含2 个整数n .m ,每两个整数之间用一个空格隔开. 接下来的n+1 行每行包含一个整数,依次为a0,a1,a2..an 输出格式: 输出文件名为equation .out . 第一行输出方程在[1, m ] 内的整数解的个数. 接下来每行一个整数,按照从小到

Noip2014 提高组 day1 T1&#183; 生活大爆炸版石头剪刀布

生活大爆炸版 石头剪刀布 描述 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头.如果两个人出拳一 样,则不分胜负.在<生活大爆炸>第二季第 8 集中出现了一种石头剪刀布的升级版游戏. 升级版游戏在传统的石头剪刀布游戏的基础上,增加了两个新手势: 斯波克:<星际迷航>主角之一. 蜥蜴人:<星际迷航>中的反面角色. 这五种手势的胜负关系如表一所示,表中列出的是甲对乙的游戏结果. 现在,小 A 和小 B 尝试玩这种升级版的猜拳游戏.已知他们的出拳都是有周期性规律的