A. Mafia
给你n个数,每次可以令n-1个数减去1,0的还是0,问最少要多少次使得所有数都为0。
思路:理想状态下每次总和是减去n-1的,这种状态的次数与最大的数
B. Apple Tree
给你一颗树,每个叶子节点上有一些苹果,现在要使得每个节点的不同子树的苹果数相等,问最少要去掉多少苹果才可以满足。可以把所有苹果去掉来满足。
思路:设cnt[ u ]表示u这颗子树总共有cnt[u]个苹果,redu[ u ]表示u这颗子树每次最少要减去redu[u]才可以使得子树满足平衡,很容易可以知道cnt[u] 一定是 redu[u]的倍数,对于u的每一个儿子to,要使得每个儿子to上的苹果数相等并且每次要减去的数也相等,所以就是对所有的redu[to]求lcm,意思就是u的每个儿子的子树每次至少要减去lcm,那么u这整颗树每次最少要减去lcm*儿子数,即redu[u] = lcm*儿子数。这里要注意判断lcm是否超过了某一个子树的cnt,如果超过了,那么不可行了,最后肯定是把所有苹果都去掉的,如果没超过,cnt[ u ]即等于 儿子数*(最小的cnt[to],并且是lcm的倍数)。
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
#define pb push_back
typedef __int64 ll;
const int maxn = 100000 + 5;
struct Edge {
int to, next;
}edge[maxn<<1];
int head[maxn], E;
ll cnt[maxn], redu[maxn];
void newedge(int u, int to) {
edge[E].to = to;
edge[E].next = head[u];
head[u] = E++;
}
void init(int n) {
for(int i = 1;i <= n; i++)
head[i] = -1;
E = 0;
}
ll gcd(ll a, ll b) {
return b ? gcd(b, a%b) : a;
}
ll lcm(ll a, ll b) {
return a/gcd(a, b)*b;
}
void dfs(int u, int pre) {
vector<int> son;
for(int i = head[u];i != -1;i = edge[i].next) {
int to = edge[i].to;
if(to == pre) continue;
dfs(to, u);
son.pb(to);
}
if(son.size() == 0) {
redu[u] = 1;
return ;
}
for(int i = 0;i < son.size(); i++) {
if(redu[son[i]] == 0) {
redu[u] = 0;
return ;
}
}
ll m = redu[son[0]];
for(int i = 0;i < son.size(); i++) {
m = lcm(m, redu[son[i]]);
}
for(int i = 0;i < son.size(); i++) {
if(m > cnt[son[i]]) {
redu[u] = 0;
return ;
}
}
ll cur = cnt[son[0]]/redu[son[0]];
ll now = cur/(m/redu[son[0]])*(m/redu[son[0]])*redu[son[0]];
for(int i = 1;i < son.size(); i++) {
cur = cnt[son[i]]/redu[son[i]];
now = min(now, cur/(m/redu[son[i]])*(m/redu[son[i]])*redu[son[i]]);
}
redu[u] = m*son.size();
cnt[u] = now*son.size();
// printf("m = %I64d u = %d cnt = %I64d redu = %I64d\n", m, u, cnt[u], redu[u]);
}
int main() {
int n, u, to;
scanf("%d", &n);
init(n);
ll tot = 0;
for(int i = 1;i <= n; i++)
scanf("%I64d", &cnt[i]), tot += cnt[i];
for(int i = 0;i < n-1; i++) {
scanf("%d%d", &u, &to);
newedge(u, to);
newedge(to, u);
}
dfs(1, -1);
printf("%I64d\n", tot - cnt[1]);
return 0;
}
D. Turtles
给你一个图,#表示不能走,现在有两只乌龟一起从(1, 1)走到 (n, m), (x, y)只能走到 (x+1, y)和(x, y+1),求两只乌龟路线的公共点只有(1,1)和(n,m)的情况数。
思路:
现在设A,B两只乌龟,令A从(1, 2)这个点出发,B从(2, 1)这个点出发,那么A最后肯定是到达(n-1,m),B肯定是到达(n, m-1)的。这样算肯定是包括所有合法路线的,但是还要把不合法路线去除掉,现在考虑每个不合法路线中间至少都会有一个交点,也就是说现在要计算的就是所有路线中至少有一个交点的情况数。
如果我令A从(1, 2)出发到达(n, m-1),B从(2, 1)出发到达(n-1, m),那么这两条路线必然有交点,实际上这个算的也就是所有的不合法情况数!因为对于这两条路线的最后一个交点,只需要把后面A,B的路线换一下,就成了第一种走法了。
#include <stdio.h>
#include <string.h>
typedef __int64 ll;
const int mod = 1000000007;
const int maxn = 3000 + 5;
char s[maxn][maxn];
ll dp[maxn][maxn];
ll getans(int x1, int y1, int x2, int y2) {
if(x2 < x1 || y2 < y1) return 0;
for(int i = x1;i <= x2; i++) {
for(int j = y1;j <= y2; j++) {
dp[i][j] = 0;
if(s[i][j] == '#') continue;
if(i == x1 && j == y1) dp[i][j] ++;
if(j > y1) dp[i][j] += dp[i][j-1];
if(i > x1) dp[i][j] += dp[i-1][j];
if(dp[i][j] >= mod) dp[i][j] -= mod;
}
}
return dp[x2][y2];
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1;i <= n; i++)
scanf("%s", s[i] + 1);
if(s[1][2] == '#' || s[2][1] == '#' || s[n-1][m] == '#' || s[n][m-1] == '#') return puts("0"), 0;
ll ans = getans(1, 2, n-1, m)*getans(2, 1, n, m-1)%mod - getans(1, 2, n, m-1)*getans(2, 1, n-1, m)%mod;
printf("%I64d\n", (ans%mod + mod)%mod);
return 0;
}