因为有限制的商店比较少,且有限制的商店的最高总消费是90000
所以我们考虑对有限制的商店看成是每个组有wi个物品体积是1…wi的多重背包问题。
f[i]表示的是总体积是i的方案数。
需要先枚举每个物品,在倒序枚举总体积,然后枚举当前物品选取的体积。这样子会TLE,所以我们需要前缀和优化一下,减去枚举当前物品选取的体积的那一层。
那么剩下的都是无限制的商店,可以用插板法计算方案数
ans=∑sumi=0f[i]∗C(k−i+n−m−1,n−m−1)
#include
#include
#include
#include
#include
#define N 100003
#define p 1000000007
#define LL long long
using namespace std;
int jc[10000003],inv[10000003];
int f[N],g[N],ans;
int n,m,k,sum,v[303],mi[33],cnt;
LL calc(int n,int m)
{
if (n<m) return 0;
return (LL)jc[n]*inv[m]%p*inv[n-m]%p;
}
int main()
{
freopen("shopping.in","r",stdin);
freopen("shopping.out","w",stdout);
scanf("%d%d%d",&n,&m,&k); sum=0; jc[0]=1; inv[0]=inv[1]=1;
for (int i=1;i<=m;i++) scanf("%d",&v[i]),sum+=v[i];
for (int i=1;i<=n+k;i++) jc[i]=(LL)jc[i-1]*i%p;
for (int i=2;i<=n+k;i++) inv[i]=(LL)(p-p/i)*inv[p%i]%p;
for (int i=1;i<=n+k;i++) inv[i]=(LL)inv[i-1]*inv[i]%p;
mi[0]=1;
for (int i=1;i<=20;i++) mi[i]=mi[i-1]*2;
f[0]=1;
for (int i=1;i<=m;i++) {
g[0]=f[0];
for (int j=1;j<=sum;j++) g[j]=(LL)(g[j-1]+f[j])%p;
for (int k=sum;k>=1;k--) {
int l=max(0,k-v[i]); int r=max(0,k-1);
if (l!=0) {
int sum=g[r]-g[l-1];
f[k]=(LL)(f[k]+sum)%p,f[k]=(f[k]%p+p)%p;
}
else f[k]=(LL)(f[k]+g[r])%p;
}
}
if (n==m) {
printf("%d\n",f[k]);
return 0;
}
n=n-m;
for (int i=0;i<=sum;i++){
if (i>k) break;
int t=k-i; int sum=0;
sum=(LL)f[i]*calc(t+n-1,n-1)%p;
ans=(ans+sum)%p;
}
printf("%d\n",ans);
}
思路与bzoj permu的做法三比较类似,也是需要用栈维护需要撤销的操作。
对于每次查询我们需要维护这段区间的邻接矩阵,然后用prim算法O(n^2)的求解最小生成树。
因为最后有可能树是不连通的,也就是一个生成森林,所以我们需要先用并查集维护出所有的连通块,然后将1先强制加入树中,对于1到每个连通块( 除去他自己所在的连通块)连一条边权为0的边。
注意最后做完后要撤回边权为0的边。
#include
#include
#include
#include
#include
#define N 300000
#define inf 1000000000
using namespace std;
int dis[103][103],dis1[103][103],low[103],pd[103],st[N],stx[N],sty[N];
int n,m,t,ans[N],belong[N],block,u[N*10],v[N*10],c[N*10],vis[103],fa[103];
struct data{
int l,r,id;
}q[N];
int cmp(data a,data b)
{
return belong[a.l]int find(int x)
{
if (fa[x]==x) return x;
fa[x]=find(fa[x]);
return fa[x];
}
int getans()
{
memset(pd,0,sizeof(pd));
memset(vis,0,sizeof(vis));
for (int i=1;i<=n;i++) low[i]=inf;
pd[1]=1; low[1]=0; int cur=1; int sum=0;
for (int i=1;i<=n;i++) fa[i]=i;
for (int i=2;i<=n;i++)
for (int j=1;jif (dis[i][j]!=dis[0][0]){
int r1=find(i); int r2=find(j);
if (r2!=r1) fa[r2]=r1;
}
for (int i=2;i<=n;i++) {
int t=find(i);
if (dis[1][i]!=dis[0][0]) vis[t]=true;
}
for (int i=2;i<=n;i++) {
int t=find(i);
if (!vis[t]) dis[1][t]=0,vis[t]=true;
}
for (int i=1;iint mincost=inf;
int k;
for (int j=1;j<=n;j++) {
if (j==cur||pd[j]) continue;
if (low[j]>dis[cur][j]) low[j]=dis[cur][j];
if (low[j]1;
if (mincost==inf) break;
sum+=mincost;
cur=k;
}
for (int i=2;i<=n;i++){
int t=find(i);
if (dis[1][t]==0) dis[1][t]=dis[0][0];
}
return sum;
}
int main()
{
freopen("highway.in","r",stdin);
freopen("highway.out","w",stdout);
scanf("%d%d%d",&n,&m,&t);
for (int i=1;i<=m;i++) scanf("%d%d%d",&u[i],&v[i],&c[i]);
for (int i=1;i<=t;i++)
scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
block=ceil(sqrt(m));
for (int i=1;i<=m;i++) belong[i]=(i-1)/block+1;
sort(q+1,q+t+1,cmp); int r,l;
for (int i=1;i<=t;i++) {
if (belong[q[i].l]!=belong[q[i-1].l]) {
memset(dis,127,sizeof(dis));
r=l=belong[q[i].l]*block;
}
while (r<q[i].r&&r<m) {
r++;
dis[u[r]][v[r]]=min(dis[u[r]][v[r]],c[r]),
dis[v[r]][u[r]]=min(dis[v[r]][u[r]],c[r]);
}
int top=0;
for (int j=q[i].l;j<=min(q[i].r,l);j++) {
++top; stx[top]=u[j]; sty[top]=v[j]; st[top]=dis[u[j]][v[j]];
dis[u[j]][v[j]]=min(dis[u[j]][v[j]],c[j]),
dis[v[j]][u[j]]=min(dis[v[j]][u[j]],c[j]);
}
ans[q[i].id]=getans();
for (int j=top;j>=1;j--) {
int x=stx[j]; int y=sty[j];
dis[x][y]=dis[y][x]=st[j];
}
}
for (int i=1;i<=t;i++) printf("%d\n",ans[i]);
}
考虑用线段树直接维护每个区间的答案。
注意到一个区间最多只有 n-1 条树边有用,所以线段树的每个节点按权值从小到大保存区间内用到的树边即可。合并两个区间的信息时,只需要将树边归并,然后做Kruskal 算法。
#include
#include
#include
#include
#include
#define N 100003
using namespace std;
struct node{
int u,v,c;
}e[N],c[N];
struct data{
node e[203];
int ans,cnt;
}tr[N*4];
int fa[N],n,m,t;
int find(int x)
{
if (fa[x]==x) return x;
fa[x]=find(fa[x]);
return fa[x];
}
data update(data a,data b)
{
data now; now.ans=0; now.cnt=0;
int t1=1; int t2=1; int cnt=0;
while (t1<=a.cnt&&t2<=b.cnt) {
if (a.e[t1].celse c[++cnt]=b.e[t2++];
}
for (int i=t1;i<=a.cnt;i++) c[++cnt]=a.e[i];
for (int i=t2;i<=b.cnt;i++) c[++cnt]=b.e[i];
int size=0;
for (int i=1;i<=n;i++) fa[i]=i;
for (int i=1;i<=cnt;i++) {
int r1=find(c[i].u); int r2=find(c[i].v);
if (r1!=r2) {
fa[r2]=r1;
now.e[++now.cnt]=c[i];
now.ans+=c[i].c;
}
}
return now;
}
void build(int now,int l,int r)
{
if (l==r) {
tr[now].cnt=1;
tr[now].e[1]=e[l];
tr[now].ans=e[l].c;
return;
}
int mid=(l+r)/2;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
tr[now]=update(tr[now<<1],tr[now<<1|1]);
}
data qjans(int now,int l,int r,int ll,int rr)
{
if (ll<=l&&r<=rr) return tr[now];
int mid=(l+r)/2; data ans; bool pd=false;
if (ll<=mid) ans=qjans(now<<1,l,mid,ll,rr),pd=true;
if (rr>mid) {
if (pd) ans=update(ans,qjans(now<<1|1,mid+1,r,ll,rr));
else ans=qjans(now<<1|1,mid+1,r,ll,rr);
}
return ans;
}
int main()
{
freopen("highway.in","r",stdin);
freopen("highway.out","w",stdout);
scanf("%d%d%d",&n,&m,&t);
for (int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].c);
build(1,1,m);
for (int i=1;i<=t;i++) {
int l,r; scanf("%d%d",&l,&r);
data now=qjans(1,1,m,l,r);
printf("%d\n",now.ans);
}
}