zoukankan      html  css  js  c++  java
  • 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;
    }
    		
    		
    
    本欲起身离红尘,奈何影子落人间。
  • 相关阅读:
    vue axios前端跨域问题
    DDD的神奇
    开源电子书《WoW C#》
    15项架构原则
    龙芯发布.NET 6.0.100开发者测试版
    国内 Mono 相关文章汇总
    Kubernetes应用程序开发认证(CKAD) 经验分享
    [LeetCode] 1281. Subtract the Product and Sum of Digits of an Integer 整数的各位积和之差
    我(webabcd)的文章索引
    上周热点回顾(1.31.9)
  • 原文地址:https://www.cnblogs.com/jcgf/p/15245338.html
Copyright © 2011-2022 走看看