10.16XJ模拟赛

两道模拟,但是给了很强的大样例!出题人真良心!

T1&T2:模拟,函数化编程很重要

周邦强迫我写了两篇题解

T3:
这题胡了个的树剖做法,但是比较详细
使用树剖,里面的线段树维护如下。

T4:
考虑建图。对于每个询问,我们可以二分答案,二分最小能量,然后将所有能量<=mid的点都加进去,判断图中最长链,如果>=k就return 1,否则return 0。发现加点的这个操作非常重复,所以我们将所有点按照能量排序,然后不断将点加进去,维护图中的最长链。所以我们要维护一个数据结构,在一个有向图中,其中出度为1,入度<=3,支持加点和实时维护图中最长链。发现这个图就是基环树森林。我们使用带权并查集来维护这个东西。我们的并查集维护了dis和maxx代表这个点到根的距离和这个并查集的最长链,mxans就是所有并查集的maxx的max。然后树直接做就行了。考虑基环树,那么最长链就是环上的每颗子树的最长链的max加上环上的所有点。考虑重构整棵树,因为每个点至多只被重构1次,所以复杂度有保证。我们建一个虚点,每颗子树的根连到虚点,并且距离是环的点数-1.因为然后实现见代码

#include<bits/stdc++.h>
#define for1(i,a,b) for( int i=(a);i<=(b);i++)
#define for2(i,a,b) for( int i=(a);i<(b);i++)
#define for3(i,a,b) for( int i=(a);i>=(b);i--)
#define for4(i,a,b) for( int i=(a);i>(b);i--)
#define puba push_back
#define mem(a,b) memset((a),(b),sizeof((a)))
using namespace std;
struct node{
    int i,j,w;
    char d;
}a[2250005];
int ans[1000005],fa[4500005],n,m,x,cnt,dis[4500005],T;//两倍空间
int maxx[4500005],faa[4500005],mxans;
bool vis[2250005];
bool vs[1505][1505];
char dir[1505][1505];
vector<int>v[2250005];
bool cmp(node x,node y){
    return x.w<y.w;
}
int get(int x){
    if(fa[x]!=x){
        int g=get(fa[x]);
        dis[x]+=dis[fa[x]];//dis是到fa的距离,所以是两个加起来
        mxans=max(dis[x],mxans);
        return fa[x]=g;
    }
    return fa[x];
}
void dfs(int u,int from,int mx){
    dis[u]=dis[from]+1;
    fa[u]=mx;
    maxx[mx]=max(maxx[mx],dis[u]);
    mxans=max(maxx[mx],mxans);
    for2(i,0,v[u].size()){
        int to=v[u][i];
        dfs(to,u,mx);
    }
}
void suo(int x,int y){
    int u=x,vv=y;
    int tot=0;
    while(x!=y){
        tot++;
        x=faa[x];
    }//cout<<tot<<"\n";
    x=u,y=vv;
    cnt++;
    while(x!=y){
        fa[x]=cnt;//连到虚点上
        dis[x]=tot;
        maxx[x]=tot;
        x=faa[x];
    }
    fa[y]=cnt;
    dis[y]=tot;
    maxx[y]=tot;
    mxans=max(mxans,tot);
    x=u,y=vv;
    for2(i,0,v[x].size()){//儿子并到这个环上
        int to=v[x][i];
        dfs(to,x,cnt);
    }
    while(x!=y){
        for2(i,0,v[faa[x]].size()){
            int to=v[faa[x]][i];
            if(to==x) continue;//to并到环上
            dfs(to,faa[x],cnt);
        }
        x=faa[x];
    }
}
void merge(int x,int y){//x->y
    int xi=get(x),yi=get(y);
    if(xi==yi) suo(y,x);//环=最长链加上环的长度
    else{
        faa[x]=y;
        v[y].puba(x);
        dis[xi]=1+dis[y]-dis[x];//带权并查集
        maxx[yi]=max(maxx[yi],dis[y]+maxx[xi]+1);//xi的最长链拼到y上
        mxans=max(mxans,maxx[yi]);//取最大值
        fa[xi]=yi;
    }
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    mem(ans,0x3f);
    cin>>x;
    cin>>n>>m;
    cnt=n*m;
    for1(i,1,2*n*m) fa[i]=i;//还有虚点
    for1(i,1,n){
        for1(j,1,m){
            cin>>a[(i-1)*m+j].d;
            dir[i][j]=a[(i-1)*m+j].d;
            a[(i-1)*m+j].i=i,a[(i-1)*m+j].j=j;
        }
    }for1(i,1,n){
        for1(j,1,m){
            cin>>a[(i-1)*m+j].w;
        }
    }
    sort(a+1,a+n*m+1,cmp);
    for1(i,1,n*m){
        int x=a[i].i,y=a[i].j;
        vs[x][y]=1;
        if(dir[x-1][y]=='D'&&vs[x-1][y]) merge((x-2)*m+y,(x-1)*m+y);
        if(dir[x+1][y]=='U'&&vs[x+1][y]) merge(x*m+y,(x-1)*m+y);
        if(dir[x][y+1]=='L'&&vs[x][y+1]) merge((x-1)*m+y+1,(x-1)*m+y);
        if(dir[x][y-1]=='R'&&vs[x][y-1]) merge((x-1)*m+y-1,(x-1)*m+y);
        //加入这个点之后有其他点有了连向这个点的边,要两个点同时有才能连上这个边
        if(a[i].d=='R') y++;
        if(a[i].d=='L') y--;
        if(a[i].d=='U') x--;
        if(a[i].d=='D') x++;
        if(x>=1&&x<=n&&y>=1&&y<=m&&vs[x][y]) merge((a[i].i-1)*m+a[i].j,(x-1)*m+y);
        //加入这个点之后有了点有了连向其他点的边
        if(mxans>=1e6) break;
        ans[mxans+1]=min(ans[mxans+1],a[i].w);//距离+1就是点的个数
    }
    for3(i,1e6,1) ans[i]=min(ans[i+1],ans[i]);
    //因为可能加入一个点后出现一个最长链的跃升,也就是mxans不连续,这里处理一下就好
    cin>>T;
    while(T--){
        cin>>x;
        if(ans[x]>=1e9) cout<<"77777777 ";
        else cout<<ans[x]<<" ";
    }
     
    return 0;
}

by wuhupai

posted @ 2025-07-09 10:30  wuhupai  阅读(8)  评论(0)    收藏  举报