Description
Solution
T1 shopping
目测是插板法乱搞一下
发现题解写的是容斥dp:
[ans = sum_i (-1)^ig[i]
]
(g[i])表示的有(i)个商店必然达到上限的方案数
考虑转化,设(f[i][j])表示前(i)个商店,必然超过限制的商店的(上限+1)的和是(j)
[f[i][j]=f[i-1][j]-f[i-1][j-w[i]-1]
]
所以答案就可以这样计算:
[ans=sum_i f[n][i] C(k+n-1,n-1)
]
后面那一块就是插板法
/*容斥dp */
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
const int MN=5e6+5,MM=301,mod=1e9+7;
int n,m,k,wi[MM],f[2][MM*MM+MN],fac[MN<<1],inv[MN<<1];
int C(int x,int y){if(y<=0)return 1;return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;}
int fpow(int x,int M){int r=1;for(;M;M>>=1,x=1ll*x*x%mod)if(M&1)r=1ll*r*x%mod;return r;}
int main()
{
register int i,j,qz=0,ans=0;
n=read(),m=read(),k=read();
for(i=1;i<=m;++i) wi[i]=read(),qz+=wi[i];
for(fac[0]=i=1;i<=n+k;++i)fac[i]=1ll*fac[i-1]*i%mod;
for(inv[n+k]=fpow(fac[n+k],mod-2),i=n+k-1;~i;--i)inv[i]=1ll*inv[i+1]*(i+1)%mod;
f[1][wi[1]+1]=mod-1;f[1][0]=1;
for(i=2;i<=m;++i)
{
for(j=0;j<=qz+m;++j)
{
int l=j-wi[i]-1;f[i&1][j]=f[(i&1)^1][j];
if(l>=0) f[i&1][j]+=mod-f[(i&1)^1][l],f[i&1][j]%=mod;
}
}
for(i=0;i<=k;++i)ans+=1ll*f[m&1][i]*C(k+n-i-1,n-1)%mod,ans%=mod;
printf("%d
",ans);
}
T2 highway
发现(n)很小,所以对于已经按照边权排号序的边集,求(Kruskal)生成森林的只要(O(alpha(n)n))
考虑线段树维护区间,每个节点保存用到的边(有序的),合并的时候做一次(Kruskal)即可
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
#define reg register
const int MN=105,MM=1e5+5;
int n,m,q;
struct edge{int u,v,w;}e[MM];
struct Node
{
vector<int> ed;
int val;
Node(){ed.clear();val=0;}
}T[MM<<2];
int fa[MN];
int getf(int x){return x==fa[x]?x:fa[x]=getf(fa[x]);}
bool union_(int x,int y){x=getf(x);y=getf(y);if(x==y)return false;fa[x]=y;return true;}
Node merge(Node x,Node y)
{
reg int si=x.ed.size(),sj=y.ed.size(),i,j;
for(i=1;i<=n;++i) fa[i]=i;
#define J y.ed[j]
#define I x.ed[i]
Node r;
for(i=0,j=0;i<si||j<sj;)
{
if(i==si||(j!=sj&&e[J].w<e[I].w))
{
if(union_(e[J].u,e[J].v)) r.ed.push_back(J),r.val+=e[J].w;
++j;continue;
}
if(union_(e[I].u,e[I].v)) r.ed.push_back(I),r.val+=e[I].w;
++i;
}
return r;
}
void build(int x,int l,int r)
{
if(l==r){T[x].ed.push_back(l);T[x].val=e[l].w;return;}
int mid=(l+r)>>1;
build(x<<1,l,mid);build(x<<1|1,mid+1,r);
T[x]=merge(T[x<<1],T[x<<1|1]);
}
Node query(int x,int l,int r,int a,int b)
{
if(l==a&&r==b) return T[x];
int mid=(l+r)>>1;
if(b<=mid) return query(x<<1,l,mid,a,b);
if(a>mid) return query(x<<1|1,mid+1,r,a,b);
else return merge(query(x<<1,l,mid,a,mid),query(x<<1|1,mid+1,r,mid+1,b));
}
int main()
{
n=read();m=read();q=read();
reg int i,l,r;
for(i=1;i<=m;++i) e[i].u=read(),e[i].v=read(),e[i].w=read();
build(1,1,m);
while(q--)
{
l=read(),r=read();
printf("%d
",query(1,1,m,l,r).val);
}
return 0;
}
T3 sailing
第一步,我们考虑舰队可以”整体地“位于哪几个位置
把环境和舰队的图都转化成一个(01)序列,可以位于一个位置当且仅当在某个区间中没有两个数同为(1)
考虑把其中一个序列倒序处理,就可以用(NTT)算出上面的答案
第二步,可以位于某个位置并不代表一定可以移动的到
从一个一定满足的位置(比如说舰队的原位置)开始(bfs),显然所有合法的位置都是相互联通的
第三步,求可以到达的格子数
一个格子可以到达,说明将舰队的(01)序列放在某个合法的位置上,这个位置是(1)
显然也可以用(NTT)来解决
#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
#define reg register
const int MN=705,P=998244353,g=3,invg=332748118;
int n,m;
int a[MN*MN*4],b[MN*MN*4],c[MN*MN*4],N,di,invN,pos[MN*MN*4];
char mp[MN][MN];
bool rea[MN][MN];
int fpow(int x,int m){int r=1;for(;m;m>>=1,x=1ll*x*x%P)if(m&1)r=1ll*r*x%P;return r;}
void NTT(int *a,int type)
{
reg int wi,w,i,j,p,k;
for(i=0;i<N;++i)if(i<pos[i])std::swap(a[i],a[pos[i]]);
for(i=1;i<N;i<<=1)
{
wi=fpow(type>0?g:invg,(P-1)/(i<<1));
for(j=i<<1,p=0;p<N;p+=j)
for(w=1,k=0;k<i;++k,w=1ll*w*wi%P)
{
int X=a[p+k],Y=1ll*a[i+p+k]*w%P;
a[p+k]=(X+Y)%P;a[i+p+k]=(X-Y+P)%P;
}
}
if(type<0)for(i=0;i<N;++i)a[i]=1ll*a[i]*invN%P;
}
std::queue<std::pair<int,int> > q;
const int dx[4]={0,-1,0,1},dy[4]={1,0,-1,0};
void bfs(int x,int y)
{
q.push(make_pair(x,y));rea[x][y]=false;
while(!q.empty())
{
int X=q.front().first,Y=q.front().second;q.pop();
a[(X-1)*m+Y]=1;
for(reg int i=0;i<4;++i)if(rea[X+dx[i]][Y+dy[i]])
rea[X+dx[i]][Y+dy[i]]=false,q.push(make_pair(X+dx[i],Y+dy[i]));
}
}
int main()
{
int i,j,x1=P,x2=-P,y1=P,y2=-P,ans=0;
n=read();m=read();
for(i=1;i<=n;++i) scanf("%s",mp[i]+1);
for(i=1;i<=n;++i)for(j=1;j<=m;++j)
{
if(mp[i][j]=='#') a[(i-1)*m+j]=1;
if(mp[i][j]=='o') x1=min(x1,i),x2=max(x2,i),y1=min(y1,j),y2=max(y2,j);
}
int T=(x2-x1)*m+y2-y1+1;
for(i=1;i<=n;++i)for(j=1;j<=m;++j)
if(mp[i][j]=='o') b[(i-x1)*m+j-y1+1]=c[T-(i-x1)*m-j+y1]=1;
for(N=1,di=0;N<=m*n*2;N<<=1,++di);invN=fpow(N,P-2);
for(i=0;i<N;++i) pos[i]=(pos[i>>1]>>1)|((i&1)<<(di-1));
NTT(a,1);NTT(b,1);NTT(c,1);
for(i=0;i<N;++i) a[i]=1ll*a[i]*c[i]%P;
NTT(a,-1);
for(i=1;i+(x2-x1)<=n;++i)for(j=1;j+(y2-y1)<=m;++j)if(a[T+(i-1)*m+j]==0)rea[i][j]=true;
memset(a,0,sizeof a);bfs(x1,y1);NTT(a,1);
for(i=0;i<N;++i) a[i]=1ll*a[i]*b[i]%P;
NTT(a,-1);
for(i=2;i<=n*m+1;++i) if(a[i]) ++ans;
return 0*printf("%d
",ans);
}
Blog来自PaperCloud,未经允许,请勿转载,TKS!