2017-10-21离线赛总结

来源:互联网 发布:北京知豆电动汽车4s店 编辑:程序博客网 时间:2024/06/03 18:06

失分小结:
估分:240
实际分数:250
本来觉得240应该是一个挺正常的分数,但是很多人都考得不好
感觉第三题的思维还是太局限了,就算打暴力也只能想着模拟。。

考试流程:
前两题打得很顺,大概就一个小时多一点,最后一题玄学debug到考试结束
最后十分钟灵感爆发,想到80分解法。。

题解:
第一题:
应该是一个裸的前缀和题目
反正这种题打完之后对拍一般就不会出什么问题了
第二题:
个人感觉这道题是需要一些想法的。。
n<=100000
纯粹的LIS肯定过不掉,要加上些数据结构维护(像什么BIT,线段树。。)
然后就是如何知道它是1、2、3
我的方法复杂度可能有点高 纯粹是因为线段树常数大的缘故
就是正着扫一遍,反着扫一遍
像普通LIS一样求出他的dp值
大约是dp1[i]+dp2[i]+1==ans 时说明这个点在LIS上
然后看多少个dp[i]相同,若这个dp[i]是唯一的
那么这个点必须出现在LIS上

代码实现:

int A[M],B[M];int Sum1[M],Sum2[M];bool mark[M];int Cnt[M];struct Tree{    int l,r,sum;}tree[M<<2];void build(int l,int r,int p){    tree[p].l=l,tree[p].r=r,tree[p].sum=0;    if(l==r)return;    int mid=(l+r)>>1;    build(l,mid,p<<1);    build(mid+1,r,p<<1|1);}void update(int x,int a,int p){    if(tree[p].l==tree[p].r){        tree[p].sum=Max(tree[p].sum,a);        return;    }    int mid=(tree[p].l+tree[p].r)>>1;    if(mid>=x)update(x,a,p<<1);    else update(x,a,p<<1|1);    tree[p].sum=Max(tree[p<<1].sum,tree[p<<1|1].sum);}int query(int l,int r,int p){    if(tree[p].l==l&&tree[p].r==r){        return tree[p].sum;    }    int mid=(tree[p].l+tree[p].r)>>1;    if(mid>=r)return query(l,r,p<<1);    else if(mid<l)return query(l,r,p<<1|1);    else return Max(query(l,mid,p<<1),query(mid+1,r,p<<1|1));}int main(){    int n;    scanf("%d",&n);    FOR(i,1,n)scanf("%d",&A[i]),B[i]=A[i];    sort(B+1,B+n+1);    int len=unique(B+1,B+n+1)-B-1;    build(1,len,1);    FOR(i,1,n){        int id=lower_bound(B+1,B+len+1,A[i])-B;        int mx=0;        if(id>1){            mx=query(1,id-1,1);            Sum1[i]=mx;        }        update(id,mx+1,1);    }    int tmp=query(1,len,1);    build(1,len,1);    DOR(i,n,1){        int id=lower_bound(B+1,B+len+1,A[i])-B;        int mx=0;        if(id<len){            mx=query(id+1,len,1);            Sum2[i]=mx;        }        update(id,mx+1,1);    }    FOR(i,1,n){        if(Sum1[i]+Sum2[i]+1!=tmp)continue;        mark[i]=1;        Cnt[Sum1[i]]++;    }    FOR(i,1,n){        if(!mark[i])printf("1");        else {            if(Cnt[Sum1[i]]==1)printf("3");            else printf("2");        }    }    return 0;}

第三题:
考试时yy这题肯定是二分加dp判定
这题的状态也比较明显,也就是第一行填到第几个,第二行填到第几个
更新也就是单纯填第一行,单纯填第二行,或者两行一起填
然后复杂度会有些爆炸。。
我考试时的复杂度是O(logSn3m) 只能过二十分的数据
然后优化最后一层,O(logSlognn2m)能卡到50分 测评机告诉我的。。
是地球人都看得出这题的复杂度应该是O(logSnm)
虽然后来Komachi dalao写出了O(logSm2)的解法
然后就加了组hack数据害人
相较于之前最愚蠢的dp转移,我们可以发现,其实没有什么必要分别枚举上下两行的状态
只用预处理最多填到哪列 然后一列一列枚举 复杂度为O(logSnm)

讲一下Komachi的玄学写法
预处理从某点铺画最多到哪个点
然后一步一步铺过去,最多铺m幅
当然之前要枚举从第一幅后开始铺
dp[i]表示铺到第几幅最多铺到第几列

代码实现:

#include<cstdio>#include<cstring>#define FOR(i,x,y) for(int i=(x);i<=(y);i++)#define ll long longinline void chk_mx(int &x,int y){if(x<y)x=y;}inline int min(int x,int y){return x<y?x:y;}//O(m*m*logS)int A[3][20005];int n,m;int Nxt[3][20005],W[105];void init(int a,int mx){    int l=1,r=1;    while(l<=n){        while(r<=n&&A[a][r]-A[a][l-1]<=mx)r++;  //l->r        Nxt[a][l]=r-1;        l++;    }}bool DP(int mx){    memset(Nxt,0,sizeof(Nxt));    memset(W,0,sizeof(W));    FOR(i,0,2)init(i,mx);    FOR(i,0,m-1){        int a=Nxt[0][W[i]+1],b=Nxt[1][W[i]+1];        chk_mx(W[i+1],Nxt[2][W[i]+1]);        int cnt=i+2;        while(cnt<=m){            chk_mx(W[cnt],min(a,b));            if(W[cnt]==n)return 1;            if(a<b)a=Nxt[0][a+1];            else b=Nxt[1][b+1];            cnt++;        }    }    return W[m]==n;}int main() {    scanf("%d%d",&n,&m);    int l=0,r=0,res=0;    FOR(k,0,1)FOR(i,1,n){        scanf("%d",&A[k][i]);        if(l<A[k][i])l=A[k][i];        r+=A[k][i];    }    FOR(i,0,1)FOR(j,1,n+1)A[i][j]+=A[i][j-1];    FOR(i,1,n+1)A[2][i]=A[0][i]+A[1][i];    while(l<=r){        int mid=(l+r)>>1;        if(DP(mid))r=mid-1,res=mid;        else l=mid+1;    }    printf("%d\n",res);    return 0;}
原创粉丝点击