10.15模拟赛

大家都好卷啊 我也来水一下总结吧


赛时

读T1: 怎么题面死长死长的

然后读完题面看了眼数据范围(埋下伏笔)

2025-10-16 08-26-29屏幕截图

"诶每次步骤不超过 1e5 ,这样乘起来是 1e10 过不去,还得优化"

然后浪费了我1个小时,没想到随即写了 "暴力" ,然后写挂了没调出来,而且觉得只要48分随即破防

然后看 T4 暴力 42 分挺顺眼的,开始写LCA

然后写挂了

时间过去了 2 个小时

觉得自己像是要爆零

看着 T1 和 T2 想着怎么招得做出来一道,不然就废了

觉得 T2 挺顺眼的

看半天觉得像大根堆练习题,然后没想明白给自己证伪了,于是写了 21 分的暴力

但是证伪后还心心念念我的大根堆来着,最后正解就是它

然后又思考了会觉得 T1 直接做应该是对的

然后调出来了

然后去调了 T4

然后去写了 T3 的暴力 56 分 ST 表

然后还剩 15 分钟

然后没然后了


赛后

T1 挂了 但只挂了9分

成为了唯一一位一题没切的人 但中位数

然后看到 hdc 大巨 AK(?) 了这场比赛,但 T1 也挂了9分

然后看到 小情侣 lmy 大巨和 fjj 大巨都各切了 3 道题

我好菜

于是来写一下今天的神奇模拟赛的题解


题解

T1

直接按照题意模拟即可,判重部分我用了 hash + unordered_map 我是神经病吧 unordered_map 自带 hash

然后挂 9 分一是因为输出不能有前导零,二是读入补零的时候要补前导零 什么神经东西

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7,b=1331;
int n,d,a[10005][15],w[15];
mt19937 rnd(20100914);
int hsh(int x[]){
    int res=0;
    for(int i=1;i<=d;i++){
        res=(res*b+w[x[i]])%mod;
    }
    return res;
}
bool cmp(int x,int y){
    return x>y;
}
signed main(){
	freopen("gravity.in","r",stdin);
	freopen("gravity.out","w",stdout);
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>d;
    for(int i=1;i<=n;i++){
        string s;
        cin>>s;
		while(s.length()<d)s="0"+s;
        s=" "+s;
        //if(i==6)cout<<s<<" -------------"<<endl;
        for(int j=1;j<=s.length()-1;j++){
            a[i][j]=s[j]-'0';
        }
    }
    for(int i=0;i<=9;i++){
        w[i]=rnd()%mod;
    }
    for(int i=1;i<=n;i++){
        //cout<<i<<"----------------"<<endl;
        unordered_map<int,bool>vis;
        vis[hsh(a[i])]=1;
        while(1){
            int maxn=0,minn=0,num;
            // for(int j=1;j<=d;j++){
            //     cout<<a[i][j];
            // }
            //cout<<" "<<1<<endl;
            sort(a[i]+1,a[i]+d+1,cmp);
            for(int j=1;j<=d;j++){
                maxn=maxn*10+a[i][j];
                //cout<<a[i][j];
            }
            //cout<<" "<<2<<endl;
            sort(a[i]+1,a[i]+d+1);
            for(int j=1;j<=d;j++){
                minn=minn*10+a[i][j];
            }
            num=maxn-minn;
            //cout<<maxn<<" "<<minn<<" "<<num<<endl;
            for(int j=d;j>=1;j--){
                a[i][j]=num%10;
                num/=10;
            }
            // cout<<i<<" "<<hsh(a[i])<<" "<<vis[hsh(a[i])]<<endl;
            // for(int j=1;j<=d;j++){
            //     cout<<a[i][j];
            // }
            // cout<<endl;
            if(vis[hsh(a[i])]){
                int j=1;
                while(a[i][j]==0&&j<=d-1){
                    j++;
                }
                for(;j<=d;j++){
                    cout<<a[i][j];
                }
                cout<<endl;
                break;
            }
            vis[hsh(a[i])]=1;
            //cout<<vis[hsh(a[i])]<<"========================"<<endl;
        }
    }
}

T2

贪心的排序出来三个数组如果有撞的直接丢掉就好

还有另解

按照 x 排序,然后跑若干个神秘三维偏序

还有另解

按照 x 排序,然后将x视为时间,y和z视为坐标,两个点组成的矩形的右上端点对答案有贡献,维护这三个点即可,若有在上方的落单点也维护一下,因为也有可能用那些点更新答案

我写了第一种

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=150001;
int n,num,cnta=1,cntb=1,cntc=1;
struct TT{
	int x,y,z,i;
}a[N],b[N],c[N];
inline bool cmpa(TT x,TT y){return x.x>y.x;}
inline bool cmpb(TT x,TT y){return x.y>y.y;}
inline bool cmpc(TT x,TT y){return x.z>y.z;}
bool vis[N];
int main(){
	freopen("apple.in","r",stdin);
	freopen("apple.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i].x>>a[i].y>>a[i].z;
        a[i].i=i;
		b[i]=c[i]=a[i];
	}
	sort(a+1,a+n+1,cmpa);
	sort(b+1,b+n+1,cmpb);
	sort(c+1,c+n+1,cmpc);
	while(num<n){
		int na=a[cnta].i,nb=b[cntb].i,nc=c[cntc].i;
        while(vis[na]){
            cnta++;
            na=a[cnta].i;
        }
        while(vis[nb]){
            cntb++;
            nb=b[cntb].i;
        }
        while(vis[nc]){
            cntc++;
            nc=c[cntc].i;
        }
        //cout<<num<<" "<<na<<" "<<nb<<" "<<nc<<endl;
		if(a[cnta].x==b[cntb].x||c[cntc].z==b[cntb].z){
			vis[nb]=1,num++;
		}else if(a[cnta].z==c[cntc].z||a[cnta].y==b[cntb].y){
			vis[na]=1,num++;
		}else if(a[cnta].x==c[cntc].x||c[cntc].y==b[cntb].y){
			vis[nc]=1,num++;
		}else{
			cout<<a[cnta].x+b[cntb].y+c[cntc].z;
			return 0;
		}
	}
	cout<<-1;
}

