• 无题


    在杭州集训五天了,现在才想起来写博客.....

    主要还是因为今天的题目我写出来了,想发篇博客记录下。

    第一题其实还挺简单的,只不过当时我认为我的算法天衣无缝,结果只拿到了中间的30分;

    下面来说下正解:

    O(nlogn)算法:一开始的朴素算法就是开一个二维数组,暴力枚举每一个的情况,但这种方法只限于数组范围小的情况,后面几组测试点的数据都很大,显然这么做是不完美的,仔细思考一番后我们发现:对于行和列,对于一个点,我们只要判断其是否存在在同一行同一列的前驱和后继就好了,我们可以通过双关键字排序做到。

    对于行:以x为第一关键字,y为第二关键字排序,判断它前一个点x值是否相同,后一个点x值是否相同。列也一样。

    对于斜着的情况,我们也可以用双关键字排序做到,如何修改排序操作,我们要把每一斜线上的点放在一起。

    对于左上向右下斜:以x-y为第一关键字,x+y为第二关键字排序.

    对于右上向左下斜:以x+y为第一关键字,x-y为第二关键字排序。

    做4遍排序,再统计答案。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    struct knight{
        int x,y,num;
    }k[100005];
    int n,m;
    int wudi[100005];
    int num2[9];
    bool cmp1(knight a,knight b)
    {
        if(a.y==b.y)
        return a.x<b.x;
        else return a.y<b.y;
    }
    void Solve1()
    {
        for(int i=1;i<=m;i++)
        {
            if(k[i].y==k[i-1].y)
            wudi[k[i].num]++;
            if(k[i].y==k[i+1].y)
            wudi[k[i].num]++;
        }
    }
    bool cmp2(knight a,knight b)
    {
        if(a.x==b.x) return a.y<b.y;
        else return a.x<b.x;
    }
    void Solve2()
    {
        for(int i=1;i<=m;i++)
        {
            if(k[i].x==k[i-1].x)
            wudi[k[i].num]++;
            if(k[i].x==k[i+1].x)
            wudi[k[i].num]++;
        }
    }
    bool cmp3(knight a,knight b)
    {
        if(a.x+a.y==b.x+b.y)
        return a.x-a.y<b.x-b.y;
        else return a.x+a.y<b.x+b.y;
    }
    void Solve3()
    {
        for(int i=1;i<=m;i++)
        {
            if(k[i].x+k[i].y==k[i-1].x+k[i-1].y)
            wudi[k[i].num]++;
            if(k[i].x+k[i].y==k[i+1].x+k[i+1].y)
            wudi[k[i].num]++;
        }
    }
    bool cmp4(knight a,knight b)
    {
        if(a.x-a.y==b.x-b.y)
        return a.x+a.y<b.x+b.y;
        else return a.x-a.y<b.x-b.y;
    }
    void Solve4()
    {
        for(int i=1;i<=m;i++)
        {
            if(k[i].x-k[i].y==k[i-1].x-k[i-1].y)
            wudi[k[i].num]++;
            if(k[i].x-k[i].y==k[i+1].x-k[i+1].y)
            wudi[k[i].num]++;
        }
    }
    int main(){
        scanf("%d %d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            cin>>k[i].x>>k[i].y;
            k[i].num=i;
        }
        sort(k+1,k+1+m,cmp1);
        Solve1();
        sort(k+1,k+1+m,cmp2);
        Solve2();
        sort(k+1,k+1+m,cmp3);
        Solve3();
        sort(k+1,k+1+m,cmp4);
        Solve4();
        for(int i=1;i<=m;i++)
            num2[wudi[i]]++;
        for(int i=0;i<=8;i++)
            cout<<num2[i]<<" ";
        return 0;
    }

    这份代码当初我写的时候一直爆零,后来发现是n和m搞混了....

    第二题这个题面和那张图真是让人无力吐槽啊.....

    考试的时候我可能有点脑残了,竟然没想到可以用DP写,我只看见了那个的30%的数据,

    显然可知的情况说明整个序列是降序的,此时把除了最大的那个点即第一个点之外的点都

    移一遍即可。

    当∑n<=1000时,我们考虑简化题意,我们可以得到 :最优解不可能把同一个数字移动 次及以上。既然我们可以一次把它移到正确的位置上,那么这个
    数字就可以理解为直接被移走了。
    那么问题就变成了:删除几个数字,使得数列单调递增,所求的是移走数字的最小和。 使得删除的和最小,就意味
    着留下的和最大。
    那么问题就变成了:保留几个单调递增的数字,使得和最大。
    这个直接O(n²)DP就可以解决

    而对于100%的数据,我们考虑优化O(n²)的DP;发现前面更新f[i]时

    当j<i&&a[j]≤a[i]时f[j]可以转移到f[i]
    用线段树维护一个前缀max,每做完一个i,将f[i]加入线段树。
    时间复杂度O(nlogn)。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int T,n;
    int ji[1000000+10];
    bool flag,hhh;
    long long uuu,sum,cnt;
    long long b[1000000+10],c[1000000+10];
    map<int,int>q;
    long long f;
    struct super_tree{
        int l,r;
        long long sum;
    }t[4000000+10];
    long long rd()
    {
        long long a=0;
        char s;
        s=getchar();
        while(s<'0'||s>'9')
        s=getchar();
        while(s>='0'&&s<='9')
        {
            a=a*10+s-'0';
            s=getchar();
        }
        return a;
    }
    void build(int p,int l,int r)
    {
        t[p].l=l;
        t[p].r=r;
        t[p].sum=0;
        if(l==r)
            return;
        int mid=(l+r)>>1;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
    }
    void change(int p,int l,int r,long long v)
    {
        if(t[p].l>=l&&t[p].r<=r)
        {
            t[p].sum=max(v,t[p].sum);
            return;
        }
        int mid=(t[p].l+t[p].r)>>1;
        if(mid>=l) change(p*2,l,r,v);
        if(mid<r) change(p*2+1,l,r,v);
        t[p].sum=max(t[p*2].sum,t[p*2+1].sum);
    }
    long long query(int p,int l,int r)
    {
        if(t[p].l>=l&&t[p].r<=r)
        {
            return t[p].sum;
        }
        long long ans=0;
        int mid=(t[p].l+t[p].r)>>1;
        if(mid>=l) ans=max(ans,query(p*2,l,r));
        if(mid<r) ans=max(ans,query(p*2+1,l,r));
         return ans;
    }
    void Solve(){
        n=rd();
        for(int i=1;i<=n;i++)
        {
            ji[i]=rd();
            b[i]=ji[i];
            sum+=ji[i];
        }
        cnt=0;
        sort(b+1,b+1+n);
        for(int i=1;i<=n;i++)
        {
            if(i==1||b[i]!=b[i-1])
            q[b[i]]=++cnt;
            else q[b[i]]=cnt;
        }
        for(int i=1;i<=n;i++)
        c[i]=q[ji[i]];
        build(1,1,n);
        for(int i=1;i<=n;i++)
        {
            f=query(1,1,c[i])+ji[i];
            change(1,c[i],c[i],f);
            uuu=max(uuu,f);
        }
        cout<<sum-uuu<<endl;
        for(int i=1;i<=n;i++)
        q[ji[i]]=0;
    }
    int main(){
        T=rd();
        while(T--)
        {
            uuu=0;
            sum=0;
            Solve();
        }
        return 0;
    }

     这就是11号的所有题目,我理解的还比较透彻。只是如果能在考试的的时候写出来就好了。

  • 相关阅读:
    【BZOJ4637】期望 Kruskal+矩阵树定理
    IEnumerable是什么
    (转)IIS windows认证
    使用pgrouting进行最短路径搜索
    Nginx设置防止IP及非配置域名访问
    java 调用c# web api 代码
    一台机器部署多个tomcat服务 nginx反向代理多个服务 笔记
    利用Kettle 从Excel中抽取数据写入SQLite
    sql server 统计信息
    sql server 索引碎片相关问题
  • 原文地址:https://www.cnblogs.com/raochenxing/p/11173852.html
Copyright © 2020-2023  润新知