冲刺Noip2017模拟赛3 解题报告——五十岚芒果酱

题1  素数

【问题描述】
    给定一个正整数N,询问1到N中有多少个素数。
【输入格式】primenum.in
    一个正整数N。
【输出格式】primenum.out
    一个数Ans,表示1到N中有多少个素数。
【输入样例】
    10
【输出样例】
    4
【数据规模】
30%   N<=100
70%   N<=5000
100%  N<=10000000
题目

  tag:数学

  思路:裸欧拉筛,注意漏判断条件导致数组越界。

 

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define maxn 10000010
 5 using namespace std;
 6 int vis[maxn],p[maxn],cnt;
 7 void ols(int n)
 8 {
 9     for(int i=2;i<=n;++i){
10         if(!vis[i]) p[++cnt]=i;
11         for(int j=1;j<=cnt&&p[j]*i<=n;++j){
12             vis[p[j]*i]=1;
13             if(i%p[j]==0) break;
14         }
15     }
16 }
17 int main()
18 {
19     //freopen("primenum.in","r",stdin);
20     //freopen("primenum.out","w",stdout);
21     int n;
22     scanf("%d",&n);
23     ols(n);
24     printf("%d",cnt);
25     return 0;
26 }

 

 

题2 传玫瑰

【题目描述】
情人节到了,情侣们也活跃起来了。这不,GoldenSun和Yoyo_Yao分到了同一个班上(真有缘)。这天,GoldenSun专门跑到了花店,为Yoyo_Yao选了999支玫瑰花(-_-|||),可当GoldenSun说了一大堆肉麻的话,正准备把花送给Yoyo_Yao时,班主任DuBowen进来了,无奈的GoldenSun只得从自己的位子上把花传给Yoyo_Yao。如果一整束传,不但比较抢眼,说不定还会被中途的一些八卦的人抢走,所以GoldenSun只能一支一支的传。
已知GoldenSun他们班的座位是一个矩形,GoldenSun的位子在(x,m),Yoyo_Yao的位子在(y,n),传玫瑰时,只能纵横传而不能斜传(更不能扔)。又知GoldenSun班上的同学很八卦,每经过一个同学传时,玫瑰花就会扣去一定的成长值(玫瑰先开始有个成长值),即那个同学的八卦度,当玫瑰的成长值不大于0时,玫瑰就会凋零。GoldenSun希望自己的心血不要白费,即所有的玫瑰花都能传到Yoyo_Yao手中,所以如果有这种情况,GoldenSun就会自己冒着风险送这朵玫瑰。
请你帮GoldenSun找到一条令玫瑰成长值为最高的路线,让GoldenSun能向Yoyo_Yao在情人节之际表达自己的爱意。
【输入格式】
输入第一行有2个用空格隔开的整数a和b,表示班里有a行b列(1<=a,b<=90)。
第二行有四个用空格隔开的整数x,m,y,n,(x,m)为GoldenSun的座位,(y,n)为Yoyo_Yao的座位。(1<=x,y<=a;1<=m,n<=b) 
第三行有一个整数love为单支玫瑰的最初成长值。(0<love<=maxlongint)
来的a行是一个a*b的矩阵,矩阵中第i行j列的正整数表示坐在第i行j列的学生的八卦程度,每行的b个整数之间用空格隔开。(当然他们两个人的八卦程度为0)
八卦程度(0<=bg<=love)
【输出格式】
输出共一行,包含一个整数,表示单支玫瑰最后成长值的最大值。
如果是GoldenSun自己送,则输出love,即单支玫瑰的最初成长值。
【输入样例】
2 2
1 1 2 2
3
0 1
1 0
【输出样例】
2
题目

  tag:最短路

  思路:在矩阵上求最短路。由于任意时刻都可以走四个方向,棋盘dp不可做,只能转图论最短路。所以把二维的点转一维,跑一遍最短路,不过这样会MLE。(下面是MLE的代码)

 

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<queue>
 5 #define maxn 8110
 6 using namespace std;
 7 queue<int> Q;
 8 int n,m,dx[]={0,1,0,-1},dy[]={1,0,-1,0},dis[maxn][maxn],d[maxn],mp[maxn],vis[maxn];
 9 int cal(int x,int y)
