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
我們思考一下這樣一個問題:
對於一行一段連續的區間,什麼情況下我們才能走這段區間的點呢?
看下面的圖~