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

浙公网安备 33010602011771号