T1:
题目描述:
题解:
n
≤
20
n\le20
n≤20的时候可以状压求出每个连通块的点分方案数。
然后就自闭了。
题解告诉我们,两棵点分树是可以合并的!两棵结构一定的点分树通过一条边连接后可以形成一些新的点分树,方案数与连边的两个点在点分树中的深度有关,并且在新的点分树中原来的两棵树的点分顺序是不变的。
画出左链右链,合并的过程有点类似于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
l1≤dfn1≤r1,
l
2
≤
d
f
n
2
≤
r
2
l_2\le dfn_2\le r_2
l2≤dfn2≤r2,
l
3
≤
d
f
n
3
≤
r
3
l_3\le dfn_3\le r_3
l3≤dfn3≤r3的点数。
而且人一开始就给出了,直接三维数点,排序+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)=c∗gcd(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);
}
}