博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

10.21 牛客提高集训营6


2018.10.21 牛客提高集训营6

比赛链接

不是很懂那些粘人代码还直接交上去的人,在提交记录里很好看么?

A 最长路(拓扑 分层)

题目链接

容易想到建反图拓扑。有了最长路后,按最长路对图分层。
因为当前点路径字典序最小,就是要满足第一条边最小后,再满足下一个点路径字典序最小。后者可以直接用上一层的答案。
按这两个关键字排序,就可以得到当前层的路径字典序排名了。
复杂度\(O(n\log n)\)

比较两条路径字典序大小,因为处理到\(x\)时,前面的转移都确定了,所以也可以倍增+Hash找出第一个不同字符。。

//505ms	78300KB
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#define mp std::make_pair
#define pr std::pair<int,int>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define mod 998244353
#define Mod(x) x>=mod&&(x-=mod)
typedef long long LL;
const int N=1e6+5,INF=2e9;

int dgr[N];
pr A[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Graph
{
	int Enum,H[N],nxt[N],to[N],len[N];
	void AE(int u,int v,int w)
	{
		to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;
	}
}G,R;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
inline bool cmp(int a,int b)
{
	return A[a]<A[b];
}
void Toposort(int n,int m)
{
	static int q[N],f[N],rk[N],sum[N];
	static bool vis[N];
	static std::vector<int> v[N];

	int h=0,t=0;
	for(int i=1; i<=n; ++i) if(!dgr[i]) q[++t]=i;
	while(h<t)
	{
		int x=q[++h];
		vis[x]=1, v[f[x]].push_back(x), A[x]=mp(INF,0);
		for(int i=R.H[x],v; i; i=R.nxt[i])
		{
			f[v=R.to[i]]=std::max(f[v],f[x]+1);
			if(!--dgr[v]) q[++t]=v;
		}
	}

	for(int i=1; i<=n; ++i)
	{
		if(!v[i].size()) break;
		int l=v[i].size();
		std::vector<int> &vi=v[i];
		for(int j=0; j<l; ++j)
		{
			int x=vi[j]; pr t;
			for(int k=G.H[x],v; k; k=G.nxt[k])
				if(f[x]==f[v=G.to[k]]+1 && (t=mp(G.len[k],rk[v]))<A[x])
					A[x]=t, sum[x]=29ll*sum[v]%mod+t.first, Mod(sum[x]);
		}
		std::sort(vi.begin(),vi.end(),cmp);
		for(int j=0; j<l; ++j) rk[vi[j]]=j;
	}

	for(int i=1; i<=n; ++i)
		if(vis[i]) printf("%d\n",(int)(29ll*sum[i]%mod));
		else puts("Infinity");
}

int main()
{
	int n=read(),m=read();
	for(int i=1,u,v,w; i<=m; ++i)
		u=read(),v=read(),w=read(),++dgr[u],G.AE(u,v,w),R.AE(v,u,w);
	Toposort(n,m);

	return 0;
}

B 选择题(DP 期望 退背包)

题目链接

\(x\)个人的期望得分为:

\[\sum_{mx=1}^4\sum_{cx=1}^4w_{mx,cx}\cdot p(x,mx,cx) \]

\(cx\)表示\(x\)选了哪个选项,\(p(x,mx,cx)\)表示在\(cnt(mx)>\lfloor\frac n2\rfloor\)时,\(x\)选了\(cx\)的概率。
那么对\(cx\)分类讨论,\(f(x,mx,cnt)\)表示除\(x\)外,恰好有\(cnt\)个人选了\(mx\)的概率。则有

\[p(x,mx,cx)=\begin{cases}p_{x,cx}\sum_{i=\lfloor\frac n2\rfloor}f(x,mx,i),& cx=mx\\p_{x,cx}\sum_{i=\lfloor\frac n2\rfloor+1}f(x,mx,i),& cx\neq mx\end{cases} \]

\(f(x,mx,cnt)\)的转移就是个背包。枚举\(x\),就可以\(O(n^3)\)了。期望得分\(55\)
我们发现\(x\)变成\(x+1\)时,对\(f\)的影响时,加入一个元素\(x\)并删掉\(x+1\)的影响。先拿所有人算一遍\(f\),加入一个人就是做一遍\(O(n)\)的DP。把\(f\)的转移反过来就可以\(O(n)\)删掉\(x\)的贡献了:

\[f_{i,j}=(1-p_{i,mx})f_{i-1,j}+p_{i,mx}f_{i-1,j-1}\\ f_{i-1,j}=\frac{f_{i,j}-p_{i,mx}f_{i-1,j-1}}{1-p_{i,mx}}\]

\(j\)\(0\)的时候特判。注意\(p\)\(1\)的时候也要特判。
复杂度\(O(n^2)\)

//226ms	536KB
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
#define mod 998244353
#define Mod(x) x>=mod&&(x-=mod)
typedef long long LL;
const int N=2005;

int P[N][4],W[4][4],f[N],g[N];
LL Ans[N];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
inline int Inv(int x,int k=mod-2)
{
	int t=1;
	for(; k; k>>=1,x=1ll*x*x%mod)
		if(k&1) t=1ll*t*x%mod;
	return t;
}
void Solve(int n,int mx)
{
	memset(f,0,sizeof f);
	f[0]=1;
	for(int i=1; i<=n; ++i)
	{
		LL p=P[i][mx];
		for(int j=i; j; --j)
			f[j]=p*f[j-1]%mod+(1-p)*f[j]%mod, Mod(f[j]);//就是(mod+)1-p啊(mod-p是-p)
		f[0]=(1-p)*f[0]%mod;
	}
	for(int i=1; i<=n; ++i)//x
	{
		LL p=P[i][mx];
		if(p!=1)
		{
			LL inv=Inv(mod+1-p);
			g[0]=inv*f[0]%mod;
			for(int j=1; j<=n; ++j) g[j]=inv*(f[j]-p*g[j-1]%mod)%mod;
		}
		else
		{
			for(int j=0; j<n; ++j) g[j]=f[j+1];//f[i-1][j-1]=f[i][j]
			g[n]=0;
		}
		LL tmp=0;
		for(int j=(n>>1)+1; j<=n; ++j) tmp+=g[j];
		tmp%=mod;
		for(int j=0; j<4; ++j) Ans[i]+=1ll*W[mx][j]*P[i][j]%mod*(tmp+(mx==j?g[n>>1]:0))%mod;//cx
	}
}

int main()
{
	int n=read();
	for(int i=1; i<=n; ++i)
		P[i][0]=read(),P[i][1]=read(),P[i][2]=read(),P[i][3]=read();
	for(int i=0; i<4; ++i)
		W[i][0]=read(),W[i][1]=read(),W[i][2]=read(),W[i][3]=read();
	for(int mx=0; mx<4; ++mx) Solve(n,mx);
	for(int i=1; i<=n; ++i) printf("%d\n",(int)((Ans[i]%mod+mod)%mod));

	return 0;
}

C 树(树链剖分)

题目链接

是不是写出这题,树剖就无敌了啊。。
写完啦!感觉还好,也就6k嘛。

肯定要用点来表示边的颜色,然后树剖。
对于操作2,我们可以拆成:
1.将u->v路径上的点,所有连向子节点的边染成col2;
2.将u->v路径上的点,所有连向父节点的边染成col1;
3.将LCA(u,v)连向父节点的边染成col2。
那么本题的操作实际有四种:链修改、链查询、修改一条链上所有连向子节点的边、换根+子树修改。
除了链修改指向子节点的边,其它都是树剖模板(换根见BZOJ3083)。
因为是改所有指向子节点的边,所以也可以树剖维护。用to_son[x]表示x指向儿子的轻边的颜色,用另一棵线段树维护(注意只是轻边,重边要单独在第一棵树上改)。
除此之外,在第一棵线段树上维护每个点x到fa[x]的边的颜色。
这样查询时,对于重链可以直接在第一棵线段树上查;对于轻边(top[x]->fa[top[x]])的颜色,需要区分是第一棵树上top[x]的值还是第二棵树上fa[top[x]]的值。修改时再带一个时间优先级即可。
要修改的链是一样的,且to_son[x]只会单点查(切换重链时)(也是单点修改),所以只要一棵线段树同时维护两种标记就行了。
复杂度\(O(m\log^2n)\)
另外单点查完全可以用非递归。
边界(dfn+/-1)问题要注意。(会不会还有锅啊。。)

//358ms	20428KB
#include <cstdio>
#include <cctype>
#include <algorithm>
#define fir first
#define sec second
#define mp std::make_pair
#define pr std::pair<int,int>//time val
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5;

int n,Enum,H[N],nxt[N<<1],to[N<<1],fa[N],dep[N],sz[N],son[N],top[N],dfn[N],Index,ref[N],out[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
	#define ls rt<<1
	#define rs rt<<1|1
	#define lson l,m,ls
	#define rson m+1,r,rs
	#define S N<<2
	int cnt[S][3],sz[S];
	pr tag1[S],tag2[S];
	#undef S

	#define Update(rt) cnt[rt][0]=cnt[ls][0]+cnt[rs][0], cnt[rt][1]=cnt[ls][1]+cnt[rs][1], cnt[rt][2]=cnt[ls][2]+cnt[rs][2]
	#define Cov1(rt,v) cnt[rt][0]=0, cnt[rt][1]=0, cnt[rt][2]=0, cnt[rt][v.sec]=sz[rt], tag1[rt]=v
	#define Cov2(rt,v) tag2[rt]=v
	inline void PushDown(int rt)
	{
		int l=ls,r=rs;
		if(tag1[rt].fir) Cov1(l,tag1[rt]), Cov1(r,tag1[rt]), tag1[rt].fir=0;
		if(tag2[rt].fir) Cov2(l,tag2[rt]), Cov2(r,tag2[rt]), tag2[rt].fir=0;
	}
	void Build(int l,int r,int rt)
	{
		cnt[rt][0]=sz[rt]=r-l+1;
		if(l!=r)
		{
			int m=l+r>>1;
			Build(lson), Build(rson);
		}
	}
	void ModifyI(int l,int r,int rt,int L,int R,pr v)//为了常数 粘了仨Modify→_→
	{
		if(L<=l && r<=R) {Cov1(rt,v); return;}
		PushDown(rt);
		int m=l+r>>1;
		if(L<=m) ModifyI(lson,L,R,v);
		if(m<R) ModifyI(rson,L,R,v);
		Update(rt);
	}
	void Modify1(int l,int r,int rt,int p,pr v)
	{
		if(l==r) {Cov1(rt,v); return;}
		PushDown(rt);
		int m=l+r>>1;
		p<=m ? Modify1(lson,p,v) : Modify1(rson,p,v);
		Update(rt);
	}
	void Modify2(int l,int r,int rt,int L,int R,pr v1,pr v2)
	{
		if(L<=l && r<=R) {Cov1(rt,v1), Cov2(rt,v2); return;}
		PushDown(rt);
		int m=l+r>>1;
		if(L<=m) Modify2(lson,L,R,v1,v2);
		if(m<R) Modify2(rson,L,R,v1,v2);
		Update(rt);
	}
	pr QueryP(int l,int r,int rt,int p,int id)
	{
		int m;
		while(l!=r)
		{
			PushDown(rt);
			m=l+r>>1, p<=m?(r=m,rt=ls):(l=m+1,rt=rs);
		}
		return id==1?tag1[rt]:tag2[rt];
	}
	int QueryI(int l,int r,int rt,int L,int R,int c)
	{
		if(L<=l && r<=R) return cnt[rt][c];
		PushDown(rt);
		int m=l+r>>1;
		if(L<=m)
			if(m<R) return QueryI(lson,L,R,c)+QueryI(rson,L,R,c);
			else return QueryI(lson,L,R,c);
		return QueryI(rson,L,R,c);
	}
}T;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
inline void AE(int u,int v)
{
	to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
	to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum;
}
inline int Jump(int u,int lca)
{
	int las=u;
	while(top[u]!=top[lca]) u=fa[las=top[u]];
	return u==lca?las:ref[dfn[lca]+1];
}
void DFS1(int x)
{
	int mx=0; sz[x]=1;
	for(int i=H[x],v; i; i=nxt[i])
		if((v=to[i])!=fa[x])
		{
			fa[v]=x, dep[v]=dep[x]+1, DFS1(v), sz[x]+=sz[v];
			if(sz[v]>mx) mx=sz[v], son[x]=v;
		}
}
void DFS2(int x,int tp)
{
	top[x]=tp, dfn[x]=++Index, ref[Index]=x;
	if(son[x])
	{
		DFS2(son[x],tp);
		for(int i=H[x],v; i; i=nxt[i])
			if((v=to[i])!=fa[x]&&v!=son[x]) DFS2(v,v);
	}
	out[x]=Index;
}
int Query(int u,int v,int c)
{
	int ans=0,tp;
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]]) std::swap(u,v);
		tp=top[u];
		if(dfn[tp]<dfn[u]) ans+=T.QueryI(1,n,1,dfn[tp]+1,dfn[u],c);
		pr t1=T.QueryP(1,n,1,dfn[tp],1),t2=T.QueryP(1,n,1,dfn[fa[tp]],2);
		ans+=t1.fir>t2.fir?(t1.sec==c):(t2.sec==c);
		u=fa[tp];
	}
	if(dep[u]<dep[v]) std::swap(u,v);
	if(dfn[v]<dfn[u]) ans+=T.QueryI(1,n,1,dfn[v]+1,dfn[u],c);
	return ans;
}
void Modify(int u,int v,int c1,int c2,int t)
{
	if(dfn[u]<n) T.Modify1(1,n,1,dfn[u]+1,mp(t,c2));//dfn[son[u]]
	if(dfn[v]<n) T.Modify1(1,n,1,dfn[v]+1,mp(t,c2));//这个修改就不会有冲突了 
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]]) std::swap(u,v);
		T.Modify2(1,n,1,dfn[top[u]],dfn[u],mp(t+1,c1),mp(t,c2));
		u=fa[top[u]];
		T.Modify1(1,n,1,dfn[u]+1,mp(t+1,c2));
	}
	if(dep[u]<dep[v]) std::swap(u,v);
	T.Modify2(1,n,1,dfn[v],dfn[u],mp(t+1,c1),mp(t,c2));
	T.Modify1(1,n,1,dfn[v],mp(t+1,c2));
}

