NOIP 2018 day1 题解

  今年noip的题和去年绝对是比较坑的题了,但是打好的话就算是普通水准也能350分以上吧。

t1:

很显然这是一个简单的dp即可。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<ctime>
#include<iomanip>
#include<vector>
#include<map>
#include<queue>
#include<stack>
using namespace std;
inline long long read()
{
    long long x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const long long maxn=100020;
long long n;
long long a[maxn],now=0,ans=0;
int main()
{
    //freopen("road.in","r",stdin);
    //freopen("road.out","w",stdout);
    n=read();
    for(long long i=1;i<=n;i++)a[i]=read();
    for(long long i=1;i<=n;i++)
    {
        if(a[i]>now){ans+=a[i]-now;now=a[i];}
        else now=a[i];
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

t2:

很显然,这是个完全背包,考试的时候沙比没看出来打了搜索。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<ctime>
#include<iomanip>
#include<vector>
#include<map>
#include<queue>
#include<stack>
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int maxn=200;
int t,n;
int a[maxn],f[25002],ans=0;
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("money.out","w",stdout);
    t=read();
    for(int u=1;u<=t;u++)
    {
        memset(f,0,sizeof(f));
        n=read();f[0]=1;ans=0;
        for(int i=1;i<=n;i++)a[i]=read();
        for(int i=1;i<=n;i++)
            for(int j=a[i];j<=25001;j++)
                    f[j]+=f[j-a[i]];
        for(int i=1;i<=n;i++)if(f[a[i]]==1)ans++;
        printf("%d\n",ans);
    }
    return 0;
}
View Code

暴力55分代码如下:

#include<bits/stdc++.h>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<queue>
#include<deque>
#include<bitset>
#include<set>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<iomanip>
using namespace std;
inline long long read()
{
    long long x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(long long x)
{
    if(x==0){putchar('0');putchar('\n');return;}
    if(x<0)putchar('-'),x=-x;
    long long num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
const long long maxn=50002;
long long n,m,flag=1,flag1=2;
long long lin[maxn<<1],nex[maxn<<1],ver[maxn<<1],e[maxn<<1],len=0;
long long ru[maxn],s,ans=0,q[maxn<<2],h=0,t=0,vis[maxn<<1];
long long d[maxn],cnt=0,u;
void add(long long x,long long y,long long z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
//赛道修建 55分做法
long long check1(long long x)
{
    t=0;h=0;
    long long tot=0,num=0;
    q[++t]=s;
    memset(vis,0,sizeof(vis));
    vis[s]=1;
    //cout<<x<<endl;
    while(h++<t)
    {
        long long te=q[h];
        for(long long i=lin[te];i;i=nex[i])
        {
            long long tn=ver[i];
            if(vis[tn]==1)continue;
            q[++t]=tn;
            num+=e[i];
            vis[tn]=1;
            if(num>=x)tot++,num=0;
        }
    }
    if(tot>=m)return 1;
    else return 0;
}
void solve1()//一条链
{
    for(long long i=1;i<=n;i++)if(ru[i]==1){s=i;break;}
    //cout<<s<<endl;
    //cout<<m<<endl;
    //cout<<ans<<endl;
    long long l=1,r=ans;
    while(l+1<r)
    {
        long long mid=(l+r)>>1;
        if(check1(mid)==1)l=mid;
        else r=mid;
    }
    if(check1(r)==1)put(r);
    else put(l);
    return;
}
void dp(long long x)
{    
    vis[x]=1;
    for(long long i=lin[x];i;i=nex[i])
    {
        long long tn=ver[i];
        if(vis[tn]==1)continue;
        dp(tn);
        cnt=max(cnt,d[x]+d[tn]+e[i]);
        d[x]=max(d[x],d[tn]+e[i]);
    }
    return;
}
void bfs()
{
    h=0,t=0;cnt=0;
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    q[++t]=1;vis[1]=1;
    while(h++<t)
    {
        long long te=q[h];
        for(long long i=lin[te];i;i=nex[i])
        {
            long long tn=ver[i];
            if(vis[tn]==1)continue;
            d[tn]=max(d[tn],d[te]+e[i]);//取max更加严谨,尽管更慢
            if(d[tn]>cnt)cnt=d[tn],u=tn;
            q[++t]=tn;vis[tn]=1;
        }
    }
    //cout<<u<<endl;
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    h=0,t=0;cnt=0;
    q[++t]=u;vis[u]=1;
    //cout<<u<<endl;
    while(h++<t)
    {
        long long te=q[h];
        for(long long i=lin[te];i;i=nex[i])
        {
            long long tn=ver[i];
            if(vis[tn]==1)continue;
            d[tn]=max(d[tn],d[te]+e[i]);//取max更加严谨,尽管更慢
            if(d[tn]>cnt)cnt=d[tn];
            q[++t]=tn;vis[tn]=1;
        }
    }
    put(cnt);return;
}
void dfs(int x)
{
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(vis[tn]==1)continue;
        vis[tn]=1;
        d[tn]=max(d[tn],d[x]+e[i]);
        if(d[tn]>cnt)cnt=d[tn],u=tn;
        dfs(tn);
    }
}
int check2(int x)
{
    cnt=0;int j=t+1;//双指针
    for(int i=1;i<=t;i++)if(q[i]>=x){j=i;break;}
    cnt+=t-j+1;j--;
    for(int i=1;i<j;i++)if(q[i]+q[j]>=x)j--,cnt++;
    if(cnt>=m)return 1;
    return 0;
}
void solve2()//菊花图
{
    h=0,t=0;
    for(int i=lin[1];i;i=nex[i])q[++t]=e[i];
    sort(q+1,q+1+t);
    //cout<<ans<<endl;
    //for(int i=1;i<=t;i++)cout<<q[i]<<endl;
    int l=1,r=ans;
    while(l+1<r)
    {
        int mid=(l+r)>>1;
        if(check2(mid)==1)l=mid;
        else r=mid;
    }
    if(check2(r)==1)put(r);
    else put(l);return;
}
void solve3()
{
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    dp(1);put(cnt);//树形dp求树的直径
    //bfs();//两次bfs求树的直径
    //两次dfs求树的直径
    /*memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    cnt=0;vis[1]=1;dfs(1);
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    cnt=0;vis[u]=1;dfs(u);
    put(cnt);*/
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    for(long long i=1;i<n;i++)
    {
        long long x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
        if(x+1!=y)flag=0;//一条链的情况
        ru[x]++;ru[y]++;
        ans+=z;
        if(x!=1)flag1=0;
    }
    //cout<<flag1<<endl;
    if(flag==1){solve1();return 0;}//一条链
    if(flag1==2){solve2();return 0;}//菊花图
    if(m==1){solve3();return 0;}//求树的直径的情况,两遍bfs或树形dp
    return 0;
}

55分很好写,一个直径一个二分一个双指针扫描。

可是考试的时候打了一个n^n的暴力。菜死了

正解是二分+树形dp+二分(二分的边界很重要要不卡死)

#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<queue>
#include<deque>
#include<bitset>
#include<set>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<iomanip>
using namespace std;
inline long long read()
{
    long long x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void put(long long x)
{
    if(x==0){putchar('0');putchar('\n');return;}
    if(x<0)putchar('-'),x=-x;
    long long num=0;char ch[50];
    while(x)ch[++num]=x%10+'0',x/=10;
    while(num)putchar(ch[num--]);
    putchar('\n');return;
}
const long long maxn=50002;
long long n,m;
long long lin[maxn<<1],nex[maxn<<1],ver[maxn<<1],e[maxn<<1],len=0;
int ans=0,u=0,cnt=0;
int f[maxn],v[maxn];//f[i]表示以i为根节点所能拼成最多的道路。
int q[maxn<<2],t=0;
//v[i]表示当前节点还剩下的最长链
void add(long long x,long long y,long long z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
//赛道修建100分做法-->>树形dp
int check1(int x,int p)
{
    int sum=0;
    for(int i=1,j=t;i<j;i++)
    {
        if(i==x)continue;
        if(j==x)j--;
        if(q[i]+q[j]>=p)sum++,j--;
    }
    if(sum>=u)return 1;
    return 0;
}
void dp(int x,int p,int fa)
{
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==fa)continue;
        dp(tn,p,x);
    }
    t=0,u=0;;cnt=0;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==fa)continue;
        f[x]+=f[tn];
        if(e[i]+v[tn]>=p){cnt++;continue;}
        q[++t]=e[i]+v[tn];
    }
    sort(q+1,q+1+t);
    for(int i=1,j=t;i<j;i++)if(q[i]+q[j]>=p)u++,j--;
    int l=0,r=t;//再次二分枚举哪条边不用来更新v[x]
    while(l+1<r)
    {
        int mid=(l+r)>>1;
        if(check1(mid,p)==1)l=mid;
        else r=mid;
    }
    if(check1(r,p)==1)v[x]=q[r];
    else v[x]=q[l];
    f[x]+=cnt+u;
}
int check(int x)
{
    memset(f,0,sizeof(f));
    memset(v,0,sizeof(v));
    dp(1,x,0);
    if(f[1]>=m)return 1;
    else return 0;
}
void wy()//闻道玉门犹被遮
{
    int l=1,r=ans;
    while(l+1<r)//二分枚举当前修建的道路长度
    {
        int mid=(l+r)>>1;
        if(check(mid)==1)l=mid;
        else r=mid;
    }
    if(check(r)==1)put(r);
    else put(l);
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<n;i++)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
        ans+=z;
    }
    wy();//应将性命逐轻车
    return 0;
}

就这样没了 300分很简单。

posted @ 2018-12-18 13:09  chdy  阅读(262)  评论(0编辑  收藏  举报