noip模拟59[信心]

noip模拟59 solutions

刚换机房心情好,顺手切掉第一题

其实是没有切掉的,具体原因是我因为优化常数导致\(long long\)被关掉\(WA\)

后来因为常数太大\(T\)了,然后直接优化掉一半,\(A\)

后面好像还有一个半原题,可以用单调栈+斜率优化\(cao\)过去

然后剩下的还有一个高精小数,还有一个挺难调的

T1 柱状图

首先这个题一眼就是单峰函数,直接三分+树状数组就\(A\)

但是我不会三分咋办,只能用二分

所以观察性质,

先把绝对值拆开,找到两侧的表达式

应该可以看出来是要枚举最高点的吧,题面暗示都那么明显了

我在最高点的地方画一条函数,这条函数在每一个横坐标的取值就是应该有的高度

那么我们就可以直接将原来的高度画在坐标系上,我们要尽量保证在线上面的点和下面的点相等

所以我们可以直接二分这个东西,用线段树维护这个个数

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define ll long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1e5+5;
int n,hi[N];
struct XDS{
    ll sum[N*35];
    int siz[N*35],ls[N*35],rs[N*35];
    int rt,seg;
    void pushup(int x){
        sum[x]=sum[ls[x]]+sum[rs[x]];
        siz[x]=siz[ls[x]]+siz[rs[x]];
        return ;
    }
    void ins(int &x,int l,int r,int pos,int v){
        if(!x)x=++seg;
        if(l==r){
            siz[x]+=v;sum[x]+=1ll*v*pos;
            return ;
        }
        
        int mid=(l+r)/2;if(mid<=0&&l<0)mid--;
        if(pos<=mid)ins(ls[x],l,mid,pos,v);
        else ins(rs[x],mid+1,r,pos,v);
        pushup(x);return ;
    }
    int query_siz(int x,int l,int r,int ql,int qr){
        if(!x)return 0;
        if(ql<=l&&r<=qr)return siz[x];
        int mid=(l+r)/2,ret=0;if(mid<=0&&l<0)mid--;
        if(ql<=mid)ret+=query_siz(ls[x],l,mid,ql,qr);
        if(qr>mid)ret+=query_siz(rs[x],mid+1,r,ql,qr);
        return ret;
    }
    ll query_sum(int x,int l,int r,int ql,int qr){
        if(!x)return 0;
        if(ql<=l&&r<=qr)return sum[x];
        int mid=(l+r)/2;ll ret=0;if(mid<=0&&l<0)mid--;
        if(ql<=mid)ret+=query_sum(ls[x],l,mid,ql,qr);
        if(qr>mid)ret+=query_sum(rs[x],mid+1,r,ql,qr);
        return ret;
    }
}le,ri;
int L=-1e6,R=1e9+1e6,mx,id;
ll ans=0x3f3f3f3f3f3f3f3f;
ll get_ans(int h,int x){
    ll ret=0;
    ret+=1ll*le.query_siz(le.rt,L,R,L,h-x-1)*(h-x)-le.query_sum(le.rt,L,R,L,h-x-1);
    ret+=1ll*ri.query_siz(ri.rt,L,R,L,h+x-1)*(h+x)-ri.query_sum(ri.rt,L,R,L,h+x-1);
    ret+=le.query_sum(le.rt,L,R,h-x+1,R)-1ll*le.query_siz(le.rt,L,R,h-x+1,R)*(h-x);
    ret+=ri.query_sum(ri.rt,L,R,h+x+1,R)-1ll*ri.query_siz(ri.rt,L,R,h+x+1,R)*(h+x);
    return ret;
}
signed main(){
    #ifdef oj
        freopen("c.in","r",stdin);
        freopen("c.out","w",stdout);
    #endif
    scanf("%d",&n);
    fo(i,1,n){
        scanf("%d",&hi[i]);
        ri.ins(ri.rt,L,R,hi[i]+i,1);
        if(hi[i]>mx)mx=hi[i],id=i;
    }
    fo(i,1,n){
        le.ins(le.rt,L,R,hi[i]-i,1);
        ri.ins(ri.rt,L,R,hi[i]+i,-1);
        int l=max(n-i+1,i),r=max(l,mx+abs(id-i)),mid,sl=0,sr=0;
        while(l+1<r){
            int mid=l+r>>1;
            sl=le.query_siz(le.rt,L,R,L,mid-i-1)+ri.query_siz(ri.rt,L,R,L,mid+i-1);
            sr=n-sl;//sr=le.query_siz(le.rt,L,R,mid-i+1,R)+ri.query_siz(ri.rt,L,R,mid+i+1,R);
            if(sl<=sr)l=mid;
            else r=mid;
        }
        ans=min(ans,min(get_ans(l,i),get_ans(r,i))); 
    }
    printf("%lld",ans);
}

