链接:https://ac.nowcoder.com/acm/problem/14524
来源:牛客网
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
有n种大佬,第i种大佬有ai个
珂朵莉想让最少个数的一种大佬的个数最多
你可以创造m个任意种类的大佬,并且可以把一些大佬变成另一些大佬
x -> y意味着可以把任意个x类型的大佬变成y类型的大佬
一个大佬可以被转换多次
对于每个y,最多有一个x使得x -> y成立
第一行两个数n,m
之后一行,第i个数xi表示第i种大佬可以被哪种大佬转换得到
如果xi为-1表示这种大佬不可以被任何大佬转换得到
之后一行,第i个数ai表示第i种大佬的个数
输出一行一个数表示答案 答案即 你要求让最少个数的一种大佬的个数最多的方案 输出这个方案下最少个数的一种大佬的个数
示例1
复制5 5 -1 1 1 1 1 4 5 1 3 2
5 5 -1 1 1 1 1 4 5 1 3 2
复制3
3
示例2
复制10 10 -1 1 1 2 1 5 5 6 10 5 6 1 7 1 7 1 10 5 1 1
10 10 -1 1 1 2 1 5 5 6 10 5 6 1 7 1 7 1 10 5 1 1
复制4
4
对于100%的数据,n <=1000000 , m , ai <= 1000000000
题目大意:
给你一个有向图,每个点都有一个初始权值,u连v代表u可以把自己一部分转换成v,你有M次机会可以使任意一个点的权值+1,问你最小权值的最大值是多少。
解法:
先缩点形成一个DAG图,然后二分每次逆拓扑dp一下,可惜这样会TLE、MLE。优化方法就是用链式前向星存图(对于1e6的点来说这样会快一些),新图由于只有一个儿子结点,单数组存即可,再预处理一下缩点后的拓扑序,感觉这道题的难点就是得想到预处理吧。
Accepted code
#pragma GCC optimize(3)
#include
#include
using namespace std;
#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define pir pair
#define MK(x, y) make_pair(x, y)
#define MEM(x, b) memset(x, b, sizeof(x))
#define MPY(x, b) memcpy(x, b, sizeof(x))
#define lowbit(x) ((x) & -(x))
#define P2(x) ((x) * (x))
typedef long long ll;
const int Mod = 1e9 + 7;
const int N = 1e6 + 100;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
inline ll dpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % Mod; b >>= 1; t = (t*t) % Mod; }return r; }
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t); b >>= 1; t = (t*t); }return r; }
struct Edge
{
int v, nxt;
}G[N];
int h[N], c;
ll a[N], dp[N], k;
ll sz[N], val[N];
void Add(int u, int v) {
G[++c] = { v, h[u] };
h[u] = c;
}
// 强连通
int dfn[N], low[N], cnt, scnt;
int stk[N], n, s;
int suo[N];
bool vis[N];
void Tarjan(int x) { // 缩点
dfn[x] = low[x] = ++cnt;
stk[++s] = x;
vis[x] = true;
for (int i = h[x]; i != -1; i = G[i].nxt) {
int v = G[i].v;
if (!dfn[v]) {
Tarjan(v);
Min(low[x], low[v]);
}
else if (vis[v])
Min(low[x], low[v]);
}
if (dfn[x] == low[x]) {
scnt++;
int k;
do {
k = stk[s--];
sz[scnt]++; // 大小
suo[k] = scnt; // 属于集合
val[scnt] += a[k]; // 集合权值
vis[k] = false;
} while (k != x);
}
}
// 拓扑序
int nxt[N], in[N];
int top[N], res;
void Init() {
for (int i = 1; i <= n; i++)
nxt[i] = h[i] = -1;
}
void Topsort() {
queue q;
for (int i = 1; i <= scnt; i++) {
if (!in[i])
q.push(i);
}
while (!q.empty()) {
int u = q.front();
q.pop();
top[++res] = u; // 拓扑序
if (nxt[u] != -1) {
int v = nxt[u];
in[v]--;
if (!in[v])
q.push(v);
}
}
}
bool Check(ll x) {
ll tot = k;
for (int i = 1; i <= scnt; i++) {
int u = top[i];
dp[u] += max(0ll, sz[u] * x - val[u]); // 需要的花费
if (val[u] > sz[u] * x) // 抵消花费
dp[u] -= min(dp[u], val[u] - sz[u] * x);
if (nxt[u] != -1)
dp[nxt[u]] += dp[u]; // 到终点,更新答案
else
tot -= dp[u];
dp[u] = 0;
}
return tot >= 0; // 足够就满足
}
int main()
{
cin >> n >> k;
Init();
for (int i = 1; i <= n; i++) {
int v;
sc("%d", &v);
if (v != -1)
Add(i, v);
}
for (int i = 1; i <= n; i++)
sc("%lld", &a[i]);
for (int i = 1; i <= n; i++)
if (!dfn[i])
Tarjan(i);
for (int i = 1; i <= n; i++) {
for (int j = h[i]; j != -1; j = G[j].nxt) {
int u = suo[i], v = suo[G[j].v];
if (u != v)
nxt[u] = v, in[v]++;
}
}
Topsort();
int l = 0, r = 1e9, ans = 1e9;
while (l <= r) {
ll mid = (l + r) >> 1;
if (Check(mid))
ans = mid, l = mid + 1;
else
r = mid - 1;
}
printf("%d\n", ans);
return 0; // 改数组大小!!!用pair记得改宏定义!!!
}