20190922

还是好虚。

十天之前开始发烧,烧了好多天,然后就给我烧虚了。感冒,发烧,头痛牙痛腿痛。

考试也连爆了,已经滚出一机房了。

……

这次考试也挺有意思。

T1暴力都不会。

T3暴力都不会。

T2只会莫队骗分。

结果T2只想骗60的程序A了?

数据真水。

我真垃圾。

T1

暴力是个$\Theta(N^3)$傻逼暴力,然而考试的时候并没有想到。

正解单调栈+dp。

大模拟填坑也能A,还飞快……

其实挺水的。

#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
int const N=1e6+5;
inline int read(){
    int ss=0;char bb=getchar();
    while(bb<48||bb>57)bb=getchar();
    while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
    return ss;
}
int n,top=1;
int h[N],stack[N];
ll C,w[N],wf[N],dp[N];
inline int _abs(int x){
    return x<0?-x:x;
}
inline ll max(ll x,ll y){
    return x>y?x:y;
}
inline ll min(ll x,ll y){
    return x<y?x:y;
}
ll find(ll x,ll l,ll r){
    ll a=r-l-1,b=-(w[r-1]-w[l]<<1),c=wf[r-1]-wf[l];
    b-=(l?C:0)+(r!=n?C:0),c+=C*((l?h[l]:0)+(r!=n?h[r]:0));
    x=min(max((ll)(double)b/(-2.0*a)+0.5,x),min(h[l],h[r]));
    return a*x*x+b*x+c;
}
signed main(){
    n=read(),C=read();
    for(register int i=1;i<=n;++i)
        w[i]=(h[i]=read())+w[i-1],wf[i]=wf[i-1]+1ll*h[i]*h[i];
    h[0]=h[++n]=1e9+7;
    for(register int i=1;i<=n;++i){
        dp[i]=(i==1||i==n)?dp[i-1]:(dp[i-1]+C*_abs(h[i]-h[i-1]));
        while(top^1 && h[stack[top]]<=h[i])                   if(top--!=1)dp[i]=min(dp[i],dp[stack[top]]+find(h[stack[top+1]],stack[top],i));
        stack[++top]=i;
    }
    printf("%lld",dp[n]);
    return 0;
}
View Code

T2

用莫队水过的,真没啥好说的。

题目是个四维偏序,可以CDQ嵌套,也可以打个多维树状数组。

%%%skyh,%%%yxs,都是打正解的巨神。