T2 应急棍

这个就是直接找规律就行了。。。

每一种距离是一层,直接寻找就好了

没有打高精,只有60pts

60pts
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
int C,T;
double l;
int pw2[100],pw4[100],n;
signed main(){
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%lld%lld%lf",&C,&T,&l);
    pw2[0]=pw4[0]=1;
    fo(i,1,35)pw2[i]=pw2[i-1]*2,pw4[i]=pw4[i-1]*4;
    while(T--){
        scanf("%lld",&n);
        if(n==1)printf("0 0\n");
        if(n==2)printf("%.10lf %.10lf\n",l,l);
        if(n==3)printf("0 %.10lf\n",l);
        if(n==4)printf("%.10lf 0\n",l);
        if(n<5)continue;
        int now=0;n-=4;
        while(n>pw4[now]+2*pw2[now]*(pw2[now]+1))
            n-=pw4[now]+2*pw2[now]*(pw2[now]+1),now++;
        double len=l/(1.0*pw2[now]);
        double x,y;
        if(n<=pw4[now])x=len/2.0+(n-1)/pw2[now]*len,y=len/2.0+(n-1)%pw2[now]*len;
        else {
            n-=pw4[now];x=(n-1)/(pw2[now+1]+1)*len;
            if((n-1)%(pw2[now+1]+1)>=pw2[now])x+=len/2.0,y=((n-1)%(pw2[now+1]+1)-pw2[now])*len;
            else y=(n-1)%(pw2[now+1]+1)*len+len/2.0;
        }
        printf("%.10lf %.10lf\n",x,y);
    }
}

T3 擒敌拳

其实吧这个可以测试点分治过去

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define ll long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=2e5+5;
int n,h[N];
int sta[N],top,bot=1;
ll ans;
double get_slope(int x,int y){
    return 1.0*(1.0*(1ll*h[y]*y-h[y])-(1ll*h[x]*x-h[x]))/(h[y]-h[x]);
}
void spj(){
    fo(i,1,n){
        while(bot<top&&get_slope(sta[bot],sta[bot+1])<=1.0*i)bot++;
        printf("%lld ",max(1ll*h[i],1ll*h[sta[bot]]*(i-sta[bot]+1)));
        while(top>bot&&get_slope(sta[top-1],sta[top])>=get_slope(sta[top],i))top--;
        sta[++top]=i;
    }
}
signed main(){
    #ifdef oj
        freopen("b.in","r",stdin);
        freopen("b.out","w",stdout);
    #endif
    scanf("%d",&n);
    bool flag=true;
    fo(i,1,n){
        scanf("%d",&h[i]);
        if(h[i]<h[i-1])flag=false;
    }
    if(flag){spj();return 0;}
    fo(i,1,n){
        
        while(top>0&&h[sta[top]]>=h[i])top--;
        sta[++top]=i;
        ans=max(ans,1ll*h[sta[1]]*i);
        int now=2;
        fo(j,2,i){
            j=sta[now-1]+1;
            ans=max(ans,1ll*h[sta[now]]*(i-j+1));
            now++;
        }
        printf("%lld ",ans);
    }
}

也可以直接用李超线段树干过去,直接维护每一个区间的函数

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=2e5+5;
int n,h[N],l[N],r[N],ans;
int sta[N],top;
struct XDS{
    #define ls x<<1
    #define rs x<<1|1
    int k[N*4],b[N*4];
    int get(int x,int k,int b){return x*k+b;}
    void ins(int x,int l,int r,int qk,int qb){
        if(get(l,k[x],b[x])<=get(l,qk,qb)&&get(r,k[x],b[x])<=get(r,qk,qb))return k[x]=qk,b[x]=qb,void();
        if(get(l,k[x],b[x])>=get(l,qk,qb)&&get(r,k[x],b[x])>=get(r,qk,qb))return ;
        int mid=l+r>>1;
        if(get(mid,k[x],b[x])<=get(mid,qk,qb))swap(k[x],qk),swap(b[x],qb);
        if(get(l,k[x],b[x])<=get(l,qk,qb))ins(ls,l,mid,qk,qb);
        if(get(r,k[x],b[x])<=get(r,qk,qb))ins(rs,mid+1,r,qk,qb);
        return ;
    }
    void upd(int x,int l,int r,int ql,int qr,int qk,int qb){
        if(ql<=l&&r<=qr)return ins(x,l,r,qk,qb),void();
        int mid=l+r>>1;
        if(ql<=mid)upd(ls,l,mid,ql,qr,qk,qb);
        if(qr>mid)upd(rs,mid+1,r,ql,qr,qk,qb);
        return ;
    }
    int query(int x,int l,int r,int pos){
        if(l==r)return get(pos,k[x],b[x]);
        int mid=l+r>>1,ret=get(pos,k[x],b[x]);
        if(pos<=mid)ret=max(ret,query(ls,l,mid,pos));
        if(pos>mid)ret=max(ret,query(rs,mid+1,r,pos));
        return ret;
    }
    #undef ls
    #undef rs
}xds;
signed main(){
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    scanf("%lld",&n);
    fo(i,1,n)scanf("%lld",&h[i]);
    fo(i,1,n){
        while(top&&h[sta[top]]>=h[i])top--;
        l[i]=sta[top]+1;sta[++top]=i;
    }top=0;sta[top]=n+1;
    fu(i,n,1){
        while(top&&h[sta[top]]>=h[i])top--;
        r[i]=sta[top]-1;sta[++top]=i;
    }
    fo(i,1,n){
        //cout<<l[i]<<" "<<r[i]<<endl;
        xds.upd(1,1,n,l[i],r[i],h[i],-(l[i]-1)*h[i]);
        printf("%lld ",ans=max(ans,xds.query(1,1,n,i)));
    }
}

