NOIP2018 提高组 Day1 题解

今年原地起爆,没进复赛,看别的dalao码代码各种AK虐场心痒痒,来写个题解

Day2还在肝,就先写Day1吧……


T1

NOIP2013年原题,用个滚动数组就能水过去了……(不用应该也行吧)

做法大概是从头开始扫,记录每个位置的深度,然后读入一个深度,如果后面的更深就需要多填 后面的深度 − - 前面的深度 次

#include
using namespace std;
int n,a,b;
long long ans;
int main()
{
    scanf("%d%d",&n,&b);
    ans=b;
    for(int i=1;i<n;i++)
    {
        scanf("%d",&a);
        if(a>b) ans+=a-b;
        b=a;
    }
    printf("%lld",ans);
    return 0;
}

T2

明显的DP背包题,想不到的话使用DFS+DP可以获得65(洛咕数据)的好成绩

把面值排个序,从头向后扫,如果 f [ a [ i ] ] = 1 f[a[i]]=1 f[a[i]]=1则表示 a [ i ] a[i] a[i]是可以用前面的面值表示出来的,跳过,否则从 a [ i ] a[i] a[i]循环到 m a x n maxn maxn a [ i ] a[i] a[i]中的最大值),判断 f [ j − a [ i ] ] f[j-a[i]] f[ja[i]]能否被表示,可以的话就让 f [ j ] = 1 f[j]=1 f[j]=1即可

#include
using namespace std;
int n,a[105],f[25005],ans,maxn;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(f,0,sizeof(f));
        maxn=0;
        ans=0;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            maxn=max(maxn,a[i]);
        }
        sort(a+1,a+n+1);
        f[0]=1;
        for(int i=1;i<=n;i++)
        {
            if(f[a[i]]) continue;
            ans++;
            for(int j=a[i];j<=maxn;j++) f[j]|=f[j-a[i]];
        }
        printf("%d\n",ans);
    }
    return 0;
}

T3

这题就不那么简单了,容易想到的是用树形DP,同时题面要求的是“最短长度最长”,就是明显的二分答案

那么我们取 m i n ( a [ i ] ) min(a[i]) min(a[i])为左边界, Σ i = 1 n a [ i ] \Sigma_{i=1}^{n}a[i] Σi=1na[i]为右边界进行二分答案

用两个数组 f [ i ] f[i] f[i] g [ i ] g[i] g[i]分别表示 i i i为根节点的子树中有多少条长度 > = m i d >=mid >=mid,去掉满足条件的路径后从 i i i往上连最长的长度

在转移时将当前节点所有子节点的 g [ i ] g[i] g[i]加上 w [ i ] w[i] w[i]后加入一个 m u l t i s e t multiset multiset中,因为我们之后要先将长度满足 > = m i d >=mid >=mid的加到答案中,所以需要从大到小排序,而且不能去重,所以 m u l t i s e t multiset multiset是一个很好的选择

加入后从后往前扫 m u l t i s e t multiset multiset的元素,如果 > = m i d >=mid >=mid加到答案(即 f f f数组中),否则退出循环

然后我们从前往后扫 m u l t i s e t multiset multiset中的元素,然后用 l o w e r lower lower_ b o u n d bound bound m u l t i s e t multiset multiset中找能够与它配对的元素,找不到的话就用这个元素来更新 g [ i ] g[i] g[i],这样就可以得到最优解了

#include
using namespace std;
int cnt,fst[50005],nxt[100005],to[100005],w[100005];
int n,m,l=2147400000,r,mid,ans,f[50005],g[50005];
multiset <int> s;
multiset <int>::iterator it;
void AddEdge(int u,int v,int c)
{
    to[++cnt]=v;
    nxt[cnt]=fst[u];
    fst[u]=cnt;
    w[cnt]=c;
}
void Dfs(int u,int faz)
{
    for(int i=fst[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==faz) continue;
        Dfs(v,u);
        f[u]+=f[v];
    }
    for(int i=fst[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==faz) continue;
        s.insert(g[v]+w[i]);
    }
    while(!s.empty())
    {
        int now=*s.rbegin();
        if(now>=mid)
        {
            it=s.find(now);
            f[u]++;
            s.erase(it);
        }
        else break;
    }
    while(!s.empty())
    {
        int now=*s.begin();
        s.erase(s.begin());
        it=s.lower_bound(mid-now);
        if(it==s.end()) g[u]=now;
        else
        {
            f[u]++;
            s.erase(it);
        }
    }
}
int main()
{
    scanf("%d %d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int x,y,z;
        scanf("%d %d %d",&x,&y,&z);
        AddEdge(x,y,z);
        AddEdge(y,x,z);
        l=min(l,z);
        r+=z;
    }
    while(l<=r)
    {
        mid=l+r>>1;
        memset(f,0,sizeof(f));
        memset(g,0,sizeof(g));
        Dfs(1,0);
        if(f[1]>=m)
        {
            ans=max(ans,mid);
            l=mid+1;
        }
        else r=mid-1;
    }
    printf("%d",ans);
    return 0;
}

你可能感兴趣的:(c++,noip)