T3

这是一个类似点分治的过程,不过每次分治中心是最大的点,如果建出来点分树且处理好边权的话答案就是最长的从根出发的链

那就从小到大枚举点,维护形成的联通块,合并联通块时更新新点的答案就做完了,距离用深度差来算,所以要求个 lca

因为这个遍历顺序形如点分树的拓扑序,所以是对的

具体的我码上给(雾)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=200005;
int n,a[N],h[N],fa[N],f[N],nx[N][25],dep[N];
bool vis[N];
vector<int>nxt[N];
void dfs(int u,int fa){
	dep[u]=dep[fa]+1;
	nx[u][0]=fa;
	for(int i=1;i<=20;i++){
		nx[u][i]=nx[nx[u][i-1]][i-1];
	}
	for(int i=0;i<nxt[u].size();i++){
		int v=nxt[u][i];
		if(v==fa)continue;
		dfs(v,u);
	}
}
int lca(int x,int y){
    if(dep[x]<dep[y])swap(x,y);
    for(int i=20;i>=0;i--){
        if(dep[nx[x][i]]>=dep[y])x=nx[x][i];
    }
    if(x==y)return x;
    for(int i=20;i>=0;i--){
        if(nx[x][i]!=nx[y][i]){
            x=nx[x][i],y=nx[y][i];
        }
    }
    return nx[x][0];
}
int find(int u){
	if(fa[u]==u)return u;
	return fa[u]=find(fa[u]);
}
int dis(int x,int y){
	return dep[x]+dep[y]-2*dep[lca(x,y)];
}
signed main(){
	freopen("butterfly.in","r",stdin);
	freopen("butterfly.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		h[a[i]]=fa[i]=i;
	}
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		nxt[u].push_back(v);
		nxt[v].push_back(u);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++){
		int u=h[i];
		vis[u]=1;
		for(int j=0;j<nxt[u].size();j++){
			int v=nxt[u][j];
			if(!vis[v])continue;
			f[u]=max(f[u],f[find(v)]+dis(u,find(v)));
			fa[find(v)]=u;
		}
	}
	cout<<f[(find(1))];
}


T4

首先两棵树每次计算答案都是不交的做两次即可

发现两个节点到 lca 的最短距离 >k 等同于同时向上跳 k 步没有祖先关系

倍增预处理 k 级祖先(当然 dfs 开个栈也可以 O(n) 做),用括号序维护祖先关系,dfs另一棵树表示处理到哪一个点,它到根的一条链上的信息都得维护,转化为添加线段,求当前线段与多少线段不交,树状数组维护即可

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=200005;
int n,k,fa[2][N],rt[2],l[2][N],r[2][N],f[2][N][25],cnt,ans;
vector<int>nxt[2][N];
struct BIT{
	int sum[400005];
	int lowbit(int x){return x&-x;}
	void add(int x,int k){
		while(x<=400000){
            //cout<<x<<endl;
			sum[x]+=k;
			x+=lowbit(x);
		}
	}
	int query(int x){
		int res=0;
		while(x){
            //cout<<x<<endl;
			res+=sum[x];
			x-=lowbit(x);
		}
		return res;
	}
}b1,b2;
void dfs(int u,int op){
	l[op][u]=++cnt;
	for(int i=0;i<nxt[op][u].size();i++){
		int v=nxt[op][u][i];
		dfs(v,op);
	}
	r[op][u]=++cnt;
}
int get(int op,int u){
	int h=k,cnt=0;
	while(h){
		if(h&1)u=f[op][u][cnt];
		h>>=1;
        cnt++;
        if(u==0)break;
	}
	return u;
}
void solve(int u,int op){
    //cout<<u<<endl;
	int x=fa[op^1][u];
    if(x){
        ans+=b1.query(400000)-b1.query(r[op^1][x]);
	    ans+=b2.query(l[op^1][x]-1);
	    b1.add(l[op^1][x],1);
        b2.add(r[op^1][x],1);
    }
	for(int i=0;i<nxt[op][u].size();i++){
		int v=nxt[op][u][i];
		solve(v,op);
	}
    if(x){
	    b1.add(l[op^1][x],-1);
        b2.add(r[op^1][x],-1);
    }
}
signed main(){
	freopen("genealogy.in","r",stdin);
	freopen("genealogy.out","w",stdout);
	cin>>n>>k;
	for(int j=0;j<=1;j++){
		for(int i=1;i<=n;i++){
			int u;
			cin>>u;
			f[j][i][0]=u;
			nxt[j][u].push_back(i);
			if(u==0){
				rt[j]=i;
			}
		}
        cnt=0;
		dfs(rt[j],j);
		for(int p=1;p<=20;p++){
			for(int i=1;i<=n;i++){
				f[j][i][p]=f[j][f[j][i][p-1]][p-1];
			}
		}
		for(int i=1;i<=n;i++){
			fa[j][i]=get(j,i);
		}
	}
	for(int j=0;j<=1;j++){
		solve(rt[j],j);
	}
	cout<<ans;
}
posted @ 2025-10-16 09:16  __Vinson  阅读(30)  评论(2)    收藏  举报