191026考试题解

T1:求一个网格图从原点出发走n步,每步等概率往上下左右走,最终到达的点和原点之间的欧氏距离的期望
打个表可以发现是n,完了
具体证明可以推式子或者三角函数

#include
#define mod 998244353
#define pii pair
#define pb push_back
#define mp make_pair
#define ll long long
#define fi first
#define se second
#define db double
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
inline void file(){freopen("grid.in","r",stdin);freopen("grid.out","w",stdout);}
int main(){
	#ifdef romiqi
	file();
	#endif
	int n=read()%mod;
	cout<<n;
	return 0;
}

T2:两棵树,对于第一棵树的每条边,求出删掉它并在第二棵树上将对应两个编号连一条边再从第二棵树上选一条边同样操作一次之后仍然是两棵树的方案数

转化为dfs序的限制,通过树上差分把第二棵树上某条边记录到第一棵树中,满足一个限制,另一个限制类似扫描线可以树状数组做一下

#include
#define pii pair
#define pb push_back
#define mp make_pair
#define re register
#define ll long long
#define fi first
#define se second
#define db double
#define re register
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=1000005;
int n;
namespace bit{
	int tr[N];
	inline int lb(int x){return x&(-x);}
	inline void add(int x,int v){for(re int i=x;i<=n;i+=lb(i)) tr[i]+=v;}
	inline int ask(int x){int res=0;for(re int i=x;i;i-=lb(i)) res+=tr[i];return res;}
	inline void modify(int l,int r,int v){add(l,v);add(r+1,-v);}
}
using bit::modify;
using bit::ask;
int idx[N],ans[N];
struct LCA{
	int head[N],nxt[N<<1],vis[N<<1],tot,idd[N<<1];
	inline void add(int x,int y,int i=0){vis[++tot]=y;nxt[tot]=head[x];head[x]=tot;idd[tot]=i;}
	int siz[N],hson[N],fa[N],dep[N];
	void dfs1(int v,int kd){
		siz[v]=1;
		for(re int i=head[v];i;i=nxt[i]){
			int y=vis[i];
			if(y==fa[v]) continue;
			fa[y]=v;dep[y]=dep[v]+1;
			if(kd==1) idx[y]=idd[i];
			dfs1(y,kd);
			siz[v]+=siz[y];
			if(siz[y]>siz[hson[v]]) hson[v]=y;
		}
	}
	int top[N],dfn[N],id[N],sign;
	LCA(){sign=0;tot=0;}
	void dfs2(int v){
		dfn[v]=++sign;id[sign]=v;
		top[v]=v==hson[fa[v]]?top[fa[v]]:v;
		for(re int i=head[v];i;i=nxt[i])
			if(!top[vis[i]]) dfs2(vis[i]);
	}
	inline int lca(int x,int y){
		while(top[x]!=top[y])	
			dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
		return dep[x]<dep[y]?x:y;
	}
	inline int D(int u,int v){return ask(dfn[u])+ask(dfn[v])-2*ask(dfn[lca(u,v)]);}
}tr[3];
vector<int>ad[N],del[N];
void dfs5(int v,int f){
	int res=0;
	if(v>1) res=tr[2].D(v,f);
	for(re int i=tr[1].head[v];i;i=tr[1].nxt[i]){
		int y=tr[1].vis[i];
		if(y==f) continue;
		dfs5(y,v);
	}
	for(re int i=0;i<ad[v].size();++i){int x=ad[v][i];modify(tr[2].dfn[x],tr[2].dfn[x]+tr[2].siz[x]-1,1);}
	for(re int i=0;i<del[v].size();++i){int x=del[v][i];modify(tr[2].dfn[x],tr[2].dfn[x]+tr[2].siz[x]-1,-2);}
	if(v>1) ans[idx[v]]=tr[2].D(v,f)-res;
}
struct E{int x,y,lca;}e[N];
inline void file(){freopen("road.in","r",stdin);freopen("road.out","w",stdout);}
int main(){  int size=100<<20;//40M
    //__asm__ ("movl  %0, %%esp\n"::"r"((char*)malloc(size)+size));//调试用这个 
    __asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));//提交用这个 

    //main函数代码 
	n=read();
	for(re int x,y,i=1;i<n;++i){x=read(),y=read();tr[1].add(x,y,i),tr[1].add(y,x,i);}
	for(re int x,y,i=1;i<n;++i){
		x=read(),y=read();
		tr[2].add(x,y),tr[2].add(y,x);
		e[i].x=x,e[i].y=y;
	}
	tr[1].dfs1(1,1),tr[1].top[1]=1;tr[1].dfs2(1);
	for(re int i=1;i<n;++i) e[i].lca=tr[1].lca(e[i].x,e[i].y);
	tr[2].dfs1(1,0);tr[2].top[1]=1;tr[2].dfs2(1);
	for(re int x,y,lca,i=1;i<n;++i){
		x=e[i].x,y=e[i].y,lca=e[i].lca;
		if(tr[2].dep[x]<tr[2].dep[y]) swap(x,y);
		ad[x].pb(x),ad[y].pb(x),del[lca].pb(x);
	}
	dfs5(1,0);
	for(re int i=1;i<n;++i) cout<<ans[i]<<" ";
