2017/8/11 离线赛

来源:互联网 发布:演唱会抢票攻略 知乎 编辑:程序博客网 时间:2024/05/22 06:53

T1 小宝卖猪

    考试的时候因为忘乘上t结果爆零了,我也是很难过啊。题目很好想,对于i位置,我们选择重量为x的猪卖出,则收益为(DiPit)x,所以对于任意的重量的猪,在i位置卖出DiPit是不变的,我们只要对于每个点选出最合适的猪就好了。也就相当于是两个数组,一个是每个位置的DiPit数组,一个是Wi数组,我们要求他们两两相乘的积的最大值,这里要用到排序不等式,其实也很好想,就是把两个数组都从小到大排序,然后依次相乘即可,可以用相邻交换的方式证明。

int n,t;int w[M],d[M],p[M];ll tmp[M];ll ans;int main(){    Rd(n);Rd(t);    for(int i=1;i<=n;i++)Rd(w[i]);    for(int i=1;i<=n;i++)Rd(d[i]);    for(int i=1;i<=n;i++)Rd(p[i]);    for(int i=1;i<=n;i++)    tmp[i]=-1LL*d[i]*t+p[i];    sort(tmp+1,tmp+n+1);    sort(w+1,w+n+1);    for(int i=1;i<=n;i++)    ans+=1LL*tmp[i]*w[i];    Pt(ans);    putchar('\n');    return 0;}

T2 放书

    考试的时候我没有想到怎么把前面的书取出来放到后面去,然后敲了一个只能从后往前放的dp。我实际上掉到了题目的坑里了,我一直想着拿出的书要怎么放,没有想到书拿出来后可以最后再放进去,根本没必要拿一个放一个。对于所有拿出来的书,只有剩余的序列中存在等高的书才能保证答案更优,如果不存在等高的书会导致白白增加一个混乱度,并且浪费拿书的机会,于是dp的思想就出来了。

    我们定义dp[i][j][k][s]表示遍历第i本书时,剩余的书的最后一本为j,拿了k本书,剩余的书的高度集合为ss就是[0,255],用状压表示是否存在某本书)。那么dp转移有两种:一种是把第i本书拿出来,一种是不拿第i本书。求答案时,我们要保证剩余的书里有之前每种高度的书,这样才能保证拿出来的书没白拿。用刷表即可,然后再滚动一下。但是,有一种情况要特殊考虑,就是把书拿出来放到最后,我们直接把s加上这个高度的书然后答案+1即可。

void check(int &a,int b){    if(a==-1||a>b)a=b;}int n,K;int h[M];int bit[8];int dp[2][9][105][256];int main(){    int S=0;    memset(dp,-1,sizeof(dp));    Rd(n);Rd(K);    for(int i=1;i<=n;i++){        Rd(h[i]);        h[i]-=25;        S|=(1<<h[i]);    }    for(int i=0;i<8;i++)    bit[i]=1<<i;    bool cur=0;    dp[cur][h[1]][0][bit[h[1]]]=1;    dp[cur][8][1][0]=0;    for(int i=2;i<=n;i++){        cur=!cur;        memset(dp[cur],-1,sizeof(dp[cur]));        for(int j=0;j<=8;j++)        for(int k=0;k<=K;k++)        for(int s=0;s<256;s++)        if(~dp[!cur][j][k][s]){            if(k<K){                check(dp[cur][j][k+1][s],dp[!cur][j][k][s]);                check(dp[cur][j][k+1][s|bit[h[i]]],dp[!cur][j][k][s]+1);            }            check(dp[cur][h[i]][k][s|bit[h[i]]],dp[!cur][j][k][s]+(j!=h[i]));        }    }    int ans=-1;    for(int i=0;i<8;i++)    for(int j=0;j<=K;j++)    if(~dp[cur][i][j][S])check(ans,dp[cur][i][j][S]);    Pt(ans);    putchar('\n');    return 0;}

T3 Crash的旅行计划

    这题给了五档切分,考试的时候我敲了两档,但是第二档敲爆了,还是再敲一遍吧。

    P1:n只有1000,所以O(n2)就可以过了,每次查询时从该点出发找到最大的权值即可。

    P2:因为是一条1~n的链,实际上就把问题转化了,每次询问求包含该点的最大连续和,用线段树维护每个点的前缀和后缀和的最大值即可。

    P3:第三档是一颗完全二叉树,每个节点最多只有两个儿子,我们只要记录它往左走能得到的最大值l和往右走能得到的最大值r即可,假如最大值是负的则赋成0(即不选这个点的儿子,只选它本身)。查询一个点的时候,我们要么从这个点往下走,要么往上走,往下走的情况已经直接处理出来了,即max(l[x],r[x])+A[x],往上走就是先走到自己的父亲,在让从父亲往另一边走,过程中要记录自己到父亲的路上得到的值。更新一个点的时候,因为它只会对自己父亲的lr产生影响,我们只要一直往上走更新lr即可。

    P4:由于深度最大只有40,因此我们可以考虑和上面二叉树类似的做法,我们用multiset存一个节点的所有儿子往下走能得到的最大值,最后再insert一个0(有可能儿子都是负的,还不如不走)。对于查询操作,我们跟P3一样,一步一步往上走飞,假如走到的点的multiset的最大值是由要查询的点得来的,我们就找次大的儿子。对于更新操作,由于父亲的multiset里还存着要更新的儿子,所以要先删去儿子,要从上往下删,然后再从上往下更新。

    P5:……

