【HEOI2013】bzoj3168 钙铁锌硒维生素

银河队的营养师小林为了确保队员在宇宙旅行期间获得适当的营养,使用两套厨师机器人来准备食物。本文介绍了一种算法,用于确定两套机器人之间的备份关系,以确保即使一套机器人出现故障,也能满足队员的营养需求。

Description

银河队选手名单出来了!小林,作为特聘的营养师,将负责银河队选手参加
宇宙比赛的饮食。众所周知,前往宇宙的某个星球,通常要花费好长好长的时间,人体情况在这之间会发生变化,因此,需要根据每天的情况搭配伙食,来保证营养。小林把人体需要的营养分成了n种,这些营养包括但不限于铁,钙。他准备了2套厨师机器人,一套厨师机器人有n个,每个厨师机器人只会做一道菜,这道菜一斤能提供第i种营养xi微克。想要吃这道菜的时候,只要输入一个数,就能吃到对应数量的这道菜了。为防止摄入过量对身体造成的伤害,每个机器人还有防过量摄入药,只要输入一个数,就能生成一定剂量的药,吃了这些药,就能减少相当于食用对应数目的这道菜提供的营养。
小林之所以准备2套厨师机器人,正是因为旅途漫漫,难以预计,也许某一个厨师机器人在途中坏掉,要是影响了银河队选手的身体,就不好了。因此,第2套厨师机器人被用来做第1套的备用。小林需要为每一个第1套厨师机器人选一个第2套厨师机器人作备份,使得当这个机器人坏掉时,用备份顶替,整套厨师机器人仍然能搭配出任何营养需求,而且,每个第2套厨师机器人只能当一个第1套厨师机器人的备份。
Input

第一行包含一个正整数n。接下来n行,每行n个整数,表示第
1套厨师机器人做的菜每一斤提供的每种营养。再接下来n行,每行n个整数,表示第2套厨师机器人做的菜每一斤提供的每种营养。 Output

第一行是一个字符串,如果无法完成任务,输出“NIE”,否则输
出“TAK”,并跟着n行,第i行表示第i个第1套机器人的备份是哪一个第2套机器人。为了避免麻烦,如果有多种可能的答案,请给出字典序最小的那一组。

