并不对劲的马后炮1210

t1

题目大意

有一个有\(n\)(\(n\leq152501\))个节点的有向图,每个节点的出度和入度都是\(1\),随机选择不重复的\(k\)个点,求从这\(k\)个点出发,能走到每一个点的概率,模998244353

题解

发现\(n\)个点中选\(k\)个共有\(C_{n}^{k}\)个方案,那么只要求出有多少种选点方案使从选中的点能走到每一个点,就能用这个方案数除以\(C_{n}^{k}\)得到答案
每个节点的出度和入度都是\(1\)的有向图只由若干个环组成,设一共有\(m\)个环
所以当且仅当每一个环上都存在一个选中的点时,能从选中的点走到每一个点
\(f(i,j)\)表示考虑前\(i\)个环,选\(j\)个点,能使每个环上都有选中的点的方案数,\(siz(i)\)表示第\(i\)个环上有几个点
则有

\[f(i,j)=\sum_{x=max(j-siz(i),1)}^{j-1}f(i-1,x)*C_{siz(i)}^x \]

设函数$$g_i(x)= 0*x0+\sum_{j=1}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('\n');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('\n');
    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\)(\(m\leq8\))的方格图,有\(s1\)种颜色的\(1*2\)的地砖,\(s2\)种颜色的\(2*1\)的地砖,\(f(x)\)表示当方格图行数为\(x\)时,将整个方格图铺满有多少种方案
给出\(L,R\)(\(L\leq R\leq10^{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('\n');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('\n');
	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\)(\(n\leq152501\))节点的树,有边权、点权,共有\(q\)(\(q\leq152501\))次操作,支持两种操作:
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('\n');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('\n');
	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;
}
posted @ 2018-12-12 10:17  echo6342  阅读(210)  评论(0编辑  收藏  举报