t1
题目大意
有一个有(n)((nleq152501))个节点的有向图,每个节点的出度和入度都是(1),随机选择不重复的(k)个点,求从这(k)个点出发,能走到每一个点的概率,模998244353
题解
发现(n)个点中选(k)个共有(C_{n}^{k})个方案,那么只要求出有多少种选点方案使从选中的点能走到每一个点,就能用这个方案数除以(C_{n}^{k})得到答案
每个节点的出度和入度都是(1)的有向图只由若干个环组成,设一共有(m)个环
所以当且仅当每一个环上都存在一个选中的点时,能从选中的点走到每一个点
设(f(i,j))表示考虑前(i)个环,选(j)个点,能使每个环上都有选中的点的方案数,(siz(i))表示第(i)个环上有几个点
则有
设函数$$g_i(x)= 0*x0+sum_{j=1}{min(siz(i),k)}C_{siz(i)}j*xj $$
设函数(h_i(x)),(j)次项的系数为(f(i,j))
则(h_i)为(h_{i-1})和(g_i)的卷积
那么方案数就变成了将(g_1),(g_2),……,(g_m)都卷起来后,(k)次项的系数
发现直接暴力将相邻的两个卷起来可能会被卡
可以像哈夫曼树那样合并,或进行分治,算出前一半和后一半结果后将这两个结果卷起来
代码
我写的是分治的做法
#include<algorithm>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(int i=(x);i>=(y);--i)
#define maxn 152510
#define maxm (maxn<<3)
#define LL long long
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0'),putchar('
');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('
');
return;
}
const LL mod=998244353;
int n,k,m,r[maxm],len,t,p[maxn],inv[maxn],po[maxn],siz[maxn];
int a[maxm],b[maxm],cnt,vis[maxn],g[maxn],ing[maxn],nowlen,nown;
int C(int x,int y){if(x==y)return 1;if(y>x)return 0;return (LL)po[x]*(LL)inv[y]%mod*(LL)inv[x-y]%mod;}
int mul(int x,int y)
{
int ans=1;
while(y)
{
if(y&1)ans=((LL)ans*(LL)x)%mod;
x=((LL)x*(LL)x)%mod,y>>=1;
}
return ans;
}
void fntt(int * c,int f)
{
rep(i,0,nown-1)r[i]=(r[i>>1]>>1)|((i&1)<<(nowlen-1));
rep(i,0,nown-1)if(i<r[i])swap(c[i],c[r[i]]);
for(int i=1;i<nown;i<<=1)
{
int wn=mul(3,(mod-1)/(i<<1)),x,y;
if(f==-1)wn=mul(wn,mod-2);
for(int j=0;j<nown;j+=(i<<1))
{
int w=1;
rep(k,0,i-1)
x=c[j+k]%mod,y=((LL)w*(LL)c[j+i+k])%mod,c[j+k]=(x+y)%mod,c[j+i+k]=(x-y+mod)%mod,w=(LL)w*(LL)wn%mod;
}
}
if(f==-1)
{
int invn=mul(nown,mod-2);
rep(i,0,nown-1)c[i]=(LL)c[i]*(LL)invn%mod;
}
}
vector<int>v[maxn<<2];
void reset(){rep(i,0,n)vis[i]=siz[i]=0;rep(i,0,(n<<2))v[i].clear();cnt=0;}
int getsiz(int u){vis[u]=1;if(!vis[p[u]])return getsiz(p[u])+1;else return 1;}
void getans(int L,int R)
{
if(L==R)
{
int lim=min(k,siz[L]);
v[L].push_back(0);
rep(i,1,lim){v[L].push_back(C(siz[L],i));}
return;
}
int mid=(L+R)>>1,lim1,lim2;
getans(L,mid),getans(mid+1,R);
lim1=v[L].size(),lim2=v[mid+1].size();
rep(i,0,lim1-1)a[i]=v[L][i];
rep(i,0,lim2-1)b[i]=v[mid+1][i];nowlen=0,nown=1;
while(nown-1<lim1+lim2-1)nown<<=1,nowlen++;
rep(i,lim1,nown-1)a[i]=0;
rep(i,lim2,nown-1)b[i]=0;
fntt(a,1),fntt(b,1);
rep(i,0,nown-1)a[i]=(LL)a[i]*(LL)b[i]%mod;
fntt(a,-1);
rep(i,0,lim1-1)v[L][i]=a[i];int lim3=min(k,lim1+lim2-1);
rep(i,lim1,lim3)v[L].push_back(a[i]);
return;
}
int main()
{
t=read();po[0]=po[1]=1;
rep(i,1,152501)po[i]=(LL)po[i-1]*(LL)i%mod;inv[152501]=mul(po[152501],mod-2);
dwn(i,152500,1)inv[i]=(LL)inv[i+1]*(LL)(i+1)%mod;
while(t--)
{
n=read(),k=read();
reset();
rep(i,1,n)p[i]=read();
rep(i,1,n)if(!vis[i]){cnt++,siz[cnt]=getsiz(i);}
getans(1,cnt);int lim=v[1].size();
if(lim<=k)write(0);
else write((LL)v[1][k]*(LL)mul(C(n,k),mod-2)%mod);
}
return 0;
}
t2
题目大意
有一个列数为(m)((mleq8))的方格图,有(s1)种颜色的(1*2)的地砖,(s2)种颜色的(2*1)的地砖,(f(x))表示当方格图行数为(x)时,将整个方格图铺满有多少种方案
给出(L,R)((Lleq Rleq10^{2501})),求(f(L)+f(L+1)+...+f(R-1)+f(R))模998244353
题解
前置知识不会,先坑着
代码
只会80分矩乘
#include<algorithm>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define maxs ((1<<8)+3)
#define maxlen 2510
#define LL long long
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0'),putchar('
');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('
');
return;
}
const LL mod=998244353;
int vis[maxs],sta[maxs],cnt,ans[2][maxs],to[maxs][maxs],tmpans[maxs],tmp[maxs][maxs],tmp2[maxs][maxs],tmp3[maxs][maxs];
int num[2][maxlen],len[2],m,s1,s2;
int mul(int x,int y)
{
int ans=1;
while(y)
{
if(y&1)ans=(LL)ans*(LL)x%mod;
x=(LL)x*(LL)x%mod,y>>=1;
}
return ans;
}
void getst(int id,int step,int nowst,int cnt1,int cnt2)
{
if(step==m)
{
if(!vis[nowst])vis[nowst]=++cnt,sta[cnt]=nowst;
to[id][vis[nowst]]=(to[id][vis[nowst]]+(LL)mul(s1,cnt1)%mod*(LL)mul(s2,cnt2)%mod)%mod;return;
}
if((sta[id]&(1<<step))!=0)getst(id,step+1,nowst,cnt1+1,cnt2);
else
{
if(step<m-1&&((sta[id]&(1<<step))==0)&&((sta[id]&(1<<(step+1)))==0))getst(id,step+2,nowst,cnt1,cnt2+1);
getst(id,step+1,(nowst|(1<<step)),cnt1,cnt2);
}
return;
}
void work(int f)
{
rep(i,1,len[f])
{
rep(xi,1,cnt)rep(xj,1,cnt){tmp2[xi][xj]=0;if(xi==xj)tmp2[xi][xj]=1;}
int cntnum=0;
rep(j,1,num[f][i])
{
cntnum++;
rep(xi,1,cnt)
rep(xj,1,cnt)
{
tmp3[xi][xj]=0;
rep(xk,1,cnt)tmp3[xi][xj]=(tmp3[xi][xj]+(LL)tmp2[xi][xk]*(LL)tmp[xk][xj]%mod)%mod;
}
rep(xi,1,cnt)
rep(xj,1,cnt)
tmp2[xi][xj]=tmp3[xi][xj];
}
rep(xj,1,cnt)
{
tmpans[xj]=0;
rep(xk,1,cnt)tmpans[xj]=(tmpans[xj]+(LL)ans[f][xk]*(LL)tmp2[xk][xj]%mod)%mod;
}
rep(xi,1,cnt)ans[f][xi]=tmpans[xi];
rep(j,num[f][i]+1,10)
{
rep(xi,1,cnt)
rep(xj,1,cnt)
{
tmp3[xi][xj]=0;
rep(xk,1,cnt)tmp3[xi][xj]=(tmp3[xi][xj]+(LL)tmp2[xi][xk]*(LL)tmp[xk][xj]%mod)%mod;
}
rep(xi,1,cnt)
rep(xj,1,cnt)
tmp2[xi][xj]=tmp3[xi][xj];
}
rep(xi,1,cnt)rep(xj,1,cnt)tmp[xi][xj]=tmp2[xi][xj];
}
return;
}
int main()
{
char c=getchar();
while(isdigit(c)){num[0][++len[0]]=c-'0',c=getchar();}
reverse(num[0]+1,num[0]+len[0]+1);
for(int i=1;i<=len[0];i++)
if(num[0][i]==0)num[0][i]=9;
else {num[0][i]--;break;}
while(!isdigit(c))c=getchar();
while(isdigit(c)){num[1][++len[1]]=c-'0',c=getchar();}
reverse(num[1]+1,num[1]+len[1]+1);
m=read(),s1=read(),s2=read();
vis[0]=1,sta[++cnt]=0;
rep(i,1,cnt)getst(i,0,0,0,0);cnt++;
rep(i,1,cnt-1)to[i][cnt]=to[i][1];
to[cnt][cnt]=1;
ans[0][1]=ans[1][1]=1;
rep(i,1,cnt)rep(j,1,cnt)tmp[i][j]=to[i][j];
work(0);
rep(i,1,cnt)rep(j,1,cnt)tmp[i][j]=to[i][j];
work(1);
write(((ans[1][cnt]-ans[0][cnt])%mod+mod)%mod);
return 0;
}
t3
题目大意
有一棵有(n)((nleq152501))节点的树,有边权、点权,共有(q)((qleq152501))次操作,支持两种操作:
1.修改一个点的点权
2.给出两点(x,y),要在(x,y)之间的简单路径上找一个点(u),使得(x,y)之间的简单路径上其他点的点权乘该点到点(u)的距离之和最小,输出最小值
保证2操作的结果不超过(10^{18})
题解
先坑着
代码
#include<algorithm>
#include<cmath>
#include<complex>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define maxn 152510
#define LL long long
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(LL x)
{
if(x==0){putchar('0'),putchar('
');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('
');
return;
}
int dfn[maxn],tim,siz[maxn],n,q;
int fir[maxn],nxt[maxn<<1],v[maxn<<1],cnt,anc[maxn][20];
LL w[maxn<<1],dep[maxn],dep0[maxn],s[2][maxn],a[maxn],ans;
void ade(int u1,int v1,LL w1){v[cnt]=v1,w[cnt]=w1,nxt[cnt]=fir[u1],fir[u1]=cnt++;}
int lt(int x){return x&(-x);}
void add(int x,LL y,int f){for(;x<=n;x+=lt(x))s[f][x]+=y;return;}
LL ask(int x,int f){LL y=0;for(;x;x-=lt(x))y+=s[f][x];return y;}
void getanc(int u)
{
dfn[u]=++tim;siz[u]=1;
rep(i,1,18)anc[u][i]=anc[anc[u][i-1]][i-1];
for(int k=fir[u];k!=-1;k=nxt[k])
if(v[k]!=anc[u][0])anc[v[k]][0]=u,dep0[v[k]]=dep0[u]+1,dep[v[k]]=dep[u]+w[k],getanc(v[k]),siz[u]+=siz[v[k]];
return;
}
int Lca(int x,int y)
{
if(dep0[x]<dep0[y])swap(x,y);
dwn(i,18,0)if(anc[x][i]&&dep0[anc[x][i]]>=dep0[y])x=anc[x][i];
if(x==y)return x;
dwn(i,18,0)if(anc[x][i]!=anc[y][i])x=anc[x][i],y=anc[y][i];
return anc[x][0];
}
LL getdis(int x,int y,int lca,int u)
{
LL lans=(ask(dfn[x],1)-ask(dfn[anc[u][0]],1))-dep[u]*(ask(dfn[x],0)-ask(dfn[anc[u][0]],0)),
rans=(dep[u]-dep[lca]-dep[lca])*(ask(dfn[y],0)-ask(dfn[lca],0))+ask(dfn[y],1)-ask(dfn[lca],1)+
dep[u]*(ask(dfn[u],0)-ask(dfn[anc[lca][0]],0))-(ask(dfn[u],1)-ask(dfn[anc[lca][0]],1)),
lsiz=ask(dfn[x],0)-ask(dfn[u],0),rsiz=ask(dfn[anc[u][0]],0)-ask(dfn[anc[lca][0]],0)+ask(dfn[y],0)-ask(dfn[lca],0);
ans=(ans==-1)?lans+rans:min(ans,lans+rans);
return lsiz-rsiz;
}
void work(int x,int y,int lca)
{
int tx=x;LL nowsiz=getdis(x,y,lca,x),nxtsiz;
dwn(i,18,0)
{
if(anc[tx][i]&&dep0[anc[tx][i]]>dep0[lca])
{
nxtsiz=getdis(x,y,lca,anc[tx][i]);
if(nxtsiz<=0)
{
tx=anc[tx][i];
nowsiz=nxtsiz;
}
}
}
if(tx!=lca)getdis(x,y,lca,anc[tx][0]);
return;
}
int main()
{
n=read();
memset(fir,-1,sizeof(fir));
rep(i,1,n)a[i]=read();
rep(i,1,n-1){int x=read(),y=read(),z=read();ade(x,y,z),ade(y,x,z);}
getanc(1);
rep(i,1,n)add(dfn[i],a[i],0),add(dfn[i]+siz[i],-a[i],0),add(dfn[i],a[i]*dep[i],1),add(dfn[i]+siz[i],-a[i]*dep[i],1);
q=read();
while(q--)
{
int tp=read(),x=read(),y=read(),lca=Lca(x,y);
if(tp==1)
{
ans=-1;
work(x,y,lca),work(y,x,lca);
write(ans);
}
else
{
int ad=y-a[x];
add(dfn[x],ad,0),add(dfn[x]+siz[x],-ad,0),add(dfn[x],dep[x]*ad,1),add(dfn[x]+siz[x],-dep[x]*ad,1);
a[x]=y;
}
}
return 0;
}