int main()
{
	n=read();
	for(int i=1; i<n; ++i) AE(read(),read());
	DFS1(1), DFS2(1,1), T.Build(1,n,1);
	for(int opt,x,y,c,c2,rt,Q=read(),i=1; i<=Q; ++i)
		switch(opt=read())
		{
			case 1: x=read(),y=read(),printf("%d\n",Query(x,y,read())); break;
			case 2: x=read(),y=read(),c=read(),c2=read(),Modify(x,y,c,c2,i<<1); break;
			case 3:
			{
				rt=read(),x=read(),c=read();
				if(x==rt) T.ModifyI(1,n,1,1,n,mp(i<<1,c));
				else if(dfn[x]<=dfn[rt]&&dfn[rt]<=out[x])
				{
					y=Jump(rt,x), T.ModifyI(1,n,1,1,dfn[y]-1,mp(i<<1,c));
					if(out[y]<n) T.ModifyI(1,n,1,out[y]+1,n,mp(i<<1,c));
				}
				else if(dfn[x]<out[x]) T.ModifyI(1,n,1,dfn[x]+1,out[x],mp(i<<1,c));
				break;
			}
		}

	return 0;
}

考试代码

A

傻了,一直不会统计答案,只要把两个关键字的顺序一换就行了啊QAQ。

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define mp std::make_pair
#define pr std::pair<int,int>
#define gc() getchar()
#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
#define mod 998244353
#define Mod(x) x>=mod&&(x-=mod)
typedef long long LL;
const int N=1e6+5;