exit(0);//必须用exit 
	return 0;
}

T3:给一个DAG,每条边有互不相同的权值,把一条路径经过所有边的权值按顺序记录下来可以得到一个字符集大小为1e9,长度为经过边数量的字符串,多次询问从某个点出发第k小的可能的字符串的终点或者判断无解

设siz表示一个点出发最多走多少不同路径,把一个点出点中siz最大的点拿出来,会形成一棵树,然后倍增预处理树上的信息,查询时,如果走树边无法走到,就二分走哪个非树边,需要预处理一下siz的前缀和,走一次非树边问题规模至少减少一半(类似轻重链剖分),那么一次询问复杂度就是log^2的

Code:

#include
#define pb push_back
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int INF=1e9,N=2e5+5;
int n,m;
int siz[N];
int s[N][32],t[N][32];
int in[N],cnt=0;
vector<int>fa[N],son[N],sum[N];
inline int search(int x,int k){
	if(!k) return x;
	for(int i=31;i;i--) if(t[x][i] && s[x][i]<=k && s[x][i]+siz[t[x][i]]>=k) return search(t[x][i],k-s[x][i]);
	int p=(--lower_bound(sum[x].begin(),sum[x].end(),k))-sum[x].begin();
	return search(son[x][p+1],k-(~p?sum[x][p]:0)-1);
}
queue<int>q;
int main(){
	n=read(),m=read();
	for(int x,y,i=1;i<=m;i++){
		x=read(),y=read();++in[x];
		son[x].pb(y);fa[y].pb(x);
	}
	for(int i=1;i<=n;i++) if(!in[i]) q.push(i);
	while(!q.empty()){
		int x=q.front();q.pop();++cnt;
		for(auto y:son[x]){
			siz[x]+=siz[y]+1;
			siz[x]=min(siz[x],INF);
			sum[x].pb(siz[x]);
		}
		if(son[x].size()){
			int pos=0;
			for(int i=1;i<son[x].size();i++) if(siz[son[x][i]]>siz[son[x][pos]]) pos=i;
			s[x][0]=1;t[x][0]=son[x][pos];
			for(int i=0;i<pos;i++) s[x][0]+=siz[son[x][i]]+1,s[x][0]=min(s[x][0],INF);
			for(int i=1;i<32;i++){
				t[x][i]=t[t[x][i-1]][i-1];
				if(!t[x][i]) break;
				s[x][i]=s[x][i-1]+s[t[x][i-1]][i-1];
				s[x][i]=min(s[x][i],INF);
			}
		}
		for(auto f:fa[x]) if(!(--in[f])) q.push(f);
	}
	int q=read();
	while(q--){
		int x=read(),k=read();
		if(siz[x]<k) puts("-1");
		else cout<<search(x,k)<<"\n";
	}
	return 0;
}

你可能感兴趣的:(倍增,树状数组)