[校内互测]20170402

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/clover_hxy/article/details/68952804

T1

这里写图片描述
这里写图片描述

题解

因为有限制的商店比较少,且有限制的商店的最高总消费是90000
所以我们考虑对有限制的商店看成是每个组有wi个物品体积是1…wi的多重背包问题。
f[i]表示的是总体积是i的方案数。
需要先枚举每个物品,在倒序枚举总体积,然后枚举当前物品选取的体积。这样子会TLE,所以我们需要前缀和优化一下,减去枚举当前物品选取的体积的那一层。
那么剩下的都是无限制的商店,可以用插板法计算方案数
ans=sumi=0f[i]C(ki+nm1,nm1)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#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);
}

T2

这里写图片描述
这里写图片描述

题解1:莫队+prim求最小生成树

思路与bzoj permu的做法三比较类似,也是需要用栈维护需要撤销的操作。
对于每次查询我们需要维护这段区间的邻接矩阵,然后用prim算法O(n^2)的求解最小生成树。
因为最后有可能树是不连通的,也就是一个生成森林,所以我们需要先用并查集维护出所有的连通块,然后将1先强制加入树中,对于1到每个连通块( 除去他自己所在的连通块)连一条边权为0的边。
注意最后做完后要撤回边权为0的边。

代码1

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#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]<belong[b.l]||belong[a.l]==belong[b.l]&&a.r<b.r;
}
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;j<i;j++) 
      if (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;i<n;i++) {
        int 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]<mincost) k=j,mincost=low[j];
        }
        pd[k]=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]);
}


题解2:线段树

考虑用线段树直接维护每个区间的答案。
注意到一个区间最多只有 n-1 条树边有用,所以线段树的每个节点按权值从小到大保存区间内用到的树边即可。合并两个区间的信息时,只需要将树边归并,然后做Kruskal 算法。

代码2

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#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].c<b.e[t2].c) c[++cnt]=a.e[t1++];
        else 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);
    }
}

猜你喜欢

转载自blog.csdn.net/clover_hxy/article/details/68952804
今日推荐