int n,m,outd[N],ind[N],pw[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Graph
{
	int Enum,H[N],nxt[N],to[N],len[N];
	void AE(int u,int v,int w)
	{
		to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum, len[Enum]=w;
	}
}G,R;
struct Node
{
	int id;
	pr x;
	inline bool operator <(const Node &a)const
	{
		return x<=a.x;
	}
}A[N];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-'0',c=gc());
	return now;
}
void Toposort()
{
	static int q[N],f[N],lev[N],lev2[N],pre[N],sum[N],dgr[N];
	static bool inq[N],vis[N];

	int h=0,t=0;
	memcpy(dgr,outd,sizeof dgr);
	for(int i=1; i<=n; ++i) if(!dgr[i]) q[++t]=i;
	while(h<t)
	{
		int x=q[++h]; inq[x]=1;
		for(int i=R.H[x],v; i; i=R.nxt[i])
		{
			f[v=R.to[i]]=std::max(f[v],f[x]+1);
			if(!--dgr[v]) q[++t]=v;
		}
	}

	memset(lev,0x3f,sizeof lev);
	memcpy(dgr,ind,sizeof dgr);
	h=0,t=0;
	for(int i=1; i<=n; ++i) if(!dgr[i]) q[++t]=i,lev[i]=1,lev2[i]=1;
	int Gap=t;
	while(h<t)
	{
		if(h==Gap)
		{
			for(int i=h+1; i<=t; ++i) A[i]=(Node){q[i],mp(lev[q[i]],pre[q[i]])}, lev2[q[i]]=lev[q[i]];
			std::sort(A+h+1,A+t+1);
			int tmp=1; lev[A[h+1].id]=1;
			for(int i=h+2; i<=t; ++i)
				if(A[i].x!=A[i-1].x) lev[A[i].id]=++tmp;
				else lev[A[i].id]=tmp;
//			for(int i=h+1; i<=t; ++i)
//				printf("%d:id:%d lev:%d pre:%d\n",i,A[i].id,lev[A[i].id],pre[A[i].id]); puts("");
			Gap=t;
		}
		int x=q[++h];
		for(int i=G.H[x],v; i; i=G.nxt[i])
		{
			if(f[v=G.to[i]]+1==f[x])
			{
				if(lev[v]>lev[x])
					lev[v]=lev[x], pre[v]=G.len[i];//, sum[v]=sum[x]*29%mod+pre[v], Mod(sum[v]);
				else if(lev[v]==lev[x]&&pre[v]>G.len[i])
					pre[v]=G.len[i];//, sum[v]=sum[x]*29%mod+pre[v], Mod(sum[v]);
//				printf("v:%d lev[v]=%d pre[v]=%d pw[%d]=%d\n",v,lev[v],pre[v],f[v],pw[f[v]]);
			}
			if(!--dgr[v]) q[++t]=v;
		}
	}
//	puts("");
//	for(int i=1; i<=n; ++i) printf("%d:lev:%d lev2:%d pre:%d\n",i,lev[i],lev2[i],pre[i]); puts("");

	memcpy(dgr,outd,sizeof dgr);
	h=0,t=0;
	for(int i=1; i<=n; ++i) if(!dgr[i]) q[++t]=i;
	while(h<t)
	{
		int x=q[++h];
		for(int i=R.H[x],v; i; i=R.nxt[i])
		{
			if(f[v=R.to[i]]==f[x]+1 && lev2[v]==lev2[x] && pre[x]==R.len[i])
				sum[v]=sum[x]*29%mod+pre[x], Mod(sum[v]);
			if(!--dgr[v]) q[++t]=v;
		}
	}

	for(int i=1; i<=n; ++i)
		if(inq[i]) printf("%d\n",(int)(1ll*sum[i]*29%mod));
		else puts("Infinity");
}

int main()
{
//	freopen("A1.in","r",stdin);
//	freopen(".out","w",stdout);

	n=read(),m=read();
	pw[0]=1;
	for(int i=1; i<=n; ++i) pw[i]=1ll*pw[i-1]*29%mod;
	for(int i=1,u,v,w; i<=m; ++i) u=read(),v=read(),w=read(),++outd[u],++ind[v],G.AE(u,v,w),R.AE(v,u,w);
	Toposort();

	return 0;
}
posted @ 2018-10-21 21:00  SovietPower  阅读(292)  评论(0编辑  收藏  举报