191026-模拟测试8
T1 走格子
1.1 description
L爷不小心走到了一个网格图上,现在他位于坐标为 的位置。L爷想在这个网格图上走走,可是他觉得按照特定的路线走太没有意思了,于是他决定在网格图上随机游走。他走的每一步,都会从他当前所处位置的东南西北四个方向中的随机选一个方向走一个单位长度。他发现最后走到的位置不是一定的,于是他想知道,走n步后的位置到原点的距离的平方的期望是多少。
1.2 input
一行一个数n
1.3 output
输出期望的步数,对998244353取模。
解析
从来没搞懂期望,考场上刚看到期望的时候,就直接跳题了,后来写完第二题暴力回来再看第一题,对期望有了一些新的理解
于是首先想到dfs,来计算前2个点,结果通过运算发现,期望长度=所走的步数。所以代码非常简单,直接输出n即可(但注意要取模)
以下为一种严谨证明:
T2 换公路
2.1 description
L爷在网格图上走着走着,不小心走出了网格图的边界,来到了一座神秘的城市——A城。A城中有n个关键点,有两种重要的物资B、C需要在这n个关键点之间运输,来保证A城的正常运作。L爷发现,这两种物资在A城中分别由两套不同的交通系统运输。每套交通系统都是由n 1条公路构成的,每条公路连接着城中的某两个关键点,这两个关键点可以通过这条公路来运输对应种类的物资。A城能正常运作,是因为每套交通系统都保证了物资能够从任一城市经过若干条公路运送到任一城市。
有时A城运送B物资的交通系统会出现故障,导致某条公路不能运送B物资,只能运送C物资。这时候就需要紧急调动已有资源,从运送C物资的交通系统中,找到一条公路,使得它运送B物资而不运送C物资,且使得A城能够正常运作。
L爷想知道,对于运送B物资的交通系统中的每条公路,如果它出现故障,有多少种调动方案能使得A城正常运作。
2.2 input
第一行一个数n,表示城市总数。
接下来n 1行,第i 行有两个数
,表示第一个交通系统中的第i 条公路连接的两条边为
。
接下来n-1行,第i 行有两个数
,表示第二个交通系统中的第i 条公路连接的两条边为
。
2.3 output
输出一行n个数,第i 个数表示第一个交通系统的第i 条边出现故障后能使得A城正常运作的调动方案数。
解析
考场上居然写出了暴力(时间复杂度为O( )),能过30分。
首先我们把题意简化一下,即在两个图中,讲第一棵树拆掉一条边加到第二棵树中,然后将第二棵树中的一条边加到第一棵树上,(即两棵树各自交换一条边),使得两者仍然联通,问这样的方案数。
我们很容易想到一种办法,首先枚举第一棵树上的所有边,将其加在第二棵树上,这样第二棵树上必然会出现一个环,我们只需在枚举环上每条边,来判断其是否能使第一棵树联通,以下为具体代码2种:
暴力代码1
#include<bits/stdc++.h>
#define M 200009
using namespace std;
int tot,first1[M],first2[M],nxt1[M],nxt2[M],to1[M],to2[M],bk[M],vis[M][3],n,f[M][21],bj[M],dep[M];
void add1(int x,int y,int z)
{
nxt1[++tot]=first1[x];
first1[x]=tot;
to1[tot]=y;
bk[tot]=x;
vis[z][++vis[z][0]]=tot;
}
void add2(int x,int y)
{
nxt2[++tot]=first2[x];
first2[x]=tot;
to2[tot]=y;
}
void bfs(int r,int p)
{
queue<int>q;
bj[r]=1;
q.push(r);
while(!q.empty())
{
int v=q.front();
q.pop();
for(int i=first1[v];i;i=nxt1[i])
{
int u=to1[i];
if(i==vis[p][1]||i==vis[p][2]) continue;
if(bj[u]) continue;
bj[u]=1;
q.push(u);
}
}
}
void init(int u,int fa)
{
dep[u]=dep[fa]+1;
for(int i=1;i<=19;i++)
f[u][i]=f[f[u][i-1]][i-1];
for(int i=first2[u];i;i=nxt2[i])
{
int v=to2[i];
if(v==fa)
continue;
f[v][0]=u;
init(v,u);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--)
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
if(x==y) return x;
for(int i=19;i>=0;i--)
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
return f[x][0];
}
int main()
{
//freopen("road.in","r",stdin);
//freopen("road.out","w",stdout);
int x,y;
scanf("%d",&n);
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
add1(x,y,i);
add1(y,x,i);
}
tot=0;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
add2(x,y);
add2(y,x);
}
init(1,0);
for(int i=1;i<=n-1;i++)
{
memset(bj,0,sizeof(bj));
bfs(1,i);
int ans=0;
int Lca=lca(bk[vis[i][1]],to1[vis[i][1]]);
int k=f[bk[vis[i][1]]][0];
int kk=bk[vis[i][1]];
while(kk!=Lca)
{
if(bj[k]!=bj[kk]) ans++;
k=f[k][0];
kk=f[kk][0];
}
k=f[to1[vis[i][1]]][0];
kk=to1[vis[i][1]];
while(kk!=Lca)
{
if(bj[k]!=bj[kk]) ans++;
k=f[k][0];
kk=f[kk][0];
}
printf("%d ",ans);
}
return 0;
}
暴力代码2
#include<bits/stdc++.h>
#define M 1000009
using namespace std;
int tot,first1[M],first2[M],nxt1[M],nxt2[M],to1[M],to2[M],bk[M],vis[M][3],n,f[M],bj[M],dep[M],fa[M],ans;
bool jl[M];
int read()
{
int f=1,re=0;
char ch;
for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;isdigit(ch);ch=getchar())
re=(re<<3)+(re<<1)+ch-'0';
return f*re;
}
void add1(int x,int y,int z)
{
nxt1[++tot]=first1[x];
first1[x]=tot;
to1[tot]=y;
bk[tot]=x;
vis[z][++vis[z][0]]=tot;
}
void add2(int x,int y)
{
nxt2[++tot]=first2[x];
first2[x]=tot;
to2[tot]=y;
}
void bfs(int r)
{
queue<int>q;
q.push(r);
fa[r]=r;
while(!q.empty())
{
int v=q.front();
q.pop();
for(int i=first1[v];i;i=nxt1[i])
{
int u=to1[i];
if(u==fa[v]) continue;
fa[u]=v;
q.push(u);
}
}
}
int getfa(int x)
{
if(fa[x]==x) return x;
if(bj[x]==1) return x;
return getfa(fa[x]);
}
void init(int u,int fa)
{
dep[u]=dep[fa]+1;
for(int i=first2[u];i;i=nxt2[i])
{
int v=to2[i];
if(v==fa)
continue;
f[v]=u;
init(v,u);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=dep[x];i>dep[y];i--)
{
if(!jl[x])
{
bj[x]=bj[getfa(x)];
jl[x]=1;
}
if(!jl[f[x]])
{
bj[f[x]]=bj[getfa(f[x])];
jl[f[x]]=1;
}
if(bj[x]!=bj[f[x]]) ans++;
x=f[x];
}
while(x!=y)
{
if(!jl[x])
{
bj[x]=bj[getfa(x)];
jl[x]=1;
}
if(!jl[f[x]])
{
bj[f[x]]=bj[getfa(f[x])];
jl[f[x]]=1;
}
if(!jl[y])
{
bj[y]=bj[getfa(y)];
jl[y]=1;
}
if(!jl[f[y]])
{
bj[f[y]]=bj[getfa(f[y])];
jl[f[y]]=1;
}
if(bj[x]!=bj[f[x]]) ans++;
if(bj[y]!=bj[f[y]]) ans++;
x=f[x];
y=f[y];
}
return ans;
}
int main()
{
//freopen("road.in","r",stdin);
//freopen("road.out","w",stdout);
int x,y;
n=read();
for(int i=1;i<=n-1;i++)
{
x=read();
y=read();
add1(x,y,i);
add1(y,x,i);
}
tot=0;
for(int i=1;i<=n-1;i++)
{
x=read();
y=read();
add2(x,y);
add2(y,x);
}
init(1,0);
bfs(1);
for(int i=1;i<=n-1;i++)
{
ans=0;
memset(jl,0,sizeof(jl));
memset(bj,0,sizeof(bj));
if(fa[bk[vis[i][1]]]==to1[vis[i][1]])
bj[bk[vis[i][1]]]=1;
else
bj[to1[vis[i][1]]]=1;
printf("%d ",lca(bk[vis[i][1]],to1[vis[i][1]]));
}
return 0;
}
至于正解,再搁一搁。
先放上题解
题解
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+50;
int n,tot,sz,in[N],out[N],rt[N],ans[N];
struct tree{int l,r,s;} t[N*60];
vector<int> vec[N];
int rd() {
int i=0,f=1; char ch=getchar();
while (!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
while (isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
return i*f;
}
struct Tree {
int cnt,lst[N],fa[N],dep[N],sze[N],top[N];
struct edge{int to,next;}e[N*2];
void addedge(int u,int v) {
e[++cnt].to=v; e[cnt].next=lst[u]; lst[u]=cnt;
e[++cnt].to=u; e[cnt].next=lst[v]; lst[v]=cnt;
}
void dfs1(int x) {
dep[x]=dep[fa[x]]+1; sze[x]=1;
for (int i=lst[x];i;i=e[i].next) {
if(e[i].to==fa[x]) continue;
fa[e[i].to]=x;
dfs1(e[i].to);
sze[x]+=sze[e[i].to];
}
}
void dfs2(int x,int chain) {
top[x]=chain; int k=0;
for (int i=lst[x];i;i=e[i].next)
if(e[i].to!=fa[x]&&sze[e[i].to]>sze[k]) k=e[i].to;
if(k) dfs2(k,chain);
for (int i=lst[x];i;i=e[i].next)
if(e[i].to!=fa[x]&&e[i].to!=k) dfs2(e[i].to,e[i].to);
}
void dfs3(int x) {
in[x]=++tot;
for (int i=lst[x];i;i=e[i].next)
if(e[i].to!=fa[x]) dfs3(e[i].to);
out[x]=++tot;
}
int get_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;
int newnode() {sz++; t[sz].l=t[sz].r=t[sz].s=0; return sz;}
int merge(int x,int y) {
if(!x||!y) return x+y;
t[x].s+=t[y].s;
t[x].l=merge(t[x].l,t[y].l);
t[x].r=merge(t[x].r,t[y].r);
return x;
}
void modify(int &d,int l,int r,int x,int y) {
if(!d) d=newnode();
t[d].s+=y;
if(l==r) return;
int mid=(l+r)/2;
if(x<=mid) modify(t[d].l,l,mid,x,y);
else modify(t[d].r,mid+1,r,x,y);
}
int query(int d,int l,int r,int x,int y) {
if(!d||x<=l&&r<=y) return t[d].s;
int mid=(l+r)/2, ans=0;
if(x<=mid) ans+=query(t[d].l,l,mid,x,y);
if(y>mid) ans+=query(t[d].r,mid+1,r,x,y);
return ans;
}
void solve(int x) {
for (int i=t2.lst[x];i;i=t2.e[i].next) {
if(t2.e[i].to==t2.fa[x]) continue;
solve(t2.e[i].to);
rt[x]=merge(rt[x],rt[t2.e[i].to]);
}
for (int i=0;i<vec[x].size();i++) {
int y=vec[x][i];
if(y>0) modify(rt[x],1,tot,in[y],1), modify(rt[x],1,tot,out[y],-1);
else modify(rt[x],1,tot,in[-y],-2), modify(rt[x],1,tot,out[-y],2);
}
if(x==1) return;
int lca=t1.get_lca(x,t2.fa[x]);
ans[x]=query(rt[x],1,tot,1,in[x])+query(rt[x],1,tot,1,in[t2.fa[x]])-2*query(rt[x],1,tot,1,in[lca]);
}
int main() {
freopen("road.in","r",stdin);
freopen("road.out","w",stdout);
n=rd();
for (int i=1;i<n;i++) {
int x=rd(), y=rd();
t2.addedge(x,y);
}
for (int i=1;i<n;i++) {
int x=rd(), y=rd();
t1.addedge(x,y);
}
t1.dfs1(1); t1.dfs2(1,1); t1.dfs3(1);
t2.dfs1(1); t2.dfs2(1,1);
for (int i=1;i<n*2-1;i+=2) {
int x=t1.e[i].to, y=t1.e[i+1].to, lca=t2.get_lca(x,y);
if(t1.dep[x]<t1.dep[y]) swap(x,y);
vec[x].push_back(x); vec[y].push_back(x);
vec[lca].push_back(-x);
}
solve(1);
for (int i=1;i<n*2-1;i+=2) {
int x=t2.e[i].to,y=t2.e[i+1].to;
if(t2.dep[x]<t2.dep[y]) swap(x,y);
printf("%d ",ans[x]);
}
return 0;
}
T3 找宝藏
3.1 description
L爷游历完A城后,又来到另一个神秘的国度——C城。
L爷发现,C城是由n个点m条边组成的有向无环图。C城的每条边都有一个权值,为
中的整数,且边的权值两两互不相同,可能存在起点终点都相同的边。从C城的某个点出发,沿着图中的边一直走到另一个点,把经过的所有边的权值按照经过的顺序依次写下来,会得到一个字符集在
,长度为经过边的数量的字符串。
L爷打听到,C城在接下来的q 天会举行寻宝活动。在第i 天,宝藏会被埋到从
号点出发,所有可能的路径所形成的非空字符串中,第
小的字符串对应的路径的终点城市。L爷想得到宝藏,于是想请你告诉他每天宝藏被埋的城市。
3.2 input
第一行两个数n,m。
接下来的m行,第i 行有两个数
,表示权值为i 的边的起点为
,终点为
。
接下来一行一个整数q。
接下来的q行,第i 行两个整数si;ki,表示第i 天是从
号城出发,找寻第
小路径的终点。
3.3 output
输出q行,其中第i 行为第i 天的终点城市。如果不存在第 小的终点城市,那么第i 行输出一个“-1”(不含引号)。
题解
#include <bits/stdc++.h>
typedef long long LL;
using namespace std;
inline int rd() {
char ch=getchar(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getchar();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getchar();}
return i*f;
}
const int N=1e5+50,LIM=31;
const LL INF=0x3f3f3f3f3f3f3f3f;
int n,m,deg[N],to[N][LIM];
LL sze[N],s[N][LIM];
vector <int> son[N];
vector <LL> sum[N];
vector <int> fa[N];
queue <int> q;
inline int getson(int u,int d) {
if(!d) return u;
for(int i=LIM-1;~i;i--)
if((to[u][i])&&(sze[to[u][i]]+s[u][i]>=d)&&(s[u][i]<=d))
return getson(to[u][i],d-s[u][i]);
int p=(--lower_bound(sum[u].begin(),sum[u].end(),d))-sum[u].begin();
return getson(son[u][p+1],d-(~p?sum[u][p]:0)-1);
}
int main() {
freopen("treasure.in","r",stdin);
freopen("treasure.out","w",stdout);
n=rd(), m=rd();
for(int i=1;i<=m;i++) {
int x=rd(),y=rd();
++deg[x];
son[x].push_back(y);
fa[y].push_back(x);
}
for(int i=1;i<=n;i++) if(!deg[i]) q.push(i);
while(!q.empty()) {
++cnt;
int u=q.front(); q.pop();
for(int e=0;e<son[u].size();++e) {
sze[u]+=(sze[son[u][e]]+1);
sze[u]=min(sze[u],INF);
sum[u].push_back(sze[u]);
}
if(son[u].size()) {
int p=0;
for(int e=1;e<son[u].size();++e)
if(sze[son[u][e]]>sze[son[u][p]]) p=e;
s[u][0]=1; to[u][0]=son[u][p];
for(int e=0;e<p;e++) s[u][0]+=sze[son[u][e]]+1, s[u][0]=min(s[u][0],INF);
for(int i=1;i<LIM;i++) {
to[u][i]=to[to[u][i-1]][i-1];
if(!to[u][i]) break;
s[u][i]=s[u][i-1]+s[to[u][i-1]][i-1];
s[u][i]=min(s[u][i],INF);
}
}
for(int e=0;e<fa[u].size();++e) {
int v=fa[u][e];
if(!(--deg[v])) q.push(v);
}
}
int q=rd();
for(int i=1;i<=q;i++) {
int x=rd(), k=rd();
if(sze[x]<k) puts("-1");
else printf("%d\n",getson(x,k));
}
}
先搁一搁,去看看暴力。