CSP-S开小灶5
[贪心]
T1:题意:乌鸦喝水,n个罐子按顺序喝,第i个罐子喝wi的水,一开始深度hi,乌鸦只能喝到H<=X的罐子里的水,每成功喝一次水,每个罐子里水
深度+ wi,问乌鸦最多喝几次水。(hi,X<=2e9,wi<=200)
一开始没读对题目,以为只要乌鸦经过了罐子就会让水少,这样每个位置我可以明确知道到了他最后一次喝水应该是第几轮的第几次,直接每个乌鸦
枚举统计最多喝的次数就行。
但是乌鸦只有喝到了水才会减水,所以统计到这个乌鸦,统计前面有几只乌鸦已经喝了水所在轮数就很困难,考虑按照最多经历的轮数
从小到大枚举每个罐子, 算出它起作用的喝到水的次数段最多是多少(如果把喝水堪称n*m的矩阵,这个枚举位置是递增的)。如果pos后面有效罐子数量
<=pos位置可以喝的次数,那么这一轮一定可以喝完,所以直接跳到下一轮last=0,如果不行,找到余数,让last=它,i作废,继续找i+1,i的作用时段已经用tot
记录下来了。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
ll x=0,h=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
const int maxn=1e5+10;
int n,m,low[maxn];
ll xx;
struct DUCK
{
ll cnt,id;
inline bool operator<(const DUCK&H)const
{
return cnt<H.cnt;
}
}e[maxn];
ll tot,last;
#define lowbit(x) ((x)&(-x))
inline void addval(int x,int val)
{
while(x<=n)
{
low[x]+=val;x+=lowbit(x);
}
}
inline int queryval(int x)
{
int ans=0;
while(x)
{
ans+=low[x];
x-=lowbit(x);
}
return ans;
}
inline int lowerbound(int x)
{
int ans=n,l=last,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(queryval(mid)-queryval(last)<=x)
{
ans=mid;l=mid+1;
}
else r=mid-1;
}
return ans;
}
int main()
{
// freopen("1.in","r",stdin);
//freopen("a.out","w",stdout);
n=re(),m=re(),xx=re();
_f(i,1,n)
e[i].cnt=re();int sta=0;
_f(i,1,n)
{
ll vi=re();
e[i].cnt=ceill((double)(xx-e[i].cnt+1)/vi);
if(e[i].cnt>0)e[++sta]=e[i];
}
n=sta;
_f(i,1,n)
{
e[i].id=i;addval(i,1);
}
sort(e+1,e+1+n);int cir=0;
_f(i,1,n)
{
if(e[i].cnt<tot)
{
addval(e[i].id,-1);continue;
}
int ct;
while(cir<m&&(ct=queryval(n)-queryval(last))<=e[i].cnt-tot)
{
++cir;
tot+=ct;
last=0;
}
if(cir>=m)break;
int pos=lowerbound(e[i].cnt-tot);
tot=e[i].cnt;last=pos;
addval(e[i].id,-1);
}
chu("%lld",tot);
return 0;
}
/*
内存?
回撤?
2 4 5
2 10
6 1 4 8
*/
T2:题意:n个人,m个怪兽,一个点s,在一维坐标系,要求每个人找到一个怪兽配对,ansi=|posi-pos_monster|+|pos_monster-s|,ans=max(ansi)(1<=i<=n)
n<=5e3,m<=5e3,s<=1e9。
70tps:二分最大距离,二分图匹配每个人给怪兽。\(O(log(1e9)*n^2)\)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
ll x=0,h=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
const int N=2e5+10;
int n,m,s;
ll p[5000+100],q[5000+100];
ll mx,mi;
int head[10000+100],tot,mach[10000+100],vis[10000+100];
struct Node
{
int to,nxt;
}e[25000000+10];
inline void Add(int x,int y)
{
e[++tot].to=y;e[tot].nxt=head[x];head[x]=tot;
}
inline int dfs(int x)
{
for(rint i=head[x];i;i=e[i].nxt)
{
int to=e[i].to;
if(!vis[to])
{
vis[to]=1;
if((!mach[to])||dfs(mach[to]))
{
mach[to]=x;
return 1;
}
}
}
return 0;
}
inline bool check(ll dis)
{
_f(i,1,n+m)head[i]=mach[i]=0;tot=0;
_f(i,1,m)//枚举怪兽
{
ll lef=dis-(abs(q[i]-s));
if(n==m&&lef<0)return 0;
if(lef<0)continue;//怪兽可以没匹配(人有就行)
ll ldis=q[i]-lef,rdis=q[i]+lef;
int ls=lower_bound(p+1,p+1+n,ldis)-p;
int rs=upper_bound(p+1,p+1+n,rdis)-p-1;
if(ls>rs&&n==m)return 0;
if(ls>rs)continue;
_f(j,ls,rs)Add(j,i+n),Add(i+n,j);
}
_f(i,1,n)
{
_f(j,1,n+m)vis[j]=0;
if(!dfs(i))return 0;//一个没匹配都不行
}
return 1;
}
int main()
{
// freopen("1.in","r",stdin);
//freopen("a.out","w",stdout);
n=re(),m=re(),s=re();mx=max(mx,(ll)s);mi=min(mi,(ll)s);
_f(i,1,n)
{
p[i]=re();
mx=max(p[i],mx);
mi=min(p[i],mi);
}
_f(i,1,m)
{
q[i]=re();
mx=max(q[i],mx);
mi=min(q[i],mi);
}
sort(p+1,p+1+n);
ll l=0,r=(ll)2*(mx-mi+1);ll ans=0;
while(l<=r)
{
ll mid=(l+r)>>1;
if(check(mid))
{
r=mid-1;ans=mid;
}
else l=mid+1;
}
chu("%lld",ans);
return 0;
}
/*
内存?
回撤?
2 4 5
2 10
6 1 4 8
*/
100tps:
(1)发现贪心可行。如果让怪兽匹配给人,可以发现:怪兽在坐标轴上一定编号位置连续,不然一定有更优解。
可以感性理解吗。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define _f(i,a,b) for(register int i=a;i<=b;++i)
#define f_(i,a,b) for(register int i=a;i>=b;--i)
#define chu printf
#define ll long long
#define rint register int
#define ull unsigned long long
inline ll re()
{
ll x=0,h=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')h=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*h;
}
const int N=2e5+10;
int n,m,s;
ll p[5000+100],q[5000+100],dis[5000+10];
int main()
{
// freopen("1.in","r",stdin);
//freopen("a.out","w",stdout);
n=re(),m=re(),s=re();
_f(i,1,n)p[i]=re();
_f(i,1,m)q[i]=re();
sort(p+1,p+1+n);
sort(q+1,q+1+m);
_f(i,1,m)dis[i]=abs(s-q[i]);
ll mi=1e17;
for(int j=1;j<=m-n+1;++j)
{
ll tr=0;
for(int i=1;i<=n;++i)
{
tr=max(tr,dis[j+i-1]+abs(q[j+i-1]-p[i]));
}
mi=min(tr,mi);
}
chu("%lld",mi);
return 0;
}
/*
内存?
回撤?
2 4 5
2 10
6 1 4 8
*/
(2)dp[i][j]:选了前i个人,选了前1~j个怪兽的最短距离
dp[i][j]=min(dp[i][j],max(dp[i-1][j-1],abs(s-qj)+abs(pi-qj)))
然后这是代表j怪兽必须选,前缀传递优化
dp[i][j]=min(dp[i][j-1])似乎更靠谱,其实也是利用了人和怪兽不会交错匹配的性质。