对于每一个bi求出他能替换哪些向量aj,然后就变成了字典序最小的二分图完备匹配问题。
把向量a组成矩阵A,然后求xA=bi,也就是bi=x1a1+x2a2++xnan。如果xj不为零,bi可以替换aj。把b拼成矩阵B,求出C=BA1就是二分图的邻接矩阵。
在二分图上先求出一个完备匹配,然后从小到大考虑每个位置连的边,选择一条最小的而且不会影响之前的匹配的路径进行更换。
原题数据貌似不保证矩阵A可逆,需要判断。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const int p=100000007;
int n,f[310],vis[310],ans[310],ok=1;
int pow(int base,int x)
{
    int ret=1;
    while (x)
    {
        if (x&1) ret=(LL)ret*base%p;
        base=(LL)base*base%p;
        x>>=1;
    }
    return ret;
}
struct mat
{
    int a[310][310];
    void rd()
    {
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                scanf("%d",&a[i][j]);
    }
    mat operator * (const mat &m) const
    {
        mat ret;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
            {
                ret.a[i][j]=0;
                for (int k=1;k<=n;k++)
                    ret.a[i][j]=(ret.a[i][j]+(LL)a[i][k]*m.a[k][j]%p)%p;
            }
        return ret;
    }
    mat inv()
    {
        mat ret;
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                ret.a[i][j]=(i==j);
        for (int i=1;i<=n;i++)
        {
            int x;
            x=-1;
            for (int j=i;j<=n;j++)
                if (a[j][i])
                {
                    x=j;
                    break;
                }
            if (x==-1)
            {
                ok=0;
                return ret;
            }
            if (x!=i)
                for (int j=1;j<=n;j++)
                {
                    swap(a[i][j],a[x][j]);
                    swap(ret.a[i][j],ret.a[x][j]);
                }
            x=pow(a[i][i],p-2);
            for (int j=1;j<=n;j++)
            {
                a[i][j]=(LL)a[i][j]*x%p;
                ret.a[i][j]=(LL)ret.a[i][j]*x%p;
            }
            for (int j=1;j<=n;j++)
                if (j!=i)
                {
                    x=a[j][i];
                    for (int k=1;k<=n;k++)
                    {
                        a[j][k]=(a[j][k]-(LL)a[i][k]*x%p+p)%p;
                        ret.a[j][k]=(ret.a[j][k]-(LL)ret.a[i][k]*x%p+p)%p;
                    }
                }
        }
        return ret;
    }
}a,b,c;
int dfs1(int u)
{
    for (int v=1;v<=n;v++)
        if (c.a[v][u]&&!vis[v])
        {
            vis[v]=1;
            if (!f[v]||dfs1(f[v]))
            {
                f[v]=u;
                return 1;
            }
        }
    return 0;
}
int dfs2(int u,int from)
{
    for (int v=1;v<=n;v++)
        if (c.a[v][u]&&!vis[v])
        {
            vis[v]=1;
            if (f[v]==from||(f[v]>from&&dfs2(f[v],from)))
            {
                f[v]=u;
                return 1;
            }
        }
    return 0;
}
int main()
{
    int cnt=0;
    scanf("%d",&n);
    a.rd();
    b.rd();
    c=b*a.inv();
    if (!ok)
    {
        printf("NIE\n");
        return 0;
    }
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=n;j++) vis[j]=0;
        if (dfs1(i)) cnt++;
    }
    if (cnt<n) printf("NIE\n");
    else
    {
        printf("TAK\n");
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=n;j++) vis[j]=0;
            dfs2(i,i);
        }
        for (int i=1;i<=n;i++) ans[f[i]]=i;
        for (int i=1;i<=n;i++) printf("%d\n",ans[i]);
    }
}
### HEOI2016 和 TJOI2016 竞赛中的树相关数据结构问题 #### 1. 树链剖分的应用 对于涉及树的数据结构问题,树链剖分是一种非常有效的技术。通过将树分解成若干条重路径和轻边,可以在 \(O(\log n)\) 的时间复杂度内处理树上的查询和更新操作[^1]。 ```cpp void dfs1(int u, int f, int d) { fa[u] = f; dep[u] = d; siz[u] = 1; son[u] = 0; for (auto v : G[u]) { if (v == f) continue; w[v] = ++tot; top[tot] = v; dfs1(v, u, d + 1); siz[u] += siz[v]; if (siz[v] > siz[son[u]]) son[u] = v; } } ``` 此代码片段展示了如何利用深度优先搜索(DFS)来初始化树的相关属性,如父节点、深度、子树大小等,这些信息是后续实现树链剖分的基础。 #### 2. 动态开点线段树优化 针对某些特定场景下的动态区间修改与查询需求,采用动态开点线段树能够有效降低空间消耗并提高效率。这种方法特别适用于值域较大而实际使用的范围较小的情况,在这类情况下静态分配内存可能导致浪费过多资源[^3]。 #### 3. 倍增算法求LCA 倍增法用于快速计算两点之间的最近公共祖先(Lowest Common Ancestor),其核心思想是在预处理阶段记录每个结点向上跳转\(2^i\)步后的父亲位置,从而使得每次查找的时间复杂度降为常数级别[^5]。 ```cpp for (int j = 1; j <= max_level; ++j) for (int i = 1; i <= n; ++i) dp[i][j] = dp[dp[i][j - 1]][j - 1]; // 查询u,v的lca while (dep[u] != dep[v]) { if (dep[u] < dep[v]) swap(u, v); for (int k = max_level; ~k; --k) if ((1 << k) & (dep[u] - dep[v])) u = dp[u][k]; } if (u == v) return u; for (int k = max_level; ~k; --k) if (dp[u][k] ^ dp[v][k]) u = dp[u][k], v = dp[v][k]; return dp[u][0]; ``` 这段代码实现了基于倍增原理的LCA查询功能,其中`max_level`表示最大可能跳跃次数,通常取值不超过20即可满足大多数情况的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值