T4 边数

这个吧先把没有入度的点全和1连起来

再去把不在环上的点全都删掉,注意这个地方删的时候不够的还要连边

再去遍历每一个环,在环上用倍增实现就好了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=5e5+5;
const int inf=0x3f3f3f3f;
int n,K,ans;
int to[N*2],du[N],dep[N],who[N];
bool vis[N],vih[N],via[N],cl[N];
void dfs(int x,int r){
    if(vis[x]&&who[x]!=r)return ;
    if(vis[x]&&who[x]==r){
        int now=x;vih[x]=true;now=to[now];
        while(now!=x)vih[now]=true,now=to[now];
        return ;
    }
    vis[x]=true;who[x]=r;
    if(to[x])dfs(to[x],r);
}
void topu(){
    queue<int> q;while(!q.empty())q.pop();
    fo(i,2,n)if(dep[i]==1)q.push(i);
    while(!q.empty()){
        int x=q.front();q.pop();
        if(dep[x]>K)dep[x]=1,ans++;
        du[to[x]]--;dep[to[x]]=min(dep[to[x]],dep[x]+1);
        if(!du[to[x]])q.push(to[x]);
    }
}
void bfs(){
    queue<int> q;while(!q.empty())q.pop();
    fo(i,2,n)if(dep[i]==1)q.push(i);
    while(!q.empty()){
        int x=q.front();q.pop();vis[x]=true;
        if(vis[to[x]])continue;
        q.push(to[x]);dep[to[x]]=min(dep[to[x]],dep[x]+1);
    }
}
int ji[N],cnt,nxt[N];
int st[N][20];
void sol(int x){
    cnt=0;ji[++cnt]=x;cl[x]=true;
    int now=to[x];bool flag=false;
    while(now!=x)ji[++cnt]=now,cl[now]=true,now=to[now];
    fo(i,1,cnt){
        ji[cnt+i]=ji[i];
        if(!via[ji[i]])flag=true;
    }nxt[2*cnt+1]=2*cnt+1;
    fu(i,2*cnt,1){
        if(via[ji[i]])nxt[i]=nxt[i+1];
        else nxt[i]=i;
        memset(st[i],0x3f,sizeof(st[i]));
    }
    fu(i,2*cnt,1){
        st[i][0]=i+K>=2*cnt?cnt*2+1:nxt[i+K];
        for(int j=1;st[i][j-1]<=2*cnt&&j<=19;j++)st[i][j]=st[st[i][j-1]][j-1];
    }
    int res=inf;
    if(!flag)res=0;
    fo(i,1,cnt){
        int dst=i+cnt-1,now=i,tmp=1;
        fu(j,16,0){
            if(st[now][j]>dst)continue;
            tmp+=(1<<j);now=st[now][j];
        }res=min(res,tmp);
    }
    ans+=res;
}
signed main(){
    #ifdef oj
        freopen("d.in","r",stdin);
        freopen("d.out","w",stdout);
    #endif 
    scanf("%d%d",&n,&K);
    fo(i,1,n){
        int x,y;scanf("%d%d",&x,&y);
        if(y==1)continue;
        to[x]=y;du[y]++;
    }
    fo(i,1,n)if(!vis[i])dfs(i,i);
    memset(vis,false,sizeof(vis));
    memset(dep,0x3f,sizeof(dep));dep[1]=0;
    dep[to[1]]=1;fo(i,2,n)if(!du[i])dep[i]=1,ans++;
    topu();bfs();fo(i,1,n)if(dep[i]<=K)via[i]=true;
    fo(i,2,n)if(vih[i]&&!cl[i])sol(i);
    printf("%d",ans);
}
posted @ 2021-09-24 11:09  fengwu2005  阅读(111)  评论(0编辑  收藏  举报