10 {
11     return (x-1)*m+y;
12 }
13 bool ok(int x,int y)
14 {
15     return x>0&&y>0&&x<=n&&y<=m;
16 }
17 void spfa(int x)
18 {
19     memset(d,127/3,sizeof(d));
20     d[x]=0;
21     int tot=n*m;
22     Q.push(x);
23     vis[x]=1;
24     while(!Q.empty()){
25         int u=Q.front();
26         Q.pop();
27         vis[u]=0;
28         for(int i=1;i<=tot;++i)
29             if(dis[u][i]>=0&&d[u]+dis[u][i]<d[i]){
30                 d[i]=d[u]+dis[u][i];
31                 if(!vis[i]){
32                     vis[i]=1;
33                     Q.push(i);
34                 }
35             }
36     }
37 }
38 int main()
39     //freopen("sendrose.in","r",stdin);
40     //freopen("sendrose,out","w",stdout);
41     int sx,sy,tx,ty;
42     long long love;
43     memset(dis,-1,sizeof(dis));
44     scanf("%d%d%d%d%d%d%I64d",&n,&m,&sx,&sy,&tx,&ty,&love);
45     for(int i=1;i<=n;++i)
46         for(int j=1;j<=m;++j)
47             scanf("%d",&mp[cal(i,j)]);
48     for(int i=1;i<=n;++i)
49         for(int j=1;j<=m;++j)
50             for(int k=0;k<4;++k){
51                 int X=i+dx[k],Y=j+dy[k];
52                 if(ok(X,Y)) dis[cal(i,j)][cal(X,Y)]=mp[cal(i,j)];
53             }
54     spfa(cal(sx,sy));
55     if(love-d[cal(tx,ty)]<0)printf("%I64d\n",love);
56     else printf("%I64d",love-d[cal(tx,ty)]);
57     return 0;
58 

 

这样的思路没有错,但是我们需要转成邻接表来写,解决了空间复杂度的问题(下面是AC代码)。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int xx,yy,nn,mm,a,b,zhi,f[101][101],d[101][101],head[200001],next[200001],tot,ccnt,go[200001],w[2000001],q[2000001],dist[2000001],vis[2000001];
void add(int u,int v,int z){
    next[++tot]=head[u];head[u]=tot;go[tot]=v;w[tot]=z;
}
void SPFA(int x){
    int u,v;
    memset(dist,0x3f,sizeof(dist));
    memset(vis,false,sizeof(vis));
    q[1]=x; vis[x]=true; dist[x]=0;
    int Head=0,Tail=1;
    while(Head<Tail)
    {
        u=q[++Head];        
        for(int e=head[u];e;e=next[e])
        {
            v=go[e];
            if(dist[v]>dist[u]+w[e])
            {
                dist[v]=dist[u]+w[e];
                if(vis[v]==false)
                {
                    q[++Tail]=v;
                    vis[v]=true;
                }    
            }
        }
        vis[u]=false;
    }
}
int main(){
    freopen("sendrose.in","r",stdin);
    freopen("sendrose.out","w",stdout);
    scanf("%d%d",&a,&b);
    scanf("%d%d%d%d",&xx,&yy,&nn,&mm);
    scanf("%d",&zhi);
    int jishu=0;
    for(int i=1;i<=a;i++)
        for(int j=1;j<=b;j++){
            ccnt++;
            d[i][j]=ccnt;
            scanf("%d",&f[i][j]);
         }
    for(int i=1;i<=a;i++)
        for(int j=1;j<=b;j++){
            if(d[i][j-1]!=0) add(d[i][j],d[i][j-1],f[i][j-1]);
            if(d[i-1][j]!=0) add(d[i][j],d[i-1][j],f[i-1][j]);
            if(d[i+1][j]!=0) add(d[i][j],d[i+1][j],f[i+1][j]);
            if(d[i][j+1]!=0) add(d[i][j],d[i][j+1],f[i][j+1]);
    }
    SPFA(d[xx][yy]);
    if(zhi-dist[d[nn][mm]]<=0)  printf("%lld",zhi);
    else printf("%lld",zhi-dist[d[nn][mm]]);
}

 

题3 一起去打cs

