6.1 考试修改+总结

QAQ 终于回到衡中了,昨天做了一个晚上的火车,所以没来的及补题解

Em 儿童节快乐,撒花~~

第一题

NOIP难度?提到国王游戏都提醒你怎么做了

直接裸上排序不等式化简一下就可以了

具体化简过程就不再赘述了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
 
typedef long long LL;
const int maxn=100010;
int T,n;
struct OP{
    LL a,b;
}c[maxn];
LL sum,ans,tmp;
 
bool cmp(const OP &A,const OP &B){
    return A.a+B.b+max(A.b,B.a)<B.a+A.b+max(A.a,B.b);
}
void read(LL &num){
    num=0;char ch=getchar();
    while(ch<'!')ch=getchar();
    while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
 
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;++i)read(c[i].a),read(c[i].b);
        sort(c+1,c+n+1,cmp);
        sum=tmp=ans=0;
        for(int i=1;i<=n;++i){
            sum+=c[i].a;
            tmp=max(tmp,sum)+c[i].b;
        }ans=tmp;
        printf("%lld
",ans);
    }return 0;
}

第二题

貌似是在湖南雅礼培训的时候考过的原题?

现在看看是二维曼哈顿距离最小生成树的裸题

随便写写之后在树上做一下倍增就可以了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
 
const int maxn=100010;
const int oo=0x7fffffff;
int n,m,cnt=0;
int u,v;
struct Edge{
    int u,v,w;
}c[1000010];
int tmp[maxn];
int fa[maxn];
int anc[maxn][20];
int mx[maxn][20];
int dep[maxn];
int h[maxn],tot=0;
struct edge{
    int to,next,w;
}G[maxn<<1];
struct pos{
    int x,y,id;
}p[maxn],P[maxn];
struct BIT{
    int mn,p;
}t[maxn];
 
