2021.08.28 膜你赛

2021.08.28 膜你赛

lock

Description

LYK 想把你的智商锁起来,它出了道神题。
它有一壶水,这壶水的水量在[L,R]中,你只知道 L 和 R 是多少,但你并不知道具体的值是多少。

它有两个容量为无穷大的杯子。它想尽可能把壶里的水均匀的分在两个杯子中。它觉得
这个任务可能太难了,因此它只要求最终两个杯子的水的容量至多相差 1。并且它也不要求
把壶里的水全部倒完,只要求最终剩在壶里的水不超过 1。

LYK 在倒水过程中,不知道这壶水还有多少水,但能知道是否倒完了。

一个最笨的方法是左边倒 1 滴,右边的倒 1 滴,左边再倒 1 滴,这样依次下去,直到壶
里没水或者已经倒了 R-1 的水时,就满足条件了。

但根据题名,这题并不简单。

LYK 想知道如果采用最优策略,则在最坏情况下 LYK 要倒多少次水才能满足条件。

(注:最优策略下的最坏情况指,事件总是往最坏的情况发展,例如 L=2,R=10000,你
第一次就大胆的往第一杯水倒 2 滴水,但可能壶里一开始总共就只有 2 的水,就不满足条件
了。所以你的方案应当是就算情况再坏,也能满足条件,并且在此基础上倒水的次数最少)

Solution

手模样例,大力找规律,发现如果固定 \(r\)\(l\)​ 递减的话,答案也会减小,几乎是每隔两个数答案+1,因此直接推出式子,Ans=(r-l+2)/2 ,然后还要特判几种情况,然后有一种情况没有判,但是数据没有这种情况,然后就过了。

Code