#include<set>#include<stdio.h>#include<string.h>#include<algorithm>#include<iostream>#define M 100005using namespace std;template <class T>inline void Rd(T &res){    char c;res=0;int k=1;    while(c=getchar(),c<48&&c!='-');    if(c=='-'){k=-1;c='0';}    do{        res=(res<<3)+(res<<1)+(c^48);    }while(c=getchar(),c>=48);    res*=k;}template <class T>inline void Pt(T res){    if(res<0){        putchar('-');        res=-res;    }    if(res>=10)Pt(res/10);    putchar(res%10+48);}struct edge{    int v,nxt;}e[M<<1];struct opr{    bool f;    int u,x;}Q[M];int n,m;int A[M],head[M],edgecnt;int fa[M],dep[M],L[M],R[M],top[M],sz[M],son[M],tim;int LCA(int u,int v){    while(top[u]!=top[v]){        if(dep[top[u]]>dep[top[v]])u=fa[top[u]];        else v=fa[top[v]];    }    return dep[u]<dep[v]?u:v;}struct P1{//n<=1000    int ans;    void dfs(int x,int t,int res){        if(res>ans)ans=res;        for(int i=head[x];~i;i=e[i].nxt)        if(e[i].v!=t)dfs(e[i].v,x,res+A[e[i].v]);    }    void solve(){        for(int i=1;i<=m;i++)        if(Q[i].f){            int u=Q[i].u;            ans=A[u];            dfs(u,-1,A[u]);            Pt(ans);            putchar('\n');        }else A[Q[i].u]=Q[i].x;    }}P1;struct P2{//链    struct Segment_Tree{        int sum[M];        struct node{            int L,R,mx,add;        }tree[M<<2];        void up(int p){            tree[p].mx=max(tree[p<<1].mx,tree[p<<1|1].mx);        }        void down(int p){            if(!tree[p].add)return;            tree[p<<1].add+=tree[p].add;            tree[p<<1].mx+=tree[p].add;            tree[p<<1|1].add+=tree[p].add;            tree[p<<1|1].mx+=tree[p].add;            tree[p].add=0;        }        void update(int L,int R,int a,int p){            if(tree[p].L==L&&tree[p].R==R){                tree[p].add+=a;                tree[p].mx+=a;                return;            }            down(p);            int mid=(tree[p].L+tree[p].R)>>1;            if(R<=mid)update(L,R,a,p<<1);            else if(L>mid)update(L,R,a,p<<1|1);            else{update(L,mid,a,p<<1);update(mid+1,R,a,p<<1|1);}            up(p);        }        int query(int L,int R,int p){            if(tree[p].L==L&&tree[p].R==R)return tree[p].mx;            down(p);            int mid=(tree[p].L+tree[p].R)>>1;            if(R<=mid)return query(L,R,p<<1);            if(L>mid)return query(L,R,p<<1|1);            return max(query(L,mid,p<<1),query(mid+1,R,p<<1|1));        }        void build(int L,int R,int p){            tree[p]=(node){L,R,0,0};            if(L==R){                tree[p].mx=sum[L];                return;            }            int mid=(L+R)>>1;            build(L,mid,p<<1);            build(mid+1,R,p<<1|1);            up(p);        }    }L,R;    void solve(){        L.sum[0]=R.sum[n+1]=0;        for(int i=1;i<=n;i++)        L.sum[i]=L.sum[i-1]+A[i];        for(int i=n;i>=1;i--)        R.sum[i]=R.sum[i+1]+A[i];        L.build(1,n,1);R.build(1,n,1);        for(int i=1;i<=m;i++)        if(Q[i].f){            int u=Q[i].u;            Pt(max(L.query(u,n,1)-L.query(u-1,u-1,1),R.query(1,u,1)-R.query(u+1,u+1,1)));            putchar('\n');        }else{            int u=Q[i].u,x=Q[i].x;            L.update(u,n,x-A[u],1);            R.update(1,u,x-A[u],1);            A[u]=x;        }    }}P2;struct P3{//二叉树    int l[M],r[M],ans;    void Init(int x){        int L=x<<1,R=L|1;        if(L<=n){            Init(L);            l[x]=max(max(l[L],r[L])+A[L],0);        }        if(R<=n){            Init(R);            r[x]=max(max(l[R],r[R])+A[R],0);        }    }    void calc(int x,int t,int res){        if((x<<1)==t)ans=max(ans,res+A[x]+r[x]);        else ans=max(ans,res+A[x]+l[x]);        if(x>>1)calc(x>>1,x,res+A[x]);    }    void up(int x){        int L=x<<1,R=L|1;        if(L<=n)l[x]=max(max(l[L],r[L])+A[L],0);        if(R<=n)r[x]=max(max(l[R],r[R])+A[R],0);        if(x>>1)up(x>>1);    }    void solve(){        Init(1);        for(int i=1;i<=m;i++)        if(Q[i].f){            int u=Q[i].u;            ans=max(l[u],r[u])+A[u];            if(u>>1)calc(u>>1,u,A[u]);            Pt(ans);            putchar('\n');        }else{            int u=Q[i].u,x=Q[i].x;            A[u]=x;            if(u>>1)up(u>>1);        }    }}P3;struct P4{    typedef pair<int,int> P;    multiset<P>G[M];    multiset<P>::iterator it;    int ans;    void dfs(int x,int t){        G[x].insert(P(0,0));        for(int i=head[x];~i;i=e[i].nxt){            int v=e[i].v;            if(v==t)continue;            dfs(v,x);            G[x].insert(P((*--G[v].end()).first+A[v],v));        }    }    void calc(int x,int t,int res){        it=--G[x].end();        if((*it).second==t)--it;        ans=max(ans,(*it).first+res+A[x]);        if(x!=1)calc(fa[x],x,res+A[x]);    }    void del(int x,int t){        if(x!=1)del(fa[x],x);        G[x].erase(G[x].find(P((*--G[t].end()).first+A[t],t)));    }    void up(int x,int t){        G[x].insert(P((*--G[t].end()).first+A[t],t));        if(x!=1)up(fa[x],x);    }    void solve(){        dfs(1,-1);        for(int i=1;i<=m;i++)        if(Q[i].f){            int u=Q[i].u;            ans=(*--G[u].end()).first+A[u];            if(u!=1)calc(fa[u],u,A[u]);            Pt(ans);            putchar('\n');        }else{            int u=Q[i].u,x=Q[i].x;            if(u!=1){                del(fa[u],u);                A[u]=x;                up(fa[u],u);            }        }    }}P4;struct P5{    void solve(){    }}P5;char s[10];int degree[M];void add_edge(int u,int v){    e[++edgecnt]=(edge){v,head[u]};head[u]=edgecnt;}bool chk1(){    int cnt=0;    for(int i=1;i<=n;i++)    if(degree[i]==1)cnt++;    return cnt==2;};bool chk2(){    for(int i=2;i<=n;i++)    if(fa[i]!=(i>>1))return false;    return true;};bool chk3(){    for(int i=1;i<=n;i++)    if(dep[i]>40)return false;    return true;};void dfs(int x,int t){    sz[x]=1;    fa[x]=t;    L[x]=++tim;    if(~t)dep[x]=dep[t]+1;    for(int i=head[x];~i;i=e[i].nxt){        int v=e[i].v;        if(v==t)continue;        dfs(v,x);        sz[x]+=sz[v];        if(sz[v]>sz[son[x]])son[x]=v;    }    R[x]=tim;}void rdfs(int x,int t,int tp){    top[x]=tp;    if(son[x])rdfs(son[x],x,tp);    for(int i=head[x];~i;i=e[i].nxt){        int v=e[i].v;        if(v==t||v==son[x])continue;        rdfs(v,x,v);    }}int main(){    memset(head,-1,sizeof(head));    int a,b;    Rd(n);    for(int i=1;i<=n;i++)Rd(A[i]);    for(int i=1;i<n;i++){        Rd(a);Rd(b);        add_edge(a,b);        add_edge(b,a);        degree[a]++;        degree[b]++;    }    dfs(1,-1);    rdfs(1,-1,1);    while(1){        scanf("%s",s);        if(s[0]=='C'){            Rd(a);Rd(b);            Q[++m]=(opr){0,a,b};        }else if(s[0]=='Q'){            Rd(a);            Q[++m]=(opr){1,a,0};        }else break;    }    if(n<=1000)P1.solve();    if(chk1())P2.solve();    else if(chk2())P3.solve();    else if(chk3())P4.solve();    else P5.solve();    return 0;}

    有时候不能顺着题目意思来,要跳出来看问题,不然容易陷进去,把简单的东西想难了。

原创粉丝点击