【问题描述】
    早就和lyk约好了去打cs,一直没找着时间,终于今天我家没人,他家也没人,总算可以出去了。但是偏偏天公不作美,某某人非要留那么多题要做。没办法只能尽快做完然后抓紧时间吧……
    为了尽量节省时间,我俩决定分开做所有题吧(嘿嘿,稍微耍一下滑~~)。但是有的题我比较擅长,而有的题lyk要比我做的快。所以为了尽快做完所有的题,我们要好好的分配一下。现在给出你要做题 的数目和我俩分别做每个题所需要的时间。希望你帮忙计算一下,我们最少需要多长时间才能做完所有的题去打cs啊!!!
【输入格式】cs.in
    第一行一个正整数n,表示有n个题要做。 
    接下来有n行,每行两个正整数ai,bi。 分别表示我和lyk做每个题所用的时间。
【输出格式】cs.out
    一个数,最少需要多长时间才能去打CS。 
【输入样例】
    3
    5 10
    6 11
    7 12
【输出样例】
    12
【输入输出样例解释】
    我完成题目1和题目2,时间为11。lyk完成题目3,时间为12。 
    或者 我完成题目1和题目3,时间为12。lyk完成题目2,时间为11。 
【数据规模】
    30%的数据满足:1 <= n <= 20 
    100%的数据满足:1 <= n <= 200 , 1 <= ai,bi <=200
题目

  tag:DP

  思路:关于这一问题,有一个专业的名称叫“独立任务最优调度问题”,是基于DP的方法。我们用f[i][j]表示前i个任务,第一个人用了

j时间,第二个人用的最小时间,最后来找最小的max(i,f[n][i])。转移方程为f[i][j]=min(f[i-1][j-a[i]],f[i-1][j]+b[i]),进一步作解释——如果选a,b不变,由a[i]时间前继承;如果选b,a不变,当前加上b[i]时间,防止越界作特判。

 

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define l 210 
 6 using namespace std;
 7 int n,ans=1<<29,a[l],b[l],f[l][l*l],A;
 8 int main()
 9 {
10     //freopen("cs.in","r",stdin);
11     //freopen("cs.out","w",stdout); 
12     scanf("%d",&n);
13     for(int i=1;i<=n;++i){
14         scanf("%d%d",&a[i],&b[i]);
15         A+=a[i];
16     }
17     for(int i=1;i<=n;++i)
18         for(int j=0;j<=A;++j){ 
19             f[i][j]=f[i-1][j]+b[i];
20             if(j-a[i]<0) continue;
21             f[i][j]=min(f[i][j],f[i-1][j-a[i]]);
22         }
23     for(int i=0;i<=A;++i) ans=min(ans,max(i,f[n][i]));
24     printf("%d",ans);
25     return 0;
26 }

 

 

 

题4 遇见

【故事背景】
    聪聪是个非常单纯的小朋友。有一天他在食堂打饭的时候遇见了一位绝世MM,立刻就被吸引住了。正当他想上前搭讪的时候,天空突然黑了,一个巨大的蚕宝宝从天而降,竟然把该MM吃了下去!蚕宝宝狂笑着对聪聪说:“这个MM,你给她东西吃,她就吃,你不给她吃,她自己就死掉了是吧?我已经把她藏到了我体内的最深处,再过不久她就饿死了!聪聪当然不会放过这个英雄救美的机会,于是他立刻施展法术,进入蚕宝宝体内去营救MM ! 
【问题描述】 
    进入蚕宝宝体内后,聪聪发现蚕宝宝的身体是一个含有N个结点的有向图。聪聪所在的位置为1号结点,MM所在的位置为N号结点。两个结点之间可能会有多条路,通过每条路都需要一定的时间。另外,聪聪还发现这个迷宫中有一些隐藏的双向道路,通过隐藏道路不需要花费任何时间。当然,两个结点之间可能有不止一条隐藏道路。无论是普通道路还是隐藏道路,当聪聪第一次通过时能获得一定数量的金币。聪聪现在从1号结点出发,要到MM所在的N号结点。聪聪虽然救MM心切,但他也希望能多拿走一些金币,所以现在你的目标是:在保证所需时间最小的前提下,拿走最多的金币。 
【输入格式】 
    输入文件meet.in包含M2+M1+1行。 
    第1行包含三个正整数N、M1、M2,分别表示结点总数、普通道路与隐藏道路的数量。 
    第2行到M2+1行,每行包含三个正整数A、B、C,表示有一条连接A和B的隐藏道路,该道路上的金币数为C。 
    第M2+2行到M2+M1+1行,每行包含四个正整数A、B、C、D,表示有一条从A到B的普通道路,这条路上的金币数为C,通过这条路所需花费的时间为D。 