/*
* @Author: smyslenny
* @Date:    2021.08.
* @Title:   
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>

#define int long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)

using namespace std;
const int mod=998244353;
const int M=1e3+5;
int l,r;
int read()
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
namespace substack1{
	
	int mp[6][6]={{0,0,0,0,0,0},
				  {0,0,1,2,2,3},
				  {0,0,1,2,2,2},
				  {0,0,0,2,2,2},
				  {0,0,0,0,2,2},
				  {0,0,0,0,0,2}};
	void main()
	{
		printf("%lld\n",mp[l][r]);
	}
}

namespace substack2{
	int Ans;
	void main()
	{
		Ans=2;
		if(l>r) printf("0\n");
		else if(r<=1) printf("0\n");
		else if(r<=2)
			printf("1\n");
		else if(l==r)
			printf("2\n");
		else
		{	
			printf("%lld\n",max(2ll,(r-l+2ll)/2ll));
		}
	}
}
signed main()
{	
//	freopen("lock.in","r",stdin);
//	freopen("lock.out","w",stdout);
	l=read(),r=read();
	if(r<=5) substack1::main();
	else substack2::main();
	return 0;
}

sequence

Description

LYK 有一个序列,它想玩一个游戏,就是找一段最长的连续的数字,使得它们的和小于等于 p,我们假设这个最长的区间的长度为 L。

但这件事好像有点简单,因此 LYK 给自己一次机会:将其中一段连续的长度不超过 d 的区间的数都变成 0,之后再找最长的连续的数字使得和小于等于 p。

LYK 想知道,在所有的可能的改变策略中,L 最大为多少。

Solution

预处理出所有 d 区间的答案,然后用单调队列维护 d 的最大答案,双指针求最大区间。

Code

/*
* @Author: smyslenny
* @Date:    2021.08.
* @Title:   
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>

#define int long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)

using namespace std;
const int mod=998244353;
const int M=1e6+5;
int n,p,d,l,Ans,Div[M];
int read()// 
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
int Max,js,mp[M],sum[M],q[M],head=1,tail;
signed main()
{
//	freopen("sequence.in","r",stdin);
//	freopen("sequence.out","w",stdout); 
	n=read(),p=read(),d=read();
	for(int i=1;i<=n;i++)
		mp[i]=read(),sum[i]=sum[i-1]+mp[i];
	for(int i=1;i<=n;i++) 
		Div[i]=sum[i]-sum[max(0ll,i-d)];//当前位置到 d 的和 
	for(int r=1;r<=n;r++)
	{
		while(Div[q[tail]]<=Div[r] && head<=tail) tail--;//当前点往前比当前点 d 的值要小的最远距离 
		q[++tail]=r;
		while(sum[r]-sum[l]-Div[q[head]]>p && l<r)//删去之后找能到达的最远的位置 
		{
			l++;//所能到达的最远左端点 
			while(head<=tail && q[head] <l+d) head++;//d 区间断向左挪 
		}
		if(sum[r]-sum[l]-Div[q[head]]<=p) 
			Ans=max(Ans,r-l);
	}
	printf("%lld\n",Ans);
	return 0;
}

fight

Description

\(LYK\)\(n\) 个小朋友排成一排。第 \(i\) 个小朋友的战斗力是 \(a_i\),且他们的战斗力互不相同。
战斗力高的会打败战斗力低的。
\(LYK\) 想恶搞这些小朋友们,具体地,它有 \(k\) 次操作。
\(i\) 次操作会有两个参数 \(l_i\)\(r_i\),表示如果两个小朋友 \(A\)\(B\) 的战斗力均在 \([l_i,r_i]\)
段区间中,它们的打架结果会相反。即如果一开始 \(A\) 能赢 \(B\),则现在变成 \(B\) 能赢 \(A\)。当然它
们的打架结果可能在后来的操作中又被反过来。
\(LYK\) 想知道,\(k\) 次操作后,存在多少三元组 \((a,b,c)\),其中 \(a\) 能赢 \(b\)\(b\) 能赢 \(c\)\(c\) 能赢 \(a\)
注意这里 \((a,b,c),(b,c,a),(c,a,b)\) 算同一种三元组。

Solution

先离散化,答案是用 C(n,3)-不合法的;
不合法的三元组必定有一个可以战胜其他两个,那么只要统计出他能打败的人,再C(cnt,2),再求一下和。线段树维护修改查询操作,又发现对于i来说只有覆盖i的修改才对i有用,所以对于所有操作只要扫两遍即可

Code

/*
* @Author: smyslenny
* @Date:    2021.08.
* @Title:   
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>

#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
#define lson(x) (x)<<1
#define rson(x) (x)<<1|1 

using namespace std;
const int mod=998244353;
const int M=1e3+5;
int n,k,mp[M],Ans;
pair<int,int> a[M],b[M];
int read()
{
	int x=0,y=1;
	char c=getchar();
	while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
	while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
struct tr
{
	int l,r,mid,len,sum,laz;
}tree[M<<2];
void buildtree(int p,int l,int r)
{
	tree[p].l=r,tree[p].r=r,tree[p].mid=(l+r)>>1,tree[p].len=r-l+1;
	if(l==r) return;
	int mid=(l+r)>>1;
	buildtree(lson(p),l,mid);
	buildtree(rson(p),mid+1,r);
}
void push_down(int p)
{
	tree[lson(p)].sum=tree[lson(p)].len-tree[lson(p)].sum;
	tree[rson(p)].sum=tree[rson(p)].len-tree[rson(p)].sum;
	tree[lson(p)].laz^=1;tree[rson(p)].laz^=1;
	tree[p].laz=0;
}
void update(int p,int l,int r)
{
	if(l<=tree[p].l && tree[p].r<=r)
	{
		tree[p].sum=tree[p].len-tree[p].sum;
		tree[p].laz^=1;
		return;
	}
	if(tree[p].laz) push_down(p);
	if(l<=tree[p].mid) update(lson(p),l,tree[p].mid);
	if(r>=tree[p].mid) update(rson(p),tree[p].mid+1,r);
	tree[p].sum=tree[lson(p)].sum+tree[rson(p)].sum;
}
int query(int p,int l,int r,int op)
{
	if(l>r) return 0;
	if(l<=tree[p].l && r>=tree[p].r) return op?tree[p].sum:tree[p].len-tree[p].sum;
	if(tree[p].laz) push_down(p);
	if(l<=tree[p].mid)
		if(r<tree[p].mid) return query(lson(p),l,r,op)+query(rson(p),l,r,op);
		else return query(lson(p),l,r,op);
	else return query(rson(p),l,r,op);
}

int main()
{
	n=read(),k=read();
	for(int i=1;i<=n;i++) mp[i]=read();sort(mp+1,mp+1+n);
	sort(mp+1,mp+1+n);
	buildtree(1,1,n); 
	Ans=n*(n-1)*(n-2)/6;
	int x=unique(mp+1,mp+1+n)-mp-1;
	for(int i=1;i<=k;i++)
	{
		int l=read(),r=read();
		l=lower_bound(mp+1,mp+1+x,l)-mp;
		r=upper_bound(mp+1,mp+1+x,r)-mp-1;
		a[i]=make_pair(l,r),b[i]=make_pair(r,l);
	}
	for(int i=1,j=1,y=1;i<=n;i++)
	{
		while(j<=y && a[j].first==i) 
			update(1,a[j].first,a[j].second),++j;
		int x=query(1,1,i-1,0)+query(1,i+1,n,1);
		Ans-=x*(x-1)/2;
		while(y<=k&&b[y].first==i) 
			update(1,b[y].second,b[y].first),++k;
	}
	printf("%d\n",Ans);
	return 0;
}
		
		
本欲起身离红尘,奈何影子落人间。
原文地址:https://www.cnblogs.com/jcgf/p/15245338.html