#include<cstdio>
#include<algorithm>
using namespace std;
int const N=202;
inline int read(){
    int ss=0;char bb=getchar();
    while(bb<48||bb>57)bb=getchar();
    while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
    return ss;
}
inline int min(int x,int y){
    return x<y?x:y;
}
inline int max(int x,int y){
    return x>y?x:y;
}
inline int ml(int x){
    return x*x-(x-1)*(x-1);
}
inline int dl(int x){
    return (x+1)*(x+1)-x*x;
}
int r,c,qs,tot,cnt=1;
int bl[N][N],a[N][N];
int v[N*N],bar[N*N],now;
int as[100005];
struct node{
    int xo,yo,xi,yi,id;
}q[100005];
struct ljj{
    int w,xp,yp;
}sj[N*N];
int main(){
    r=read(),c=read(),qs=read();
    int rs=1,cs=1;
    for(register int i=1;i<=r;++i)
        for(register int j=1;j<=c;++j)
            sj[++tot].xp=i,sj[tot].yp=j,sj[tot].w=read();
    sort(sj+1,sj+tot+1,[](ljj skyh,ljj yxs){
        return skyh.w<yxs.w;
    });
    a[sj[1].xp][sj[1].yp]=1;
    for(register int i=2;i<=tot;++i)
        a[sj[i].xp][sj[i].yp]=cnt=cnt+(sj[i].w!=sj[i-1].w);
    int xlit=ceil((double)c/cs),ylit=ceil((double)r/rs);
    for(register int i=1,xl,xr=0,ct=0;i<=xlit;++i){
        xl=xr+1,xr=min(xr+cs,c);
        for(register int j=1,yl,yr=0;j<=ylit;++j){
            yl=yr+1,yr=min(yr+rs,r),++ct;
            for(register int k=yl;k<=yr;++k)
                for(register int u=xl;u<=xr;++u)
                    bl[k][u]=ct;
        }
    }
    for(register int i=1;i<=qs;++i)
        q[i].id=i,q[i].xo=read(),q[i].yo=read(),q[i].xi=read(),q[i].yi=read();
    sort(q+1,q+qs+1,[](node skyh,node yxs){
        return (bl[skyh.xo][skyh.yo]^bl[yxs.xo][yxs.yo])?(bl[skyh.xo][skyh.yo]<bl[yxs.xo][yxs.yo]):(bl[skyh.xi][skyh.yi]>bl[yxs.xi][yxs.yi]);
    });
    int nxo(0),nyo(0),nxi(0),nyi(0),ans(0);
    for(register int i=1;i<=qs;++i){
        int jx=min(q[i].xi,nxi)-max(q[i].xo,nxo)+1,jy=min(q[i].yi,nyi)-max(q[i].yo,nyo)+1;
        if(jx<=0 || jy<=0 || (nxi-nxo+1)*(nyi-nyo+1)-(jx*jy<<1)>=0){
            nxo=q[i].xo,nyo=q[i].yo,nxi=q[i].xi,nyi=q[i].yi;
            ++now,ans=0;
            for(register int j=nxo;j<=nxi;++j)
                for(register int k=nyo;k<=nyi;++k)
                    if(v[a[j][k]]!=now)v[a[j][k]]=now,bar[a[j][k]]=1,++ans;
                    else ans+=ml(++bar[a[j][k]]);
            as[q[i].id]=ans;
            continue;
        }
        while(nxo<q[i].xo){
            for(register int j=nyo;j<=nyi;++j){
                ans-=dl(--bar[a[nxo][j]]);
                if(!bar[a[nxo][j]])v[a[nxo][j]]=now-1;
            }
            ++nxo;
        }
        while(nyo<q[i].yo){
            for(register int j=nxo;j<=nxi;++j){
                ans-=dl(--bar[a[j][nyo]]);
                if(!bar[a[j][nyo]])v[a[j][nyo]]=now-1;
            }
            ++nyo;
        }
        while(nxi>q[i].xi){
            for(register int j=nyo;j<=nyi;++j){
                ans-=dl(--bar[a[nxi][j]]);
                if(!bar[a[nxi][j]])v[a[nxi][j]]=now-1;
            }
            --nxi;
        }
        while(nyi>q[i].yi){
            for(register int j=nxo;j<=nxi;++j){
                ans-=dl(--bar[a[j][nyi]]);
                if(!bar[a[j][nyi]])v[a[j][nyi]]=now-1;
            }
            --nyi;
        }
        while(nxo>q[i].xo){
            --nxo;
            for(register int j=nyo;j<=nyi;++j)
                if(v[a[nxo][j]]!=now)++ans,v[a[nxo][j]]=now,bar[a[nxo][j]]=1;
                else ans+=ml(++bar[a[nxo][j]]);
        }
        while(nyo>q[i].yo){
            --nyo;
            for(register int j=nxo;j<=nxi;++j)
                if(v[a[j][nyo]]!=now)++ans,v[a[j][nyo]]=now,bar[a[j][nyo]]=1;
                else ans+=ml(++bar[a[j][nyo]]);
        }
        while(nxi<q[i].xi){
            ++nxi;
            for(register int j=nyo;j<=nyi;++j)
                if(v[a[nxi][j]]!=now)++ans,v[a[nxi][j]]=now,bar[a[nxi][j]]=1;
                else ans+=ml(++bar[a[nxi][j]]);
        }
        while(nyi<q[i].yi){
            ++nyi;
            for(register int j=nxo;j<=nxi;++j)
                if(v[a[j][nyi]]!=now)++ans,v[a[j][nyi]]=now,bar[a[j][nyi]]=1;
                else ans+=ml(++bar[a[j][nyi]]);
        }
        as[q[i].id]=ans;
    }
    for(register int i=1;i<=qs;++i)printf("%d\n",as[i]);
    return 0;
}
View Code

T3

两个直径分别为$l_1$和$l_2$的联通块合并后直径最小为$max(\lceil\frac{l_1}{2}\rceil+\lceil\frac{l_2}{2}\rceil+1,max(l_1,l_2))$

