10.15模拟赛
大家都好卷啊 我也来水一下总结吧
赛时
读T1: 怎么题面死长死长的
然后读完题面看了眼数据范围(埋下伏笔)

"诶每次步骤不超过 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;
}

浙公网安备 33010602011771号