【JSOI 2008】【BZOJ 1016】最小生成数计数

这题题目中有一个很显眼的提示,每种权值的边不会超过10条,这提示我们可以采用些暴力方法。
首先在每个最小生成树中有两个结论:
1、每种权值的边数相等。
2、每种权值所选边构建后图的联通形态相同。
1比较好理解,若1不成立,则最小生成树总权值不固定。
2可以通过Kruskal算法流程来理解。
code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct hq{
    int sum;
    int num[1001];
}stack[1001],tot[1001];
struct hp{
    int u,v,w;
}a[1001];
int father[1001],now[1001],fathernow[1001],fatherlast[1001];
int b[1001],n,m,size,sum=0,t=0,ans=1;
int cmp(const hp &a,const hp &b)
{
    if (a.w<b.w) return 1;
    else return 0;
}
int findnow(int x)
{
    if (x!=fathernow[x])
      fathernow[x]=findnow(fathernow[x]);
    return fathernow[x];
}
int find(int x)
{
    if (x!=father[x])
      father[x]=find(father[x]);
    return father[x];
}
bool judge()
{
    int i,r1,r2;
    bool f=false;
    for (i=1;i<=n;++i)
      fathernow[i]=fatherlast[i];
    for (i=1;i<=t;++i)
      {
        r1=findnow(a[now[i]].u);
        r2=findnow(a[now[i]].v);
        if (r1<r2)
          fathernow[r1]=fathernow[r2];
        else
          {
            if (r2<r1)
              fathernow[r2]=fathernow[r1];
            else
              f=true;
          }
      }
    for (i=1;i<=n;++i)
      findnow(i),find(i);
    for (i=1;i<=n;++i)
      if (fathernow[i]!=father[i])
        f=true;
    for (i=1;i<=n;++i)
      fathernow[i]=fatherlast[i];
    if (f)
      return false;
    else
      return true;
}
void work(int wgt,int i,int last)
{
    int j;
    if (i==tot[wgt].sum+1)
      {
        if (judge())
          sum=(sum+1)%31011;
        return;
      }
    for (j=last+1;j<=stack[wgt].sum;++j)
      {
        now[++t]=stack[wgt].num[j];
        work(wgt,i+1,j);
        --t;
      }
}
int main()
{
    int i,r1,r2,j,k=0,wgt=0;
    scanf("%d%d",&n,&m);
    for (i=1;i<=m;++i)
      {
        scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
        b[i]=a[i].w;
      }
    sort(b+1,b+m+1);
    size=unique(b+1,+b+m+1)-b-1;
    for (i=1;i<=m;++i)
      a[i].w=upper_bound(b+1,b+size+1,a[i].w)-b-1;
    sort(a+1,a+m+1,cmp);
    for (i=1;i<=m;++i)
      stack[a[i].w].num[++stack[a[i].w].sum]=i;
    for (i=1;i<=n;++i)
      father[i]=i;
    for (i=1;i<=m;++i)
      {
        r1=find(a[i].u); r2=find(a[i].v); 
        if (r1!=r2)
          {
            tot[a[i].w].num[++tot[a[i].w].sum]=i;
            wgt+=a[i].w;
            father[r1]=r2;
            k++;
            size=a[i].w;
          }     
        if (k==n-1)
          break;  
      }
    if (k!=n-1)
      printf("0\n");
    else
      {
        for (i=1;i<=n;++i)
          father[i]=fatherlast[i]=i;
        for (i=1;i<=size;++i)
          {
            for (j=1;j<=tot[i].sum;++j)
              {
                r1=find(a[tot[i].num[j]].u);
                r2=find(a[tot[i].num[j]].v);
                if (r1<r2)
                  father[r1]=father[r2];
                if (r2<r1)
                  father[r2]=father[r1];
              }
            memset(now,0,sizeof(now));
            t=0; sum=0;
            work(i,1,0);  
            ans=(ans*sum)%31011;
            for (j=1;j<=n;++j)
              fatherlast[j]=father[j];
          }
        printf("%d\n",ans);
      }
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
根据引用[1],dp[u][j]表示在u子树选取恰好j个人时能获得的最大价值。而根据引用,该问题的时间复杂度为O(log2​104×nm)。 对于洛谷P2143 [JSOI2010] 巨额奖金问题,我们可以使用动态规划来解决。具体步骤如下: 1. 首先,我们需要构建一棵树来表示员工之间的关系。树的根节点表示公司的总经理,其他节点表示员工。每个节点都有一个权值,表示该员工的奖金金额。 2. 接下来,我们可以使用动态规划来计算每个节点的dp值。对于每个节点u,我们可以考虑两种情况: - 如果选择节点u,则dp[u][j] = dp[v][j-1] + value[u],其v是u的子节点,value[u]表示节点u的奖金金额。 - 如果不选择节点u,则dp[u][j] = max(dp[v][j]),其v是u的子节点。 3. 最后,我们可以通过遍历树的所有节点,计算出dp[u][j]的最大值,即为所的巨额奖金。 下面是一个示例代码,演示了如何使用动态规划来解决洛谷P2143 [JSOI2010] 巨额奖金问题: ```python # 构建树的据结构 class Node: def __init__(self, value): self.value = value self.children = [] # 动态规划解最大奖金 def max_bonus(root, j): dp = [[0] * (j+1) for _ in range(len(root)+1)] def dfs(node): if not node: return for child in node.children: dfs(child) for k in range(j, 0, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-1] + node.value) for child in node.children: for k in range(j, 0, -1): for l in range(k-1, -1, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-l-1] + dp[child.value][l]) dfs(root) return dp[root.value][j] # 构建树 root = Node(1) root.children.append(Node(2)) root.children.append(Node(3)) root.children[0].children.append(Node(4)) root.children[0].children.append(Node(5)) root.children[1].children.append(Node(6)) # 解最大奖金 j = 3 max_bonus_value = max_bonus(root, j) print("最大奖金为:", max_bonus_value) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值