无题

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

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

第一题其实还挺简单的,只不过当时我认为我的算法天衣无缝,结果只拿到了中间的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号的所有题目,我理解的还比较透彻。只是如果能在考试的的时候写出来就好了。

原文地址:https://www.cnblogs.com/raochenxing/p/11173852.html