2023.2.2 日寄
距离放假还有 \(\underline{~1~}\) 天
2023.2.2 日寄
一言
\(~~~~\) “全国公民们,在三十五分钟后,我们的国家可能受到一次大规模核打击。加上第一批核弹头到达前所用的飞行时间,我们只有一个小时左右的时间了。我代表国家紧急状态委员会命令,立刻进行全国疏散。我国所有的核掩蔽部,只能容纳二亿人。也就是全国人口的十分之一。按照我们民族的传统,在灾难来临之际,总是把安全让给年岁最大的人,我相信现在大家也都想这样做。但如果这样的话,核浩劫之后, 留给我们的是一个只有二亿近二百岁老人的国家,每一个人都清楚这意味着什么!为了国家和民族的生存,我请求老人们,让孩子和年轻人进入掩蔽部吧!所有的孩子和年轻人,请你们走进掩蔽部!请你们活下来!!如果你们死去,我们的民族是真正死了;但如果你们活下来,我们的共和国,我们的民族,就会像一只涅槃的火凤凰,在烈焰中获得新生!我代表国家紧急状态委员会命令,各集团军,所有地方部队和武装警察部队,迅速占领核掩蔽部的各个入口,所有入口只准许孩子和年轻人进入,如果有大规模无视武装部队命令的行为,可以开枪射击。公民们,国家和民族的命运掌握在你们手里,我再次请求你们,共和国请求你们,救救孩子!”在夜色中,最高执政官的命令在被默默地,迅速地执行着。没有抱怨,没有骚乱。这个民族伟大的的牺牲精神在广阔的国土上显示出来。——刘慈欣《中国2185》
\(~~~~\) 今天的文段很长,但我想是值得的。
模拟赛
模拟费用流
「ICPC2018 WF」Conquer The World
题解
\(~~~~\) 把昨天讲的模型搬到了树上。
\(~~~~\) 还是类似地,我们从下往上开两个优先队列来匹配应占领的位置和有多余兵力的位置。那么在LCA处合并若干个子树的堆后找到最优的即可。
\(~~~~\) 同时为了让所有应该被占领的点都被占领,我们给这些点的dis加上 \(-\infty\),保证其能被优先匹配。
代码
查看代码
#include <bits/extc++.h>
#include <bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=f;
}
struct cmp{
bool operator()(const ll a,const ll b){return a>b;}
};
__gnu_pbds::priority_queue<ll,cmp> Q1[250005],Q2[250005];
vector <PII> G[250005];
ll x[250005],y[250005],dis[250005],dep[250005],tot,Ans;
const ll INF=1e12;
void dfs(ll u,ll fa)
{
if(x[u]>0)
for(ll i=1;i<=x[u];i++) Q1[u].push(dis[u]);
else
for(ll i=x[u];i<=-1;i++) Q2[u].push(dis[u]-INF),tot++;
for(ll i=0;i<G[u].size();i++)
{
ll v=G[u][i].first;
if(v==fa) continue;
dis[v]=dis[u]+G[u][i].second;
dfs(v,u);
Q1[u].join(Q1[v]); Q2[u].join(Q2[v]);
}
while(!Q1[u].empty()&&!Q2[u].empty()&&Q1[u].top()+Q2[u].top()-2*dis[u]<0)
{
// cerr<<Q1[u].top()<<" "<<Q2[u].top()<<endl;
ll a=Q1[u].top(),b=Q2[u].top();
Q1[u].pop(); Q2[u].pop(); Ans+=a+b-2*dis[u];
Q1[u].push(-b+dis[u]*2); Q2[u].push(-a+dis[u]*2);
}
// if(Q1[u].size()&&Q2[u].size()) cerr<<Q1[u].top()<<" "<<Q2[u].top()<<endl;
}
int main() {
ll n;read(n);
for(ll i=1,u,v,w;i<n;i++)
{
read(u);read(v);read(w);
G[u].push_back(mp(v,w));
G[v].push_back(mp(u,w));
}
for(ll i=1,y;i<=n;i++) read(x[i]),read(y),x[i]-=y;
dfs(1,0);
printf("%lld",Ans+1ll*tot*INF);
return 0;
}
/*
西风吹老洞庭波,一夜湘君白发多。
醉后不知天在水,满船清梦压星河。
*/
「NOI2017」 蔬菜
题解
\(~~~~\) 感谢 Syadouhayami 即使退役也还能在冥冥之中给予我启示。
\(~~~~\) 考虑倒推来做,那也就是从某天开始,每天会多一些蔬菜。那这样的话正向做的后效性就消失了,因为已经存在的蔬菜放在那里是不会跑掉的,也就是说每一天的决策也还是可以在之后再做。
\(~~~~\) 那这样的话就很明显每天选最大的 \(m\) 个就行了。开一个堆随便算算写写模拟即可。
\(~~~~\) 但是这题有多组询问,如果每个询问都做一次还是会爆炸。那我们考虑能不能从已知的答案去推未知的答案。考虑若第 \(i\) 天的答案是由 \(S\) 集合内的蔬菜组成的,那我们在第 \(i-1\) 天也即少了一天的情况下,只需要去除 \(S\) 内价值最小的 \(m\) 个蔬菜即可。这显然是可行的。
代码
查看代码
#include <bits/stdc++.h>
#define ll long long
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
x*=f;
}
struct cmp1{
bool operator()(const PII a,const PII b){return a.first<b.first;}
};
struct cmp2{
bool operator()(const PII a,const PII b){return a.first>b.first;}
};
priority_queue<PII,vector<PII>,cmp1>Q;
priority_queue<PII,vector<PII>,cmp2>Q2;
ll Ans[100005];
vector<ll>App[100005];
ll val[100005],c[100005],ex[100005],Cex[100005],New[100005],Use[100005];
inline ll Ceil(ll a,ll b){return (a+b-1)/b;}
int main() {
ll n,m,k;read(n);read(m);read(k);
for(ll i=1;i<=n;i++)
{
read(val[i]),read(ex[i]),read(c[i]),read(New[i]); Cex[i]=ex[i];
if(New[i]==0) App[100000].push_back(i);
else App[Ceil(c[i],New[i])].push_back(i);
}
for(ll i=100000;i>=1;i--)
{
vector <PII> PushAgain;
for(ll j=0;j<(ll)App[i].size();j++) Q.push(mp(val[App[i][j]]+ex[App[i][j]],App[i][j]));
ll Rest=m;
while(!Q.empty()&&Rest)
{
PII Tmp=Q.top();Q.pop();
ll Val=Tmp.first,id=Tmp.second;
if(ex[id])
{
ex[id]=0; Ans[100000]+=Val; Use[id]++; Rest--;
if(c[id]>1) Q.push(mp(val[id],id));
}
else
{
ll Num=min(Rest,c[id]-Use[id]-(i-1)*New[id]);
Ans[100000]+=1ll*Num*Val; Use[id]+=Num; Rest-=Num;
if(Use[id]!=c[id]) PushAgain.push_back(Tmp);
}
}
for(ll j=0;j<(ll)PushAgain.size();j++) Q.push(PushAgain[j]);
}
ll tot=0;
for(ll i=1;i<=n;i++)
{
if(Use[i]==1) Q2.push(mp(Cex[i]+val[i],i));
else if(Use[i]) Q2.push(mp(val[i],i));
tot+=Use[i];
}
for(ll i=100000-1;i>=1;i--)
{
// if(i==3)
// puts("???");
Ans[i]=Ans[i+1];
if(tot<=i*m) continue;
ll Throw=tot-i*m;
while(Throw&&!Q2.empty())
{
PII Tmp=Q2.top();Q2.pop();
ll Val=Tmp.first,id=Tmp.second;
ll Num=min(Use[id]-1,Throw);
if(Use[id]==1) Num=1;
Ans[i]-=Val*Num; Use[id]-=Num; Throw-=Num;
if(Use[id]==1) Q2.push(mp(Cex[id]+val[id],id));
else if(Use[id]) Q2.push(mp(val[id],id));
}
tot=m*i;
}
ll x;
while(k--) read(x),printf("%lld\n",Ans[x]);
return 0;
}
/*
西风吹老洞庭波,一夜湘君白发多。
醉后不知天在水,满船清梦压星河。
*/