1. 程式人生 > 其它 >2021“MINIEYE杯”中國大學生演算法設計超級聯賽 第四場題解

2021“MINIEYE杯”中國大學生演算法設計超級聯賽 第四場題解

2021“MINIEYE杯”中國大學生演算法設計超級聯賽 第四場題解

第一題傻瓜題卡了兩小時?

6986 Kanade Loves Maze Designing

題意:

給定一棵樹,樹上每個點的編號從1 - N,然後每個點有一個值。

現在要你從樹上每一個點出發,到其他點,然後帶入到該函式中求函式值......

有點繞,我們打個比方:

比如現在我們從第 i 個點出發,顯然輪到遍歷第 j 個點了,函式中 ai,j 代表的就是從i -> j我能拿去不同權值的數量

比如下面這樣一棵樹

其中:

1 -> 1

2 -> 1

3 -> 4

4 -> 5

5 -> 1

6 -> 4

以上是各個點的權值

那麼我們從1 -> 2的ai,j就是1,因為只能拿到1這個種類的數

但是我們從1 -> 4的ai,j就是2,因為我們能拿到1和5這兩個數

知道了這個ai,j是怎麼回事,下一步就是怎麼算?

由於是從一個點出發,我們需要遍歷整個圖,然後對於一棵樹我們可以知道如下性質:

每兩點之間的路徑有且只有一條(如果不重複經過一條邊的話)

那麼其實我們從一個節點出發,遍歷一遍這個樹,就能得到ai,j的值了

具體DFS程式碼如下:

點選檢視程式碼
void dfs(int cur,int fa)
{
    if(!cn[c[cur]])
    now[cur] = now[fa] + 1;
    else
    now[cur] = now[fa];
    
    cn[c[cur]] += 1;
    for(int i = head[cur];i;i = a[i].ne)
    {
        int to = a[i].to;
        if(to != fa)
        dfs(to,cur);
    }
    cn[c[cur]] -= 1;
}

簡單解釋一下:

從cur點出發,如果當前點的權值是第一次遍歷得到,那麼就相當於是在父親結點的a i , j基礎上+1

否則就是父親結點的值

然後記錄一下這個權值。

後面我們退出當前節點時需要將此權值回溯,也就是 - 1操作~

完整程式碼:

點選檢視程式碼
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int MAXN = 2000 + 7;
const int MOD1 = 1e9 + 7;
const int MOD2 = 1e9 + 9;
typedef long long ll;
struct node
{
    int ne,to;
    int w;
}a[MAXN<<2];
int head[MAXN],cnt;
void add(int x,int y,int w = 0)
{
    a[++cnt].ne = head[x];
    head[x] = cnt;
    a[cnt].to = y;
    a[cnt].w = w;
}
ll c[MAXN];
ll pw1[MAXN],pw2[MAXN];
void ini()
{
    pw1[0] = pw2[0] = 1;
    for(int i = 1;i < MAXN;i++)
    {
        pw1[i] = pw1[i - 1]*19560929%MOD1;
        pw2[i] = pw2[i - 1]*19560929%MOD2;
    }
}
ll now[MAXN];
ll cn[MAXN];
void dfs(int cur,int fa)
{
    if(!cn[c[cur]])
    now[cur] = now[fa] + 1;
    else
    now[cur] = now[fa];
    
    cn[c[cur]] += 1;
    for(int i = head[cur];i;i = a[i].ne)
    {
        int to = a[i].to;
        if(to != fa)
        dfs(to,cur);
    }
    cn[c[cur]] -= 1;
}
int main()
{
    int t;
    ini();
//    ll ans = 0;
//    int b[6] = {1,1,2,2,1,2};
//    for(int i = 0;i < 6;i++)
//    {
//        ans = ans + b[i]*pw1[i]%MOD1;
//        ans %= MOD1;
//    }
//    cout<<ans;
//    return 0;
    scanf("%d",&t);
    while(t--)
    {
        for(int i = 1;i <= cnt;i++)
        head[i] = 0;
        cnt = 0;
        
        int n;
        scanf("%d",&n);
        for(int i = 2;i <= n;++i)
        {
            int x;
            scanf("%d",&x);
            add(x,i);
            add(i,x);
        }
        for(int i = 1;i <= n;++i)    scanf("%lld",&c[i]);
        for(int i = 1;i <= n;++i)
        {
            now[i] = 0;
            dfs(i,i);
            ll ans1 = 0,ans2 = 0;
            for(int j = 1;j <= n;++j)
            {
                ans1 = ans1 + now[j]*pw1[j - 1]%MOD1;
                ans1 %= MOD1;
                ans2 = ans2 + now[j]*pw2[j - 1]%MOD2;
                ans2 %= MOD2;
            }
            printf("%lld %lld\n",ans1,ans2);
        }
    }
    return 0;
}

6992 Lawn of the Dead

兩場連續的比賽都有從1,1出發到N,M,而且每次都是往右或者往下走的這種題,還是不會,真彩

題意:

給你一個殭屍,開始在(1,1),只能往右走和往下走,然後有些地方有雷,問你殭屍可以訪問的節點數量

思路:

首先我們非常容易得到一個結論,那就是

第一行出現的第一個禁區之後的所有點我們都不能走到

嗯,然後就沒有然後了

後面的情況會有些複雜,dalao們覺得簡單當我沒說QAQ

我們思考一下這樣一個問題:

對於一行一段連續的區間,什麼情況下我們才能走這段區間的點呢?

看下面的圖~