【输出格式】 
输出文件meet.out只包含1行,依次输出最优方案所需的时间和金币数,中间用一个空格分开。 
【输入输出样例】 
meet.in    
4 4 1 
2 3 5 
1 2 8 2 
3 2 5 2 
3 4 6 6 
1 4 10 9
    
meet.out
8 19
【样例解释】 
    最优路线为1→234,其中2→3是隐藏道路。 
【数据规模和约定】 
    对于10%的数据,N≤10,M1≤8,M2=0。 
    对于30%的数据,N≤1000,M1≤50000,M2=0。 
    对于50%的数据,N≤10000,M1≤200000,M2=0。 
    对于另外30%的数据,N≤2000,M1≤60000,M2≤200。 
    对于全部的数据,N≤10000,M1≤200000,M2≤1000。 
    通过每条普通道路所需的时间和通过每条道路所获得的金币数均不超过100。 
    保证不会出现无解的情况,即至少存在一条从结点1通向结点N的路径。
题目

  tag:最短路 并查集 缩点

  思路:先跑裸最短路,再考虑双向边(隐藏通道)怎么处理。既然时间花费为0,而且要最快通过,那么可以忽略两点连接的其他边,然后通道连接的两点可以缩为一点,将金币加到祖先点。这时需要灵活使用并查集,之后的每次调用都是看祖先点。

 

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<algorithm>
#define maxn 300010
using namespace std;
int n,m1,m2,cnt,hl[maxn],dis[maxn],vis[maxn],cost[maxn],coin[maxn],fa[maxn];
queue<int>Q;
struct Edge{
    int u,v,w,t,ne;
}e[maxn];
void add(int u,int v,int w,int t)
{
    e[++cnt].u=u;
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].t=t;
    e[cnt].ne=hl[u];
    hl[u]=cnt;
}
int find(int x)
{
    return x==fa[x]?x:fa[x]=find(fa[x]);
}
void spfa(int x)
{
    memset(dis,127/3,sizeof(dis));
    Q.push(x);
    dis[x]=0;
    cost[x]=coin[x];
    vis[x]=1;
    while(!Q.empty()){
        int u=Q.front();
        Q.pop();
        vis[u]=0;
        for(int i=hl[u];i;i=e[i].ne){
            int v=e[i].v,w=e[i].w,t=e[i].t;
            if(dis[v]>dis[u]+t||(dis[v]==dis[u]+t&&cost[v]<cost[u]+w+coin[v])){
                dis[v]=dis[u]+t;
                cost[v]=cost[u]+w+coin[v];
                if(!vis[v]){
                    vis[v]=1;
                    Q.push(v);
                }
            }
        }
    }
}
int main()
{
    //freopen("meet.in","r",stdin);
    //freopen("meet.out","w",stdout);
    int x,y,w,t;
    scanf("%d%d%d",&n,&m1,&m2);
    for(int i=1;i<=n;++i) fa[i]=i;    
    for(int i=1;i<=m2;++i){
        scanf("%d%d%d",&x,&y,&w);
        int k1=find(x),k2=find(y);
        if(k1!=k2){
            fa[k1]=k2;
            coin[k2]+=coin[k1]+w;
        }
        else coin[k1]+=w;
    }
    for(int i=1;i<=m1;++i){
        scanf("%d%d%d%d",&x,&y,&w,&t);
        if(find(x)!=find(y)) add(find(x),find(y),w,t);
    }
    spfa(find(1));
    printf("%d %d\n",dis[find(n)],cost[find(n)]);
    return 0;
}

 

----------懒得去复制分割线了-----------

  芒果君:刚打完CF把解题报告补上。这次还是一如既往的渣,而且还更渣了233333333333最抓狂的是DP一个半小时没搞出来,SPFA也没写好,一团糟OTZ 最后记录下第一次CF定rank,rate是1528好开心,不过C题没切掉D题知道是DP不会写。

   突然遇到让人心情糟的事,看来是时候说晚安啊。

 

posted @ 2017-07-31 03:06  五十岚芒果酱  阅读(826)  评论(0编辑  收藏  举报