2021ccpc预选重赛

F

\(dp\)出前\(i\)个位置匹配了多少个\(nunhehheh\),再统计每个位置后面\(a\)的个数即可计算答案

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
namespace CALC
{
	inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
	inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
	inline int mul(int a,int b){return (1LL*a*b)%MOD;}
	inline void inc(int &a,int b){a=pls(a,b);}
	inline void dec(int &a,int b){a=mns(a,b);}
	inline void tms(int &a,int b){a=mul(a,b);}
	inline int qp(int x,int t,int res=1)
		{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
	inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
int n,pw[MAXN],f[MAXN][10],sum[10],numa[MAXN],ans;
char s[MAXN];
int main()
{
	pw[0]=1;rep(i,1,1e5) pw[i]=mul(pw[i-1],2);
	rep(T,1,read())
	{
		scanf("%s",s+1);n=strlen(s+1);
		numa[n+1]=0;ans=0;Fill(sum,0);
		dwn(i,n,1) numa[i]=numa[i+1]+(s[i]=='a');
		rep(i,1,n)
		{
			Fill(f[i],0);
			if(s[i]=='n') f[i][0]=1,f[i][2]=sum[1];
			else if(s[i]=='u') f[i][1]=sum[0];
			else if(s[i]=='h')
				f[i][3]=sum[2],f[i][5]=sum[4],f[i][6]=sum[5],f[i][8]=sum[7];
			else if(s[i]=='e') f[i][4]=sum[3],f[i][7]=sum[6];
			rep(j,0,8) inc(sum[j],f[i][j]);
			inc(ans,mul(f[i][8],mns(pw[numa[i+1]],1)));
		}
		printf("%d\n",ans);
	}
}

H

对于一个\(m\)的排列,分两类情况讨论

1.当这个排列完全属于一个\(n\)的排列时,容易得到此时贡献为\(m!(n-m+1)!\)

2.否则该排列被分成两个部分,分别存在于两个相邻的排列中

先不考虑排列数加一造成的影响,则此时贡献为\((m-1)m!(n-m)!\)

和上式加和可得\(n\cdot m!(n-m)!\),即\(m\)的排列可以放在任意位置,超出去的部分放到\(n\)个位置的前面

由于这种情况要求两个相邻的\(n\)排列的前面一部分一样

将该\(n\)排列分为三部分\(a\ b\ c\),其中\(a\ c\)中的所有数均\(\le m\)\(b\)中的数\(>m\)

容易发现当且仅当\(b\ c\)部分为一个单调下降序列时,这个排列的下一个排列前面才不会是\(a\)部分,否则均满足条件

考虑枚举\(c\)的长度,则前面\(a\)的部分随意排列,

不合法情况总数为:\(\sum\limits_{i=1}^{m-1}\binom{m}{i}(m-i)!=\sum\limits_{i=1}^{m-1}\frac{m!}{i!}=m!\sum\limits_{i=1}^{m-1}\frac{1}{i!}\)

最终答案为\(m!(n(n-m)!-\sum\limits_{i=1}^{m-1}\frac{1}{i!})\)

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 1001001
#define MOD 1000000007
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
namespace CALC
{
	inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
	inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
	inline int mul(int a,int b){return (1LL*a*b)%MOD;}
	inline void inc(int &a,int b){a=pls(a,b);}
	inline void dec(int &a,int b){a=mns(a,b);}
	inline void tms(int &a,int b){a=mul(a,b);}
	inline int qp(int x,int t,int res=1)
		{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
	inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
int n,m,fac[MAXN],ifac[MAXN],sum[MAXN];
void init(int n=1e6)
{
	fac[0]=ifac[0]=1;rep(i,1,n) fac[i]=mul(fac[i-1],i);
	ifac[n]=Inv(fac[n]);dwn(i,n-1,1) ifac[i]=mul(ifac[i+1],i+1);
	rep(i,1,n) sum[i]=pls(sum[i-1],ifac[i]);
}
int main()
{
	init();rep(T,1,read())
	{
		n=read(),m=read();
		printf("%d\n",mul(fac[m],mns(mul(fac[n-m],n),sum[m-1])));
	}
}

I

容易想到直接将边拆成点,但直接做显然复杂度无法接受

将所有边同一起点的边按照\(a_e\)升序排列之后,每条边可以优化的边即在一段连续的区间内

image

如图所示,将每个点\(x\)拆成\(deg_x+1\)个点,所有边权为\(a_e\)边起点仍为\(x\),边权为\(a_e-b_e\)的边按\(a_e\)排序后起点不同,相邻的边起点直接升序连接0边

对于终点为\(v\)的边,二分出合法区间,然后将\(a_e\)\(a_e-b_e\)的边均连向\(a\)最小的点即可 正确性容易验证

#include<bits/stdc++.h>
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 200100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
namespace CALC
{
	inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
	inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
	inline int mul(int a,int b){return (1LL*a*b)%MOD;}
	inline void inc(int &a,int b){a=pls(a,b);}
	inline void dec(int &a,int b){a=mns(a,b);}
	inline void tms(int &a,int b){a=mul(a,b);}
	inline int qp(int x,int t,int res=1)
		{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
	inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
const ll inf=1e18;
int n,m;
int nxt[MAXN<<2],fst[MAXN<<1],to[MAXN<<2],val[MAXN<<2],cnt;
int vis[MAXN<<1],l[MAXN],r[MAXN];
struct Edge{int u,v,a,b;}e[MAXN];
bool operator < (Edge x,Edge y){return x.u<y.u||(x.u==y.u&&x.a<y.a);}
void add(int u,int v,int w){nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v,val[cnt]=w;}
struct node{int x;ll dis;};
bool operator < (node a,node b) {return a.dis>b.dis;}
ll dis[MAXN<<1];
priority_queue <node> q;
inline void dij()
{
    int a;
    rep(i,1,n+m) dis[i]=inf,vis[i]=0;dis[1]=0;
    q.push((node){1,0});
    while(!q.empty())
    {
        a=q.top().x;q.pop();if(vis[a]) continue;
        vis[a]=1;
        for(int i=fst[a];i;i=nxt[i])
            if(dis[to[i]]>dis[a]+val[i])
				{dis[to[i]]=dis[a]+val[i];if(!vis[to[i]]) q.push((node){to[i],dis[to[i]]});};
    }
}
int main()
{
    int v,ed;e[0].a=0,e[0].b=0;
	rep(T,1,read())
    {
    	n=read(),m=read();
		rep(i,1,n) l[i]=r[i]=0;
		rep(i,1,n+m) fst[i]=0;cnt=0;
		rep(i,1,m) scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b);
		sort(e+1,e+m+1);
		rep(i,1,m)
		{
			if(!l[e[i].u]) l[e[i].u]=i;
			else add(i+n-1,i+n,0);
			r[e[i].u]=i;
		}
		rep(i,1,n) if(r[i]) add(r[i]+n,i,0);
		rep(i,1,m)
		{
			v=e[i].v;
			if(!l[v]) ed=v;
			else 
			{
				ed=upper_bound(e+l[v],e+r[v]+1,(Edge){e[i].v,0,e[i].a,0})-e;
				if(ed>r[v]) ed=v;else ed+=n;
			}
			add(e[i].u,ed,e[i].a);
			add(i+n,ed,e[i].a-e[i].b);
		}
		dij();
		rep(i,1,n) printf("%lld%c",dis[i]==inf?-1:dis[i],i==n?'\n':' ');
	}
}

K

由于所有点权均不相同,因此对于当前的连通块

显然所有点都可以选择走到当前联通块内点权最大的点,然后把这个点删掉,继续递归这个过程

事实上由于删点难以做到,我们考虑按照权值从小到大加入每个点

每次遇到相邻的已经被访问过的连通块,就从这个新点向该连通块的根连边,这样每个点的深度即为答案

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 100100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
namespace CALC
{
	inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
	inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
	inline int mul(int a,int b){return (1LL*a*b)%MOD;}
	inline void inc(int &a,int b){a=pls(a,b);}
	inline void dec(int &a,int b){a=mns(a,b);}
	inline void tms(int &a,int b){a=mul(a,b);}
	inline int qp(int x,int t,int res=1)
		{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
	inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
int n,nxt[MAXN<<1],to[MAXN<<1],cnt,fst[MAXN],dep[MAXN],vis[MAXN],fa[MAXN];
pii g[MAXN];
vector<int> G[MAXN];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void add(int u,int v){nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v;}
void dfs(int x,int pa)
{
	dep[x]=dep[pa]+1;
	for(auto v:G[x]) dfs(v,x);
}
int main()
{
	int a,b,x;rep(T,1,read())
	{
		n=read();cnt=0;
		rep(i,1,n) fa[i]=i,vis[i]=fst[i]=0,G[i].clear();
		rep(i,2,n) a=read(),b=read(),add(a,b),add(b,a);
		rep(i,1,n) g[i].fi=read(),g[i].se=i;
		sort(g+1,g+n+1);
		rep(i,1,n)
		{
			x=g[i].se;vis[x]=1;
			ren if(vis[to[i]])
				G[x].pb(find(to[i])),fa[find(to[i])]=x;
		}
		dfs(g[n].se,0);
		rep(i,1,n) printf("%d\n",dep[i]);
	}
}

比赛时候用了一种非常麻烦的做法

大概是按点权倒序加点,对每个点维护一个\(now_i\)

每次查询这个点到该点最近被访问过的祖先这条链上的\(now\)最大值\(+1\)即为答案

再把这条链上除最近被访问过的祖先以外的点的\(now\)用该点答案更新

查询祖先过程可以用二分+\(ST\)表预处理,修改与查询用树链剖分+线段树维护

#include<bits/stdc++.h>
#define inf 2139062143
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define MAXN 200100
#define MOD 998244353
#define Fill(a,x) memset(a,x,sizeof(a))
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
namespace CALC
{
	inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
	inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
	inline int mul(int a,int b){return (1LL*a*b)%MOD;}
	inline void inc(int &a,int b){a=pls(a,b);}
	inline void dec(int &a,int b){a=mns(a,b);}
	inline void tms(int &a,int b){a=mul(a,b);}
	inline int qp(int x,int t,int res=1)
		{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
	inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
int n,nxt[MAXN<<1],to[MAXN<<1],cnt,fst[MAXN],w[MAXN],h[MAXN],st[MAXN];
int fmx[MAXN][20],ff[MAXN][20];
pii g[MAXN];
int bl[MAXN],sz[MAXN],dep[MAXN],fa[MAXN],hvs[MAXN],in[MAXN],out[MAXN],dfn;
int mx[MAXN<<2],tag[MAXN<<2],ans[MAXN];
void add(int u,int v){nxt[++cnt]=fst[u],fst[u]=cnt,to[cnt]=v;}
void dfs(int x,int pa)
{
    sz[x]=1,fa[x]=pa,fmx[x][0]=w[pa],ff[x][0]=fa[x],hvs[x]=0;
    rep(j,1,19)
		ff[x][j]=ff[ff[x][j-1]][j-1],fmx[x][j]=max(fmx[x][j-1],fmx[ff[x][j-1]][j-1]);
	ren if(to[i]^pa)
    {
		dep[to[i]]=dep[x]+1;dfs(to[i],x);
		sz[x]+=sz[to[i]],hvs[x]=sz[to[i]]>sz[hvs[x]]?to[i]:hvs[x];
	}
}
void Dfs(int x,int anc)
{
    bl[x]=anc,in[x]=out[x]=++dfn;
	if(!hvs[x]) return ;Dfs(hvs[x],anc);
    ren if(to[i]^fa[x]&&to[i]^hvs[x]) Dfs(to[i],to[i]);out[x]=dfn;
}
void build(int k,int l,int r)
{
	tag[k]=mx[k]=0;if(l==r) return ;
	int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
inline void pshd(int k)
{
	mx[k<<1]=mx[k<<1|1]=tag[k<<1]=tag[k<<1|1]=tag[k],tag[k]=0;
}
void mdf(int k,int l,int r,int a,int b,int x)
{
	if(a<=l&&r<=b) {tag[k]=mx[k]=x;return;}int mid=l+r>>1;
	if(tag[k]) pshd(k);
	if(a<=mid) mdf(k<<1,l,mid,a,b,x);
	if(b>mid) mdf(k<<1|1,mid+1,r,a,b,x);
	mx[k]=max(mx[k<<1],mx[k<<1|1]);
}
int query(int k,int l,int r,int a,int b)
{
	if(a<=l&&r<=b) return mx[k];int mid=l+r>>1,res=0;
	if(tag[k]) pshd(k);
	if(a<=mid) res=max(res,query(k<<1,l,mid,a,b));
	if(b>mid) res=max(res,query(k<<1|1,mid+1,r,a,b));
	return res;
}
inline int getmx(int x,int ww)
{
	int res=0;dwn(i,19,0) if(ww&(1<<i))
		res=max(res,fmx[x][i]),x=ff[x][i];
	return res;
}
inline int getfa(int x,int ww)
{
	dwn(i,19,0) if(ww&(1<<i))
		x=ff[x][i];
	return x;
}
int solve(int x,int y,int res=0)
{
	for(;bl[x]!=bl[y];y=fa[bl[y]])
		res=max(res,query(1,1,n,in[bl[y]],in[y]));
	res=max(res,query(1,1,n,in[x],in[y]));
	return res;
}
void work(int x,int y,int z)
{
	for(;bl[x]!=bl[y];y=fa[bl[y]])
		mdf(1,1,n,in[bl[y]],in[y],z);
	if(in[x]<in[y]) mdf(1,1,n,in[x]+1,in[y],z);
}
int main()
{
	int a,b;bl[0]=1;rep(T,1,read())
	{
		n=read();rep(i,1,n) fst[i]=0;dfn=cnt=0;
		rep(i,2,n) a=read(),b=read(),add(a,b),add(b,a);
		rep(i,1,n) w[i]=h[i]=read();sort(h+1,h+n+1);
		rep(i,1,n) w[i]=lower_bound(h+1,h+n+1,w[i])-h,g[i]={w[i],i};
		sort(g+1,g+n+1);
		dfs(1,1);Dfs(1,1);
		rep(i,1,n)
		{
			int l=1,r=dep[i],res=dep[i]+1,mid=l+r>>1;
			for(;l<=r;mid=l+r>>1)
				if(getmx(i,mid)>w[i]) res=mid,r=mid-1;
				else l=mid+1;
			if(res==dep[i]+1) st[i]=0;else st[i]=getfa(i,res);
		}
		build(1,1,n);
		int x;dwn(i,n,1)
		{
			x=g[i].se;
			ans[x]=solve(st[x],x)+1;
			work(st[x],x,ans[x]);
		}
		rep(i,1,n) printf("%d\n",ans[i]);
	}
}
posted @ 2021-10-12 19:08  jack_yyc  阅读(57)  评论(0编辑  收藏  举报