int ufs(int x){return fa[x]==x?x:fa[x]=ufs(fa[x]);}
bool cmp(const pos &A,const pos &B){
    if(A.x==B.x)return A.y<B.y;
    return A.x<B.x;
}
bool cmpdis(const Edge &A,const Edge &B){
    return A.w<B.w;
}
void add(int x,int y,int z){
    ++tot;G[tot].to=y;G[tot].next=h[x];G[tot].w=z;h[x]=tot;
}
int sqr(int x){return x*x;}
int dis(int i,int j){
    return abs(P[i].x-P[j].x)+abs(P[i].y-P[j].y);
}
int lowbit(int x){return x&(-x);}
void UPD(int x,int mn,int id){
    for(int i=x;i<=m;i+=lowbit(i)){
        if(t[i].mn>mn){
            t[i].mn=mn;t[i].p=id;
        }
    }return;
}
int ask(int x){
    int mn=oo,p=-1;
    for(int i=x;i>=1;i-=lowbit(i)){
        if(t[i].mn<mn){
            mn=t[i].mn;p=t[i].p;
        }
    }return p;
}
void make_Graph(){
    for(int flag=0;flag<4;++flag){
        if(flag==1||flag==3)for(int i=1;i<=n;++i)swap(p[i].x,p[i].y);
        else if(flag==2)for(int i=1;i<=n;++i)p[i].y=-p[i].y;
        for(int i=1;i<=n;++i)tmp[i]=p[i].x-p[i].y;
        sort(tmp+1,tmp+n+1);
        m=unique(tmp+1,tmp+n+1)-tmp-1;
        sort(p+1,p+n+1,cmp);
        for(int i=1;i<=n;++i)t[i].mn=oo,t[i].p=-1;
        for(int i=n;i>=1;--i){
            int Pos=lower_bound(tmp+1,tmp+m+1,p[i].x-p[i].y)-tmp;
            int cur=ask(Pos);
            if(cur!=-1){
                ++cnt;
                c[cnt].u=p[i].id;c[cnt].v=cur;
                c[cnt].w=dis(c[cnt].u,c[cnt].v);
            }UPD(Pos,p[i].x+p[i].y,p[i].id);
        }
    }return;
}
void Kruscal(){
    sort(c+1,c+cnt+1,cmpdis);
    for(int i=1;i<=n;++i)fa[i]=i;
    for(int i=1;i<=cnt;++i){
        int d1=ufs(c[i].u),d2=ufs(c[i].v);
        if(d1!=d2){
            fa[d1]=d2;
            add(c[i].u,c[i].v,c[i].w);
            add(c[i].v,c[i].u,c[i].w);
        }
    }return;
}
void DFS(int u,int f){
    anc[u][0]=f;
    for(int i=h[u];i;i=G[i].next){
        int v=G[i].to;
        if(v==f)continue;
        mx[v][0]=G[i].w;
        dep[v]=dep[u]+1;
        DFS(v,u);
    }return;
}
void pre_LCA(){
    for(int i=1;i<=n;++i){
        for(int j=1;(1<<j)<=n;++j)anc[i][j]=-1,mx[i][j]=-oo;
    }
    for(int j=1;(1<<j)<=n;++j){
        for(int i=1;i<=n;++i){
            if(anc[i][j-1]!=-1){
                int a=anc[i][j-1];
                anc[i][j]=anc[a][j-1];
                mx[i][j]=max(mx[i][j-1],mx[a][j-1]);
            }
        }
    }return;
}
int LCA(int p,int q){
    if(dep[p]<dep[q])swap(p,q);
    int log,ans=0;
    for(log=0;(1<<log)<=dep[p];++log);--log;
    for(int i=log;i>=0;--i){
        if(dep[p]-(1<<i)>=dep[q]){
            ans=max(ans,mx[p][i]);
            p=anc[p][i];
        }
    }
    if(p==q)return ans;
    for(int i=log;i>=0;--i){
        if(anc[p][i]!=-1&&anc[p][i]!=anc[q][i]){
            ans=max(ans,mx[p][i]);p=anc[p][i];
            ans=max(ans,mx[q][i]);q=anc[q][i];
        }
    }ans=max(ans,mx[p][0]);ans=max(ans,mx[q][0]);
    return ans;
}
 
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d%d",&p[i].x,&p[i].y),p[i].id=i,P[i]=p[i];
    make_Graph();Kruscal();
    DFS(1,-1);pre_LCA();
    scanf("%d",&m);
    while(m--){
        scanf("%d%d",&u,&v);
        if(u==v){printf("0
");continue;}
        printf("%d
",LCA(u,v));
    }return 0;
}

第三题

第三题失误好大。。一开始看题第一感dp,然后就陷在dp中拔不出来了

这道题目最大的性质就是一个点只能增加不能减少

如果可以减少的话就是只能写架设电话线那种O(n*a)的dp了

我们考虑每个点只能增加,那么一个点当且仅当左右都比他高的时候增加才是有意义的

但是一个点的高度增加之后可能会对其他点是否增加产生影响

不难发现只有高度比这个点小的点增加才会对这个点产生影响

那么我们把点从小到大排序依次增加就可以了

问题就转化成了如何O(1)的计算要增加多少以及代价

然后考虑每一步的收益,拆掉x^2变成每一步是2*base+1(base是之前增高的次数)

用并查集维护联通块内的sigma(base)就可以了,化简一下式子就可以O(1)判断了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std;
 
const int maxn=100010;
const int oo=0x7fffffff/3;
typedef long long LL;
int cnt;
LL n,c,ans;
LL Num,mx,sz;
LL a[maxn];
LL sum[maxn];
int pre[maxn],nxt[maxn];
int L[maxn],R[maxn];
int val[maxn];
int fa[maxn];
struct OP{
    int v,id;
}t[maxn];
bool cmp(const OP &A,const OP &B){return A.v<B.v;}
int ufs(int x){return fa[x]==x?x:fa[x]=ufs(fa[x]);}
void del(int x){pre[nxt[x]]=pre[x];nxt[pre[x]]=nxt[x];}
 
int main(){
    scanf("%lld%lld",&n,&c);
    for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
    a[0]=oo;cnt=0;
    for(int i=1;i<=n;++i){
        if(a[i]!=a[i-1]){
            ++cnt;
            pre[cnt]=cnt-1;
            nxt[cnt]=cnt+1;
            L[cnt]=i;R[cnt]=i;
            val[cnt]=a[i];
        }else R[cnt]++;
    }nxt[0]=1;pre[cnt+1]=cnt;
    for(int i=1;i<=cnt;++i)fa[i]=i,t[i].v=val[i],t[i].id=i;
    fa[0]=0;fa[cnt+1]=cnt+1;
    sort(t+1,t+cnt+1,cmp);
    for(int i=1;i<cnt;++i){
        int now=ufs(t[i].id);
        Num=2;sz=R[now]-L[now]+1;mx=0;
        int l=ufs(pre[now]),r=ufs(nxt[now]);
        if(l==0||r==cnt+1){
            Num=1;
            if(l==0)mx=val[r]-val[now];
            else mx=val[l]-val[now];
        }else mx=min(val[l],val[r])-val[now];
        if(mx<=0){continue;}
        LL tmp=(Num*c-2*sum[now]-sz)/(2*sz);
        if(2*sum[now]+sz>Num*c)tmp=-1;
        if(tmp>=mx-1){
            ans=ans+mx*mx*sz+2*mx*sum[now];
            sum[now]+=mx*sz;
            val[now]+=mx;
            if(val[now]==val[l]&&val[now]==val[r]){
                sum[now]+=sum[l]+sum[r];
                L[now]=L[l];R[now]=R[r];
                fa[l]=now;fa[r]=now;
                del(l);del(r);
            }else if(val[now]==val[l]){
                sum[now]+=sum[l];
                L[now]=L[l];fa[l]=now;
                del(l);
            }else{
                sum[now]+=sum[r];
                R[now]=R[r];fa[r]=now;
                del(r);
            }
        }else if(tmp>=0){
            ans=ans+(tmp+1)*(tmp+1)*sz+2*(tmp+1)*sum[now];
            sum[now]+=(tmp+1)*sz;
            val[now]+=tmp+1;
        }
    }
    LL la=val[ufs(nxt[0])];
    for(int k=ufs(nxt[ufs(nxt[0])]);k&&k!=cnt+1;k=ufs(nxt[k])){
        ans=ans+abs(val[k]-la)*c;
        la=val[k];
    }
    printf("%lld
",ans);
    return 0;
}

第三题的失误好大的QAQ

主要是没有发现只能增加的性质,也就是这个性质决定了贪心的正确性

但是现在想想,就是考场上我想出贪心也不敢去写

贪心功力太差,正确性的证明能力决定了自己的信心

看来以后要学一学数学了 QAQ 顺便做做贪心题?

原文地址:https://www.cnblogs.com/joyouth/p/5552073.html