T1 走格子
这算是一道结论题吧,考场上写了个 d f s dfs dfs 发现 n n n 的答案就是 n n n。
std 的推导如下:
#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
scanf("%d",&n);
printf("%d\n",n%998244353);
return 0;
}
T2 换公路
这道题在考场上想了大部分的 s t d std std,但是没有调出来,有点可惜。
考虑第一棵树的边 e 1 e_1 e1 和第二棵树的边 e 2 e_2 e2 在满足什么条件时可以互换,即:
- e 2 e_2 e2 在 e 1 e_1 e1 加进第二棵树时形成的环上。
- e 1 e_1 e1 在 e 2 e_2 e2 加进第一棵树时形成的环上。
现在就是要对于每个 e 1 e_1 e1,询问能与之互换的 e 2 e_2 e2 数量。
那么我们把 e 2 e_2 e2 在第一棵树上差分一下,相当于是处理第 1 1 1 个限制。然后在第一棵树上 d f s dfs dfs 求答案。对于每一条 e 1 e_1 e1,把能 “ “ “覆盖 ” ” ”它的 e 2 e_2 e2 的权值赋成 1 1 1(差分的时候即可完成),然后在第 2 2 2 棵树上查 e 1 e_1 e1 两端点的链的权值,相当于是处理第 2 2 2 个限制。
这样做的话我们要支持单点加,链查询,如果树剖暴力跳重链的话 O ( n log 2 n ) O(n\log^2n) O(nlog2n) 过不了。
有一个小优化,我们可以维护子树加,单点查询,就有点像树状数组的思想一样。由于子树的 d f s dfs dfs 序是连续的,不需要暴力跳重链来修改,时间复杂度就降为 O ( n log n ) O(n\log n) O(nlogn)。
然后这个东西可以不用线段树,用树状数组就可以了。
#include<bits/stdc++.h>
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c;T x;
while(!isdigit(c=gc()));x=c^48;
while( isdigit(c=gc())) x=(x+(x<<2)<<1)+(c^48);
return x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=1e6+5;
int n,ans[N];
struct edge{int u,v;}e1[N],e2[N];
struct edges{
int t,first[N],v[N<<1],nxt[N<<1];
void add(int x,int y) {nxt[++t]=first[x],first[x]=t,v[t]=y;}
int tot,id[N],dep[N],fa[N],Size[N],son[N],top[N],pos[N];
void dfs1(int x){
Size[x]=1;
for(int i=first[x];i;i=nxt[i]){
int to=v[i];
if(to==fa[x]) continue;
fa[to]=x,dep[to]=dep[x]+1,id[to]=(i+1)/2;
dfs1(to),Size[x]+=Size[to];
if(Size[to]>Size[son[x]]) son[x]=to;
}
}
void dfs2(int x,int tp){
top[x]=tp,pos[x]=++tot;
if(son[x]) dfs2(son[x],tp);
for(int i=first[x];i;i=nxt[i])
if(v[i]!=fa[x]&&v[i]!=son[x]) dfs2(v[i],v[i]);
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
return (dep[x]<dep[y])?x:y;
}
}T1,T2;
typedef pair<int,int> pii;
vector<pii> p[N];
namespace BIT{
int bit[N];
#define lowbit(x) (x&(-x))
void add(int x,int val) {for(;x<=n;x+=lowbit(x))bit[x]+=val;}
int Query(int x,int ans=0) {for(;x;x-=lowbit(x))ans+=bit[x];return ans;}
void update(int l,int r,int val) {add(l,val),add(r+1,-val);}
int ask(int x,int y) {return Query(T2.pos[x])+Query(T2.pos[y])-2*Query(T2.pos[T2.LCA(x,y)]);}
#undef lowbit
}
void dfs(int x,int fa){
int pre=0;
if(x>1) pre=BIT::ask(x,fa);
for(int i=T1.first[x];i;i=T1.nxt[i]){
int to=T1.v[i];
if(to==fa) continue;
dfs(to,x);
}
for(int i=0;i<p[x].size();++i){
int now=p[x][i].first,val=p[x][i].second;
BIT::update(T2.pos[now],T2.pos[now]+T2.Size[now]-1,val);
}
ans[T1.id[x]]=BIT::ask(x,fa)-pre;
}
int main(){
n=in();
for(int i=1,x,y;i<n;++i) x=in(),y=in(),T1.add(x,y),T1.add(y,x),e1[i]=(edge){x,y};
for(int i=1,x,y;i<n;++i) x=in(),y=in(),T2.add(x,y),T2.add(y,x),e2[i]=(edge){x,y};
T1.dfs1(1),T1.dfs2(1,1),T2.dfs1(1),T2.dfs2(1,1);
for(int i=1;i<n;++i){
int u=e2[i].u,v=e2[i].v;
if(T2.dep[u]>T2.dep[v]) swap(u,v);
p[u].push_back(pii(v,1));
p[v].push_back(pii(v,1));
p[T1.LCA(u,v)].push_back(pii(v,-2));
}
dfs(1,0);
for(int i=1;i<n;++i) printf("%d ",ans[i]);
return 0;
}
T3 找宝藏
#include<bits/stdc++.h>
using namespace std;
namespace IO{
const int Rlen=1<<22|1;
char buf[Rlen],*p1,*p2;
inline char gc(){
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T Read(){
char c;T x;
while(!isdigit(c=gc()));x=c^48;
while( isdigit(c=gc())) x=(x+(x<<2)<<1)+(c^48);
return x;
}
inline int in() {return Read<int>();}
}
using IO::in;
const int N=2e5+5;
int n,m,q,rt;
int t[N][21],s[N][21],deg[N],son[N],f[N];
vector<int>e[N],val[N];
const int lim=1e9+5;
int add(int x,int y) {return min(lim,x+y);}
void dfs(int x){
if(f[x]) return;
int heaviest=0;
for(int &to:e[x]){
dfs(to);
if(f[to]>f[son[x]]) son[x]=to,heaviest=f[x]+1;
f[x]=add(f[x],f[to]+1),val[x].push_back(f[x]);
}
t[x][0]=son[x],s[x][0]=heaviest;
for(int i=1;i<=20;++i) t[x][i]=t[t[x][i-1]][i-1],s[x][i]=add(s[x][i-1],s[t[x][i-1]][i-1]);
}
int find(int x,int k){
if(!k) return x;
for(int i=20;~i;--i)
if(t[x][i]&&s[x][i]<=k&&s[x][i]+f[t[x][i]]>=k) return find(t[x][i],k-s[x][i]);
int pos=lower_bound(val[x].begin(),val[x].end(),k)-val[x].begin();
return pos?find(e[x][pos],k-val[x][pos-1]-1):find(e[x][0],k-1);
}
int main(){
n=in(),m=in();
for(int i=1,x,y;i<=m;++i){
x=in(),y=in(),e[x].push_back(y),deg[y]++;
}
for(int i=1;i<=n;++i) if(!deg[i]) rt=i;
dfs(rt);q=in();
while(q--){
int S=in(),k=in();
if(k>f[S]) {puts("-1");continue;}
printf("%d\n",find(S,k));
}
return 0;
}
反思
关于这套题我的遗憾就是 T2。一是想到了 s t d std std 却没调出来,另一个就是暴力也没拿分。
由于要调正解就这样写的:
/*if(n<=3000) subtask1::solve();
else */subtask2::solve();
最后很慌就没有把注释删掉,然后正解又没有调出来。。。
这种 s b sb sb 错误在正式考试的时候一定不能再犯。