版权声明:随意转载哦......但还是请注明出处吧: https://blog.csdn.net/dreaming__ldx/article/details/89008328
战略游戏(game)
假设现在已经钦定了一条被所有人覆盖的路径
,我们要计算这条路径的贡献。
那么对于一个点
,设对于
的所有儿子,它们的
为
,那么搞一个生成函数
我们记
这个时候不难看出如果
不为祖孙,那么
就是这条路径的贡献。
否则假设
是祖先,
为
的祖先且为
的儿子,那么令
,这样这条路径的贡献就是
然后
可以用分治
算,时间复杂度总和为
,后面的则要枚举不同
的子树算,由于树上面一个点的不同
的子树数量是
的,因此总时间复杂度:
代码:
#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
typedef long long ll;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void update(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
const int N=1e5+5;
typedef vector<int> poly;
int lim,tim,pos[N<<2];
inline void init(const int&up){
lim=1,tim=0;
while(lim<=up)lim<<=1,++tim;
for(ri i=1;i<lim;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(tim-1));
}
inline void ntt(poly&a,const int&type){
for(ri i=0;i<lim;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
int a0,a1,mult=(mod-1)/2,typ=type==1?3:(mod+1)/3,w,wn;
for(ri mid=1;mid<lim;mid<<=1,mult>>=1){
wn=ksm(typ,mult);
for(ri j=0,len=mid<<1;j<lim;j+=len){
w=1;
for(ri k=0;k<mid;++k,w=mul(w,wn)){
a0=a[j+k],a1=mul(a[j+k+mid],w);
a[j+k]=add(a0,a1),a[j+k+mid]=dec(a0,a1);
}
}
}
if(type==-1)for(ri i=0,inv=ksm(lim,mod-2);i<lim;++i)a[i]=mul(a[i],inv);
}
inline poly operator*(poly a,poly b){
int n=a.size()-1,m=b.size()-1;
init(n+m);
a.resize(lim),b.resize(lim);
ntt(a,1),ntt(b,1);
for(ri i=0;i<lim;++i)a[i]=mul(a[i],b[i]);
return ntt(a,-1),a;
}
int tmp[N],top;
inline poly solve(int l,int r){
if(l==r){poly ret;return ret.push_back(1),ret.push_back(tmp[l]),ret;}
int mid=l+r>>1;
return solve(l,mid)*solve(mid+1,r);
}
vector<int>e[N],coe[N],upd;
int fa[N],sum[N],siz[N],ans=0,n,k,fac[N],ifac[N];
inline void init_inv(){
fac[0]=fac[1]=ifac[0]=ifac[1]=1;
for(ri i=2;i<=k;++i)fac[i]=mul(fac[i-1],i),ifac[i]=mul(ifac[mod-mod/i*i],mod-mod/i);
for(ri i=2;i<=k;++i)ifac[i]=mul(ifac[i-1],ifac[i]);
}
void dfs1(int p){
siz[p]=1;
for(ri i=0,v;i<e[p].size();++i){
if((v=e[p][i])==fa[p])continue;
fa[v]=p,dfs1(v),siz[p]+=siz[v];
update(ans,mul(sum[p],sum[v])),update(sum[p],sum[v]);
}
int val=top=0;
for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])^fa[p])tmp[++top]=siz[v];
if(top){
coe[p]=solve(1,top);
for(ri i=min(k,top);~i;--i)update(val,mul(ifac[k-i],coe[p][i]));
update(sum[p],mul(val,fac[k]));
}
else sum[p]=1;
}
map<int,int>mp[N];
typedef map<int,int>::iterator It;
inline void mul(poly&a,const int&b){
a.push_back(0);
for(ri i=a.size()-2;~i;--i)update(a[i+1],mul(a[i],b));
}
inline void div(poly&a,const int&b){
int inv=ksm(b,mod-2);
poly t=a;
for(ri i=a.size()-1;i;--i)a[i-1]=mul(t[i],inv),t[i-1]=dec(t[i-1],a[i-1]);
a.pop_back();
}
void dfs2(int p){
for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])^fa[p])dfs2(v),update(mp[p][siz[v]],sum[v]);
int val;
for(It it=mp[p].begin();it!=mp[p].end();++it){
val=0;
upd=coe[p];
div(upd,it->fi),mul(upd,n-siz[p]);
for(ri i=min((int)coe[p].size()-1-(p==1),k);~i;--i)update(val,mul(ifac[k-i],upd[i]));
update(ans,mul(mul(val,fac[k]),it->se));
}
}
int main(){
n=read(),k=read();
if(k==1)cout<<(ll)n*(n-1)/2%mod,exit(0);
init_inv();
for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
dfs1(1);
dfs2(1);
cout<<ans;
exit(0);
}
生成膜咒(string)
51nod原题
考虑对于一个串,每个前缀的贡献就是它在里面出现的次数,然后对于每次的答案用增量法算新的后缀的贡献,转化一下就是在
的
树上面把其对应节点到跟这条路径整体加贡献,离线+树剖搞。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int mod=1e9+7;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void modify(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
const int N=4e5+5;
int n,m;
char s[N];
namespace Sam{
int len[N],son[N][26],link[N],tot=1,pos[N],last=1;
inline void expand(const int&x,const int&id){
int p=last,np=++tot;
len[last=pos[id]=np]=len[p]+1;
while(p&&!son[p][x])son[p][x]=np,p=link[p];
if(!p){link[np]=1;return;}
int q=son[p][x],nq;
if(len[q]==len[p]+1){link[np]=q;return;}
len[nq=++tot]=len[p]+1,memcpy(son[nq],son[q],sizeof(son[nq])),link[nq]=link[q],link[q]=link[np]=nq;
while(p&&son[p][x]==q)son[p][x]=nq,p=link[p];
}
int num[N],pred[N],dep[N],top[N],fa[N],siz[N],hson[N],Tot=0;
vector<int>e[N];
void dfs1(int p){
siz[p]=1;
for(ri i=0,v;i<e[p].size();++i){
fa[v=e[p][i]]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
if(siz[v]>siz[hson[p]])hson[p]=v;
}
}
void dfs2(int p,int tp){
top[p]=tp,pred[num[p]=++Tot]=p;
if(!hson[p])return;
dfs2(hson[p],tp);
for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])^hson[p])dfs2(v,v);
}
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
int upd[N<<2],tag[N<<2],sum[N<<2];
inline void pushup(int p){sum[p]=add(sum[lc],sum[rc]);}
inline void pushnow(int p,int v){modify(sum[p],mul(upd[p],v)),modify(tag[p],v);}
inline void pushdown(int p){if(tag[p])pushnow(lc,tag[p]),pushnow(rc,tag[p]),tag[p]=0;}
inline void build(int p,int l,int r){
if(l==r){upd[p]=len[pred[l]]-len[link[pred[l]]];return;}
build(lc,l,mid),build(rc,mid+1,r);
upd[p]=add(upd[lc],upd[rc]);
}
inline int query(int p,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return tot=sum[p],pushnow(p,1),tot;
pushdown(p);
if(qr<=mid)return tot=query(lc,l,mid,ql,qr),pushup(p),tot;
if(ql>mid)return tot=query(rc,mid+1,r,ql,qr),pushup(p),tot;
return tot=add(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr)),pushup(p),tot;
}
#undef lc
#undef rc
#undef mid
inline int query(int x){
int ret=0;
while(x)modify(ret,query(1,1,n,num[top[x]],num[x])),x=fa[top[x]];
return ret;
}
inline void build(){
for(ri i=1;i<=n;++i)expand(s[i]-'a',i);
for(ri i=tot;i^1;--i)e[link[i]].push_back(i);
dfs1(1),dfs2(1,1);
m=n,n=tot;
build(1,1,n);
}
inline void solve(){
for(ri ans=0,sum=0,p,i=1;i<=m;++i){
p=pos[i];
modify(sum,query(p));
modify(ans,sum);
cout<<ans<<'\n';
}
}
}
int main(){
scanf("%d%s",&n,s+1);
Sam::build();
Sam::solve();
return 0;
}
猎人杀(hunter)
这场比赛T2的升级版。
我们发现就是从走
步变成了走
步。
原题的转移是每一层只有一个环。
现在可能有多个,然而它们是互不相交的,因此可以用
记一下未访问过的。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int mod=1e9+7,inv=5e8+4;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
const int N=2005;
struct dat{
int x,y;
dat(int x=0,int y=0):x(x),y(y){}
friend inline dat operator+(const dat&a,const dat&b){return dat(add(a.x,b.x),add(a.y,b.y));}
friend inline dat operator+(const dat&a,const int&b){return a+dat(0,b);}
friend inline dat operator*(const dat&a,const int&b){return dat(mul(a.x,b),mul(a.y,b));}
}g[N];
bool vis[N];
int f[N][N],k,idx[N];
inline int preidx(const int&i,const int&j){return j^k?(j>k?j-k:j-k+i):i;}
inline int sufidx(const int&i,const int&j){return j+k<=i?j+k:j+k-i;}
inline void update(int n,int m){
vis[m]=1,idx[1]=m;
if(m^k)g[1]=dat(inv,mul(inv,f[n-1][preidx(n,m)]));
else g[1]=dat(inv,0);
int p=sufidx(n,m),pre=m,tim=2,x0;
while(!vis[p]){
idx[tim]=p,vis[p]=1;
if(p^k)g[tim]=(g[tim-1]+f[n-1][pre])*inv;
else g[tim]=g[tim-1]*inv;
pre=p,p=sufidx(n,p),++tim;
}
--tim;
x0=mul(g[tim].y,ksm(dec(1,g[tim].x),mod-2));
for(ri i=1;i<=tim;++i)f[n][idx[i]]=add(mul(g[i].x,x0),g[i].y);
}
int n,K;
int main(){
cin>>n>>K;
if(n==1)return cout<<"1",0;
f[1][1]=1;
for(ri i=2;i<=n;++i){
for(ri j=1;j<i;++j)vis[j]=0;
k=K==K/i*i?i:K-K/i*i;
for(ri j=1;j<=i;++j)if(!vis[j])update(i,j);
}
cout<<f[n][1];
return 0;
}