模拟赛20200207 Day3

16 篇文章 0 订阅
13 篇文章 0 订阅
T1:
题目描述:

在这里插入图片描述
在这里插入图片描述

题解:

n ≤ 20 n\le20 n20的时候可以状压求出每个连通块的点分方案数。
然后就自闭了。
在这里插入图片描述
题解告诉我们,两棵点分树是可以合并的!两棵结构一定的点分树通过一条边连接后可以形成一些新的点分树,方案数与连边的两个点在点分树中的深度有关,并且在新的点分树中原来的两棵树的点分顺序是不变的。
画出左链右链,合并的过程有点类似于Treap的merge操作。

Code:

#include<bits/stdc++.h>
#define maxn 5005
using namespace std;
const int mod = 1e9+7;
int n,c[maxn][maxn],f[maxn][maxn],siz[maxn];
vector<int>G[maxn];
void dfs(int u,int ff){
	f[u][1]=siz[u]=1;
	for(int v:G[u]) if(v!=ff){
		dfs(v,u);
		for(int i=siz[u]+siz[v];i>=1;i--){
			f[u][i]=1ll*f[u][i]*f[v][0]%mod;
			for(int j=min(i-1,siz[u]),lim=max(1,i-siz[v]);j>=lim;j--)
				f[u][i]=(f[u][i]+1ll*f[u][j]*f[v][i-j]%mod*c[i-1][j-1])%mod;
		}
		siz[u]+=siz[v];
	}
	for(int i=siz[u];i>=0;i--) f[u][i]=(f[u][i]+f[u][i+1])%mod;
}
int main()
{
	scanf("%d",&n);
	for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),G[x].push_back(y),G[y].push_back(x);
	for(int i=(c[0][0]=1);i<=n;i++)
		for(int j=(c[i][0]=1);j<=i;j++)
			c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
	dfs(1,0);
	printf("%d\n",f[1][1]);
}

T2:
题目描述:

在这里插入图片描述
在这里插入图片描述

题解:

第一眼:bitset有50分!
建出Trie树,一个询问相当于问三棵Trie树的子树中的人的交集大小。
我菜炸了居然没有看出这就是个三维数点

子树相当于是dfs序的一段区间,如果把人记作 ( d f n 1 , d f n 2 , d f n 3 ) (dfn_1,dfn_2,dfn_3) (dfn1,dfn2,dfn3),那么询问就相当于统计在 l 1 ≤ d f n 1 ≤ r 1 l_1\le dfn_1\le r_1 l1dfn1r1 l 2 ≤ d f n 2 ≤ r 2 l_2\le dfn_2\le r_2 l2dfn2r2 l 3 ≤ d f n 3 ≤ r 3 l_3\le dfn_3\le r_3 l3dfn3r3的点数。
而且人一开始就给出了,直接三维数点,排序+cdq+树状数组,虽然是 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)但是常数优秀就可以过。

题解给出的解法是 O ( n l o g n ) O(nlogn) O(nlogn)的:
因为总字符串长度是 O ( n ) O(n) O(n)的,所以可以将第一棵树中的子树转化为链加,即在每个人在第一棵trie上到根的路径上全部插入这个人,我们就是要询问后两个trie中某两子树中的人与第⼀个trie中某个点上的人的交集大小。这样问题就转化成了二维数点问题,把第⼀个trie上每个点的询问离线出来,在后两个trie的dfs序做一个二维数点即可,这个可以排序之后扫描线树状数组(或者在线主席树也可以)。

Code:

#include<bits/stdc++.h>
#define maxn 500005
#define maxc 26
using namespace std;
int n,Q,ans[maxn],a[3]={1,2,3},b[3],pos[maxn][3],in[maxn],out[maxn],tim,lim;
int rt[3]={1,2,3},ch[maxn][maxc],fa[maxn],t0[maxn],tot=3;
char s[maxn];
void ins(int t,char *s,int x){
	int r=rt[t],v;
	for(int i=0;s[i];i++,r=ch[r][v])
		if(!ch[r][v=s[i]-'a']) fa[ch[r][v]=++tot]=r;
	pos[x][t]=r;
}
void dfs(int r){
	in[r]=++tim;
	for(int i=0,c;i<maxc;i++) if(c=ch[r][i]) dfs(c);
	out[r]=tim;
}
void dfs2(int r){t0[++t0[0]]=r;for(int i=0,c;i<maxc;i++) if(c=ch[r][i]) dfs2(c);}
struct node{int x,y;bool operator < (const node &p)const{return x<p.x;}};
struct qry{int x,l,r,t,id;bool operator < (const qry &p)const{return x<p.x;}};
vector<node>p[maxn];
vector<qry>q[maxn];
int arr[maxn];
inline void upd(int i,int d){for(;i<=lim;i+=i&-i) arr[i]+=d;}
inline int qsum(int i){int s=0;for(;i;i-=i&-i) s+=arr[i];return s;}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++) for(int j=0;j<3;j++) scanf("%s",s),ins(j,s,i);
	dfs(rt[2]),lim=tim,dfs(rt[1]),dfs2(rt[0]);
	for(int i=1;i<=n;i++) for(int j=pos[i][0];j;j=fa[j]) p[j].push_back((node){in[pos[i][1]],in[pos[i][2]]});
	scanf("%d",&Q);
	int x,c; char op;
	for(int i=1;i<=Q;i++){
		scanf("\n%c%d",&op,&x),x--;
		if(op=='+'){
			getchar(),c=getchar()-'a';
			if(b[x]||!ch[a[x]][c]) b[x]++;
			else a[x]=ch[a[x]][c];
		}
		else{
			if(b[x]) b[x]--;
			else a[x]=fa[a[x]];
		}
		if(b[0]||b[1]||b[2]) continue;
		q[a[0]].push_back((qry){in[a[1]]-1,in[a[2]],out[a[2]],-1,i});
		q[a[0]].push_back((qry){out[a[1]],in[a[2]],out[a[2]],1,i});
	}
	for(int o=1,i;o<=t0[0];o++){
		i=t0[o],sort(p[i].begin(),p[i].end()),sort(q[i].begin(),q[i].end());
		int sq=q[i].size(),sp=p[i].size(),k=0;
		for(int j=0;j<sq;j++){
			while(k<sp&&p[i][k].x<=q[i][j].x) upd(p[i][k].y,1),k++;
			ans[q[i][j].id]+=q[i][j].t*(qsum(q[i][j].r)-qsum(q[i][j].l-1));
		}
		while(k--) upd(p[i][k].y,-1);
	}
	for(int i=1;i<=Q;i++) printf("%d\n",ans[i]);
}

T3:
题目描述:

在这里插入图片描述
在这里插入图片描述

题解:

只需要明白四点:

  • g c d ( a , b ) = g c d ( a , b % a ) gcd(a,b)=gcd(a,b\%a) gcd(a,b)=gcd(a,b%a)
  • g c d ( a c , b c ) = c ∗ g c d ( a , b ) gcd(ac,bc)=c*gcd(a,b) gcd(ac,bc)=cgcd(a,b)
  • g c d ( a , c ) = 1 gcd(a,c)=1 gcd(a,c)=1,则 g c d ( a , b c ) = g c d ( a , b ) gcd(a,bc)=gcd(a,b) gcd(a,bc)=gcd(a,b)
  • g c d ( F [ n ] , F [ n + 1 ] ) = 1 gcd(F[n],F[n+1])=1 gcd(F[n],F[n+1])=1(扩展结论是 g c d ( F [ n ] , F [ m ] ) = F [ g c d ( n , m ) ] gcd(F[n],F[m])=F[gcd(n,m)] gcd(F[n],F[m])=F[gcd(n,m)]
    在这里插入图片描述
    实际求 F [ n + 1 ] % a F[n+1]\%a F[n+1]%a F [ n + 1 ] % ( g c ) F[n+1]\%(gc) F[n+1]%(gc)不需要算两遍,直接算 F [ n + 1 ] % ( a c ) F[n+1]\%(ac) F[n+1]%(ac)就可以了。

Code:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
const int mod = 998244353;
int p;
struct Mat{
	int s[2][2];
	Mat(){memset(s,0,sizeof s);}
	Mat operator * (const Mat &B)const{
		Mat ret; const int p = ::p;
		for(int k=0;k<2;k++) for(int i=0;i<2;i++) for(int j=0;j<2;j++)
			ret.s[i][j]=(ret.s[i][j]+1ll*s[i][k]*B.s[k][j])%p;
		return ret;
	}
}f,g;
inline int solve(LL n,int p){
	::p=p,f=Mat(),g=Mat(),f.s[0][0]=g.s[0][0]=g.s[0][1]=g.s[1][0]=1;
	for(n--;n;n>>=1,g=g*g) if(n&1) f=f*g;
	return f.s[0][0];
}
LL n;
int T,a,b,c,d,x,y,ans;
int main()
{
	read(T);
	while(T--){
		read(n),read(a),read(b),read(c),read(d);
		while(c) x=c,y=a/c,c=a-y*c,a=x,b-=y*d,swap(b,d);
		if(d<0) d=-d;
		if(b<0) b+=(-b+d-1)/d*d;
		if(!d) solve(n+1,mod),ans=(1ll*a*f.s[0][1]+1ll*b*f.s[0][0])%mod;
		else if(!a) ans=1ll*__gcd(b,d)*solve(n+1,mod)%mod;
		else{
			x=__gcd(a,solve(n+1,a*d));
			ans=1ll*x*__gcd(int((1ll*a*f.s[0][1]+1ll*b*f.s[0][0])%(x*d)/x),d)%mod;
		}
		printf("%d\n",(ans+mod)%mod);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值