LuoguP4951 [USACO01OPEN]Earthquake
分析
假设我们最后选出的边集为\(S\),显然,奶牛们可以获得的单位时间钱数就是\(\frac{f-\Sigma c_i}{\Sigma t_i} (i\in S)\)。这样的式子简直就是在暗示01分数规划。
设最大答案为\(ans\),那么就有式子\(ans=\frac{f-\Sigma c_i}{\Sigma t_i} (i\in S)\),对这个式子做变形得到
\[ans*\Sigma t_i=f-\Sigma c_i (i\in S)
\]
进一步有
\[f=\Sigma c_i + ans*\Sigma t_i (i\in S)
\]
将这个式子看为\(f\)关于\(ans\)的函数。易知这个函数有单调性。于是我们可以二分枚举\(ans\)的取值,再通过这个式子判断是否符合题意。
那么本题的下一个关键点就是如何判断\(ans\)的取值合法。根据我们对\(ans\)的定义,对于任意的边集\(K\),都存在
\[\frac{f-\Sigma c_i}{\Sigma t_i}\leq ans (i\in K)
\]
变形后即为
\[f\leq \Sigma c_i + ans*\Sigma t_i (i\in K)
\]
这就是题目的限制条件。不难发现,不等式的右侧可以通过最小生成树解决。对于每一个\(ans\),将边权赋值为\(c_i+ans*t_i\),对整棵树进行最小生成树的流程。最后得出的树边权之和即为所求值。如果这个值小于\(f\),就说明\(ans\)的取值不合法。反之同理。
代码
/*
By Nero Claudius Caeser Augustus Germanicus,
Imeratorum Romanorum.
*/
#include <bits/stdc++.h>
using namespace std;
namespace StandardIO{
template<typename T>void read(T &x){
x=0;T f=1;char c=getchar();
for(; c<'0'||c>'9'; c=getchar()) if(c=='-') f=-1;
for(; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T>void write(T x){
if(x<0) putchar('-'),x*=-1;
if(x>=10) write(x/10);
putchar(x%10+'0');
}
} using namespace StandardIO;
namespace Project{
const int N=100000+100;
int n,m,block;
int a[N];
struct node{
int l,r,a,b,id;
} ask[N];
int l,r,res,cnt[N][2],c[N],ans[N][2];
inline bool cmp(const node x,const node y){
return (x.l/block!=y.l/block)?(x.l/block<y.l/block):(((x.l/block)&1)?x.r<y.r:x.r>y.r);
}
#define lowbit(x) x&(-x)
void update(int x,int v,int t){
for(; x<=n; x+=lowbit(x)) cnt[x][t]+=v;
}
int query(int x,int t){
int res=0;
for(; x; x-=lowbit(x)) res+=cnt[x][t];
return res;
}
void add(int x){
x=a[x],++c[x];
update(x,1,0);
if(c[x]==1) update(x,1,1);
}
void del(int x){
x=a[x],--c[x];
update(x,-1,0);
if(c[x]==0) update(x,-1,1);
}
void MAIN(){
read(n),read(m),block=sqrt(n);
for(int i=1; i<=n; ++i){
read(a[i]);
}
for(int i=1; i<=m; ++i){
read(ask[i].l),read(ask[i].r),read(ask[i].a),read(ask[i].b),ask[i].id=i;
}
sort(ask+1,ask+m+1,cmp);
l=1,r=0;
for(int i=1; i<=m; ++i){
while(l>ask[i].l) add(--l);
while(r<ask[i].r) add(++r);
while(l<ask[i].l) del(l++);
while(r>ask[i].r) del(r--);
ans[ask[i].id][0]=query(ask[i].b,0)-query(ask[i].a-1,0);
ans[ask[i].id][1]=query(ask[i].b,1)-query(ask[i].a-1,1);
}
for(int i=1; i<=m; ++i){
write(ans[i][0]),putchar(' '),write(ans[i][1]),puts("");
}
}
}
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
Project::MAIN();
}