题意:
n座城市,m条路,每个城市有一盏亮度为bi的灯,每次可以选择一个联通块让联通块上所有城市k(k的数量要尽可能大)的灯亮度减1,但灯亮度为0的城市不能算入联通块中,问让全部城市的灯亮度变为0的最小次数是多少。
题解:
(这题偷偷改过题面也没发通知==,加了k要尽量大这句话)
我们可以找某个联通块上亮度最小的城市,一直减直到该城市亮度为0,然后划分为更小的联通块,一直反复即可让所有城市的灯亮度为0。
反过来做就比较简单,先假设所有点都是孤立的,sum =
∑
i
=
1
n
b
i
\sum_{i=1}^n bi
∑i=1nbi,将所有点按照bi从大到小排序,建立一根有根树,枚举所有的城市i,遍历与 i 连接的城市 j ,如果 j 遍历过并且与i的树根父亲不同,那么合并 i 与 j,并将 j 的树根父亲赋值给 i 的树根父亲,由于 i 城市与 j 城市是互相联通并且 bi <= bj,在将 j 城市灯亮度变为0之前 i 城市灯亮度已经为0,所以 i 城市亮度计算重复,sum -= bi。(PS:并查集需要使用路径压缩)
AC_CODE:
const int maxn = 4e6+5;
struct node{
int value;
int index;
}k[maxn];
VI G[maxn];
bool vis[maxn];
int fa[maxn];
int get_father(int x) {
if(x == fa[x]) return x;
return fa[x] = get_father(fa[x]);
}
void merge(int x, int y) {
x = get_father(x);
y = get_father(y);
if(x != y) {
fa[x] = y;
}
}
void solve(){
int n,m;
R(n,m);
ll sum = 0;
for(int i=1; i<=n; ++i) {
R(k[i].value);
k[i].index = i;
G[i].clear();
vis[i] = false;
fa[i] = i;
sum += k[i].value;
}
sort(k+1,k+1+n,[](node x, node y){return x.value > y.value;});
for(int i=1; i<=m; ++i) {
int u,v;
R(u,v);
G[u].PB(v);
G[v].PB(u);
}
for(int i=1; i<=n; ++i) {
for(auto x: G[k[i].index]) {
if(vis[x]) {
int root = get_father(x);
if(root != get_father(k[i].index)) {
merge(x,k[i].index);
sum -= k[i].value;
}
}
}
vis[k[i].index] = true;
}
W(sum);
}
题意:
有n个工人和m个设备,每个工人只能使用一台设备,一台设备最多只能给一个人使用,每个工人有a,b,c三个属性,工人选择第i台设备,要花费a × i2 + b × i + c的费用,输出n个数,表示i个工人选择i台设备的最少费用。n<=50<=m<=1e8。
题解:
a × i2 + b × i + c是个开口向上的二次函数,并且最多只有50台设备分配给所有工人,所以我们只要考虑二次函数对称轴左右各55个点即可。考虑以下建图方式,建立一个超级源点S和一个超级汇点T,源点与所有工人建立n条容量为1,费用为0的边,所有工人与对称轴左右50个点的机器建立110条容量为1,费用为a × i2 + b × i + c的边,所有与工人连接的机器与汇点建立容量为1,费用为0的边,跑费用流即可。根据最小费用流算法原理和增广路定理,进行i次增广时,产生的费用就是i个工人选择i台设备的最少费用。PS:由于题目中m,b的值都很大,所以需要离散化。
AC_CODE:
const int maxn = 1e5+5;
const ll INF = 1LL<<61LL;
bool vis[maxn];
int n, m, s, t;
int u, v, c, w;
ll cost[maxn];
int pre[maxn], last[maxn], flow[maxn];
ll maxFlow, minCost;
struct Edge
{
int from, to, flow;
ll cost;
}edge[maxn];
int head[maxn], num_edge;
int ed[maxn];
queue <int> q;
void addedge(int from, int to, int flow, ll cost)
{
edge[++num_edge].from = head[from];
edge[num_edge].to = to;
edge[num_edge].flow = flow;
edge[num_edge].cost = cost;
head[from] = num_edge;
edge[++num_edge].from = head[to];
edge[num_edge].to = from;
edge[num_edge].flow = 0;
edge[num_edge].cost = -cost;
head[to] = num_edge;
}
bool SPFA(int s, int t)
{
// memset(cost, 0x7f, sizeof(cost));
memset(flow, 0x7f, sizeof(flow));
for(int i=0; i<maxn; ++i) cost[i] = INF;
memset(vis, 0, sizeof(vis));
q.push(s); vis[s] = 1; cost[s] = 0; pre[t] = -1;
while (!q.empty())
{
int now = q.front();
q.pop();
vis[now] = 0;
for (int i = head[now]; i != -1; i = edge[i].from)
{
if (edge[i].flow>0 && cost[edge[i].to]>cost[now] + edge[i].cost)
{
cost[edge[i].to] = cost[now] + edge[i].cost;
pre[edge[i].to] = now;
last[edge[i].to] = i;
flow[edge[i].to] = min(flow[now], edge[i].flow);
if (!vis[edge[i].to])
{
vis[edge[i].to] = 1;
q.push(edge[i].to);
}
}
}
}
return pre[t] != -1;
}
void MCMF()
{
maxFlow = 0;
minCost = 0;
bool ok = 0;
while (SPFA(s, t))
{
if(ok) printf(" ");
ok = 1;
int now = t;
maxFlow += flow[t];
minCost += flow[t] * cost[t];
while (now != s)
{
edge[last[now]].flow -= flow[t];
edge[last[now] ^ 1].flow += flow[t];
now = pre[now];
}
printf("%lld", minCost);
}
puts("");
}
ll aa[maxn],bb[maxn],cc[maxn];
struct node2{
int idx;
ll value;
}k[maxn];
void solve(){
memset(head, -1, sizeof(head)); num_edge = -1;//初始化
memset(ed, 0, sizeof(ed));
R(n,m);
for(int i=1; i<=n; ++i) {
R(aa[i],bb[i],cc[i]);
}
int S = 1e5-2, T = 1e5-1;
for(int i=1; i<=n; ++i) {
addedge(S,i,1,0);
}
s = S; t = T;
int idx = n+1;
map<int,int>mp;
for(int i=1; i<=n; ++i) {
ll u = -bb[i]/(2*aa[i]);
int cnt = 1;
for(int j=max(1LL,u-55); cnt<=110&&j<=m; cnt++,j++) {
k[cnt].idx = j;
k[cnt].value = aa[i] * j * j + bb[i] * j + cc[i];
}
sort(k+1,k+cnt,[&](node2 x, node2 y){return x.value < y.value;});
for(int j=1; j<cnt; ++j) {
if(!mp[n+k[j].idx]) {
mp[n+k[j].idx] = idx++;
}
addedge(i,mp[n+k[j].idx],1,k[j].value);
}
}
for(auto x: mp) {
addedge(x.S, T, 1, 0);
}
MCMF();
}
题意:
设Fi为斐波那契数列,bi为非0即1的数组,对于任意一个正整数x,都有以下性质:
b1×F1 + b2×F2 + ⋯ + bn×Fn = x.
bn = 1.
bi * bi+1 = 0.(即不存在bi=1并且bi+1=1)
现在给出三个数a,b,c,这三个数由b1×F1 + b2×F2 + ⋯ + bn×Fn = x表示,并且满足c=a*b,现在将c某一项bi=1修改为0,询问i是多少。
题解:
思路是计算出a×b-c的值,然后判断与哪一项斐波那契数列的值相等。
由于这题给定的数据范围为1e6,所以我们必须求出0到1e6中所有斐波那契数列的值,但在计算中间会爆long long,所以需要用到哈希,取一个尽可能大的模数,在这道题里面一些比较常用的大质数(如998244353,1e9+7)都不能用,因为得换一个更大的模数,因为使用到大模数,会导致a×b爆long long,所以需要使用快速乘。
AC_CODE:
const ll mod = 5000000002453;
ll qmul(ll a,ll b){ ll r=0; while(b){ if(b&1) r=(r+a)%mod; a=(a+a)%mod; b>>=1; } return r;}
const int maxn = 4e6+5;
ll f[maxn];
/*
struct node{
}k[maxn];
*/
void init() {
f[0] = 1LL;
f[1] = 1LL;
f[2] = 2LL;
for(int i=3; i<=maxn-1; ++i) f[i] = (f[i-1] + f[i-2]) % mod;
}
void solve(){
int n;
R(n);
ll a(0),b(0),c(0);
for(int i=1; i<=n; ++i) {
int k; R(k);
if(k) a = (a + f[i]) % mod;
}
R(n);
for(int i=1; i<=n; ++i) {
int k; R(k);
if(k) b = (b + f[i]) % mod;
}
R(n);
for(int i=1; i<=n; ++i) {
int k; R(k);
if(k) c = (c + f[i]) % mod;
}
ll d = (qmul(a,b) % mod - c + mod) % mod;
// assert(d > 0);
// debug(a,b,c,d);
FOR(i,1,maxn-1) {
if(d % mod == f[i]) {
W(i);
return;
}
}
}