【20181110】noip2018 day1题解

题解&反思

没做过原题*1,用的不是大部分做法
首先贪心是每次都刷最长能刷段,所以先造一个取min的st表,然后用一个vector存一下每个值的位置,每到一段就查一下最小值,然后再这个值的vector里二分一下要断的区间,递归处理即可
这样比每次扫区间的在极限数据上要快一些……直接分治是可以卡成1e8的

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
const int N=100005;
int n,a[N],st[20][N],bt[N];
long long ans;
vector<int>b[10005];
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>'9'||p<'0')
    {
        if(p=='-')
            f=-1;
        p=getchar();
    }
    while(p>='0'&&p<='9')
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
int ques(int l,int r)
{
    int k=bt[r-l+1];
    return min(st[k][l],st[k][r-(1<<k)+1]);
}
int ef1(int w,int v)
{
    int l=0,r=b[w].size()-1,ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(b[w][mid]>=v)
            r=mid-1,ans=mid;
        else
            l=mid+1;
    }
    return ans;
}
int ef2(int w,int v)
{
    int l=0,r=b[w].size()-1,ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(b[w][mid]<=v)
            l=mid+1,ans=mid;
        else
            r=mid-1;
    }
    return ans;
}
void wk(int l,int r,int v)
{
    if(l>r)
        return;
    int mn=ques(l,r);
    ans+=mn-v;
    if(l==r)
        return;
    int ll=ef1(mn,l),rr=ef2(mn,r);
    wk(l,b[mn][ll]-1,mn);
    for(int i=ll+1;i<=rr;i++)
        wk(b[mn][i-1]+1,b[mn][i]-1,mn);
    wk(b[mn][rr]+1,r,mn);
}
void clc(int l,int r,int v)
{
    int nw=1e9;
    for(int i=l;i<=r;i++)
        nw=min(nw,a[i]-v);
    ans+=nw;
    if(l==r)
        return;
    for(int i=l,j=l;i<=r;i=j+1)
    {
        j=i;
        if(a[j]-v>nw)
        {
            while(j<r&&a[j+1]-v>nw)
                j++;
            clc(i,j,v+nw);
        }
    }
}
int main()
{
    n=read();
    bt[0]=-1;
    for(int i=1;i<=n;i++)
        a[i]=st[0][i]=read(),bt[i]=bt[i>>1]+1,b[a[i]].push_back(i);
    for(int i=1;i<=17;i++)
        for(int j=1;j+(1<<i)-1<=n;j++)
            st[i][j]=min(st[i-1][j],st[i-1][j+(1<<(i-1))]);
    if(n>1000)
        wk(1,n,0);
    else
        clc(1,n,0);
    printf("%lld\n",ans);
    return 0;
}


题解&反思

没做过原题*2
贪心的考虑答案即可一定是原来集合的子集,然后去掉的就是能被别的数表示的数
先sort一下,然后注意到ai≤25000,所以就直接用v[i]表示i这个数能不能被表示,从小到大,用不能表示数更新v[x]:(v[x-a[i]]==1||x%a[i]==0)即可(其实就是背包)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=105;
int T,n,a[N],ans;
bool v[25005];
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>'9'||p<'0')
    {
        if(p=='-')
            f=-1;
        p=getchar();
    }
    while(p>='0'&&p<='9')
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
int main()
{
    T=read();
    while(T--)
    {
        memset(v,0,sizeof(v));
        ans=0;
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        sort(a+1,a+1+n);
        for(int i=1;i<=n;i++)
            if(!v[a[i]])
            {
                ans++;
                for(int j=1;j<=a[n];j++)
                {
                    if(v[j]&&j+a[i]<=a[n])
                        v[j+a[i]]=1;
                    if(j%a[i]==0)
                        v[j]=1;
                }
            }
        printf("%d\n",ans);
    }
    return 0;
}
/*
2
4
3 19 10 6
5
11 29 13 19 17
*/



题解&反思

没做过原题*3
考场上想出正解但是算错复杂度,就写了55暴力挂一个假的二分+贪心,民间数据得分在90~100之间正式成绩说不定就卡成55了……
正解也容易,就是二分+贪心,贪心是设f[u]为u最长能向上贡献的链,treedp的过程中,每次凑出一个长度>=mid的链就tot++,最后看是否tot>=m
具体的判断是先把一个点u长度>=mid的f[son]直接加tot,剩下f[son]的排个序,然后先用双指针看最多能两两拼多少条长度>=mid的链,如果全都用上就f[u]=0,否则二分f[u]的最大值,判断就是删掉一个然后看是否还能拼出那么多条>=mid的链

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=500005;
int n,m,h[N],cnt,f[N],tot,a[N],top;
struct qwe
{
    int ne,to,va;
}e[N<<1];
int read()
{
    int r=0,f=1;
    char p=getchar();
    while(p>'9'||p<'0')
    {
        if(p=='-')
            f=-1;
        p=getchar();
    }
    while(p>='0'&&p<='9')
    {
        r=r*10+p-48;
        p=getchar();
    }
    return r*f;
}
void add(int u,int v,int w)
{
    cnt++;
    e[cnt].ne=h[u];
    e[cnt].to=v;
    e[cnt].va=w;
    h[u]=cnt;
}
int wk(int de,int w)
{
    int l=1,r=top,co=0;
    while(l<r)
    {
        if(r==de)
            r--;
        while(l<r&&a[l]+a[r]<w)
            l++;
        if(l==de)
            l++;
        if(l<r)
            co++,l++,r--;
    }
    return co;
}
void dfs(int u,int fa,int w)
{
    for(int i=h[u];i;i=e[i].ne)
        if(e[i].to!=fa)
            dfs(e[i].to,u,w);
    top=0;
    for(int i=h[u];i;i=e[i].ne)
        if(e[i].to!=fa)
            f[e[i].to]+e[i].va>=w?tot++:a[++top]=f[e[i].to]+e[i].va;
    sort(a+1,a+1+top);
    int con=wk(top+1,w);
    tot+=con;
    if(2*con==top)
        f[u]=0;
    else
    {
        int l=1,r=top;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(wk(mid,w)==con)
                l=mid+1,f[u]=a[mid];
            else
                r=mid-1;
        }
    }
}
bool ok(int w)
{
    tot=0;
    dfs(1,0,w);
    return tot>=m;
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read(),z=read();
        add(x,y,z),add(y,x,z);
    }
    int l=0,r=5e8,ans=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(ok(mid))
            l=mid+1,ans=mid;
        else
            r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}

总结

还没出分……总之应该除了t3虚以外发挥还可以orz

猜你喜欢

转载自www.cnblogs.com/lokiii/p/9957884.html