CF939F Cutlet

好题。

我的思考方向:一段不能翻的区间,一定是烤同一方向。可以把原来的那个东西变成一堆类似

1 1 1 1 1 20 1 1 1 1 15 1 11 ...

的区间,求在其中取尽量少的区间个数,使得其和为n

。。然而个数依然是\(10^5\)个,没有实质性的突破。
然后就不会了


考虑朴素算法。可以想到f(i,j)为前i的时间正面烤了i的单位时间的翻转次数。
这样是从\(f(..,j-1)\to f(...,j)\)的。

这样太麻烦了,不妨设为背面烤了i。

那么转移会变简单。


我们发现这样转移,在没有不能执行操作的区间是直接赋值赋过去的。所以可以考虑变为\(f(i,j)\)为前i个可操作区间,背面时间为j。

可以发现一个可操作区间只会操作02次。多的几次操作都可以转化为02次且更优。

所以可以枚举一个当前区间给背面烤了k的时间,发现转移取min是有单调性的,可用单调队列。



#include<bits/stdc++.h>
using namespace std;
#define orz cout<<"lytcltcltcltcltcltcl"<<endl
inline int r(){int s=0,k=1;char c=getchar();while(!isdigit(c)){if(c=='-')k=-1;c=getchar();}while(isdigit(c)){s=s*10+c-'0';c=getchar();}return s*k;}
int n,k,f[105][100005];
deque<int>q;
deque<int>bh;
struct node
{
	int l,r;
}a[1000001];
bool cmp(node x,node y)
{
	return x.l<y.l;
}
int main()
{
	n=r();k=r();
	for(int i=1;i<=k;i++)
	{
		a[i].l=r();a[i].r=r();
	}
	memset(f,0x3f,sizeof(f));
	f[0][0]=0;
	sort(a+1,a+k+1,cmp);
	for(int i=1;i<=k;i++)//k段
	{
		for(int j=0;j<=n;j++)f[i][j]=f[i-1][j];//不翻
		while(!q.empty())q.pop_front(),bh.pop_front();
		for(int j=a[i].r;j>=0;j--)//背面烤了j s
		{
			while(!bh.empty()&&bh.front()<a[i].l-j)q.pop_front(),bh.pop_front();
			while(!bh.empty()&&q.back()>=f[i-1][a[i].r-j])q.pop_back(),bh.pop_back();
			q.push_back(f[i-1][a[i].r-j]);
			bh.push_back(a[i].r-j);
			f[i][j]=min(f[i][j],q.front()+1);
		}
		while(!q.empty())q.pop_front(),bh.pop_front();
		for(int j=0;j<=min(n,a[i].r);j++)
		{
			while(!bh.empty()&&bh.front()<j-a[i].r+a[i].l)q.pop_front(),bh.pop_front();
			while(!bh.empty()&&q.back()>=f[i-1][j])q.pop_back(),bh.pop_back();
			q.push_back(f[i-1][j]);
			bh.push_back(j);
			f[i][j]=min(f[i][j],q.front()+2);
		}
	}
	if(f[k][n]<1e9)cout<<"Full\n"<<f[k][n];
	else cout<<"Hungry";
}
上一篇:基于STC-B实验板设计一个小游戏“坦克大战”(小学期作业)


下一篇:AtCoder Beginner Contest 221 A~E题解