知道这个结论就很好做了。

先找出原树的直径,求出每个点对于直径两端点所形成子树的直径大小。

然后枚举边,$\Theta(1)$求出合并后最小的直径大小,更新答案。

对于连边方案,如果ans与原树直径相等直接断开重连。

否则连接两个联通块直径的中点即可。

#include<cstdio>
#include<cstring>
using namespace std;
int const N=3e5+5;
inline int read(){
    int ss=0;char bb=getchar();
    while(bb<48||bb>57)bb=getchar();
    while(bb>=48&&bb<=57)ss=(ss<<1)+(ss<<3)+(bb^48),bb=getchar();
    return ss;
}
inline int max(int x,int y){
    return x>y?x:y;
}
int n,mp=-1,st,ed,ans=1e9+7,kx,ms,ct=0;
int head[N],Next[N<<1],to[N<<1],t=1;
int q[N],qj[N];
int pre[N];
char vis[N<<1];
int stl[N],edl[N];
inline void add(int x,int y){
    to[++t]=y;
    Next[t]=head[x],head[x]=t;
    return ;
}
void dfs(int x,int y,int ds){
    if(ds>mp)mp=ds,st=x;
    ++ds;
    for(int i=head[x];i;i=Next[i])
        if(to[i]^y && i^ct && i^1^ct)dfs(to[i],x,ds);
    return ;
}
int bfs(int x){
    memset(pre,0,sizeof(pre));
    int u(0),v(1);
    q[1]=x,pre[x]=-1;
    while(u^v && v^n){
        x=q[++u];
        for(register int i=head[x];i;i=Next[i])
            if(!pre[to[i]] && i^ct && i^1^ct)pre[q[++v]=to[i]]=i^1;
    }
    return v;
}
int get(int x,int y,bool p){
    int mad(0),mal(0);
    for(int i=head[x];i;i=Next[i])
        if(to[i]^y){
            int z=get(to[i],x,p)+1;
            mal=max(max(mal,(p?stl[to[i]]:edl[to[i]])),mad+z);
            mad=max(mad,z);
        }
    (p?stl[x]:edl[x])=mal;
    return mad;
}
inline void find(int x){
    printf("\n%d %d ",to[x],to[x^1]);
    ct=x,mp=-1;
    dfs(to[x],0,0);
    q[ms=1]=q[bfs(st)];
    for(register int i=pre[q[1]];~i;i=pre[to[i]])
        q[++ms]=to[i];
    printf("%d ",q[ms+1>>1]);
    mp=-1;
    dfs(to[x^1],0,0);
    q[ms=1]=q[bfs(st)];
    for(register int i=pre[q[1]];~i;i=pre[to[i]])
        q[++ms]=to[i];
    printf("%d ",q[ms+1>>1]);
    return ;
}
int main(){
    n=read();
    for(register int i=1,ff,tt;i<n;++i)ff=read(),tt=read(),add(ff,tt),add(tt,ff);
    dfs(1,0,0),bfs(st);
    for(register int i=pre[ed=q[n]];~i;i=pre[to[i]])
        vis[i]=1,++ms;
    get(st,0,1),get(ed,0,0);
    for(register int i=2;i<=t;i+=2)
        if(vis[i]){
            int x=edl[to[i]],y=stl[to[i^1]],now=max((x+1>>1)+(y+1>>1)+1,max(x,y));
            if(ans>now)ans=now,qj[kx=1]=i>>1;
            else if(ans==now)qj[++kx]=i>>1;
        }
        else if(vis[i^1]){
            int x=stl[to[i]],y=edl[to[i^1]],now=max((x+1>>1)+(y+1>>1)+1,max(x,y));
            if(ans>now)ans=now,qj[kx=1]=i>>1;
            else if(ans==now)qj[++kx]=i>>1;
        }
        else if(ans>ms)ans=ms,qj[kx=1]=i>>1;
        else if(ans==ms)qj[++kx]=i>>1;
    printf("%d\n%d ",ans,kx);
    for(register int i=1;i<=kx;++i)printf("%d ",qj[i]);
    find(qj[1]<<1);
    return 0;
}
View Code

转载于:https://www.cnblogs.com/remarkable/p/11575033.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值