首页 > 其他分享 >周末题目选解

周末题目选解

时间:2022-11-09 00:55:47浏览次数:55  
标签:now 题目 int sum 周末 flag last 选解 include

2022.11.6

洛谷 P2357 守墓人

题面传送门

为什么选这个?

练习树状数组,线段树(主要是树状数组),这个在之后的阶段会经常用到,是一种常见的数据结构。

题目解释

数据结构裸题。

有一个长度为$n$的数组,有几个操作,需要对某个区间加减操作,对某个区间求和。

由于算法思想就是模板,可以多看看树状数组 / 线段树的题解。

树状数组

Code:

#include<bits/stdc++.h>
#define int long long
#define lowbit(x) x&-x 
using namespace std;
const int N=5e5+10;
int n,m,last,opt,x,y,z,mian;
int sum1[N],sum2[N];
void add(int pos,int x)
{
    for(int i=pos;i<=n;i+=lowbit(i))
        sum1[i]+=x,sum2[i]+=pos*x;
}
long long query(int pos)
{
    long long res=0;
    for(int i=pos;i;i-=lowbit(i))
        res+=(pos+1)*sum1[i]-sum2[i];
    return res;
} 
signed main()
{
    cin >> n >> m;
    for(int i=1;i<=n;i++)
    cin >> x,add(i,x-last),last=x;
    for(int i=1,opt;i<=m;i++)
    {
        cin >> opt;
        switch(opt)
        {
            case 1:cin>>x>>y>>z,add(x,z),add(y+1,-z);break;
            case 2:cin>>z,mian+=z;break;
            case 3:cin>>z,mian-=z;break;
            case 4:cin>>x,cin>>y;printf("%lld\n",query(y)-query(x-1)+(x==1)*mian);break;
            case 5:printf("%lld\n",query(1)+mian);
        }
    }
}
树状数组

线段树

Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
struct node
{
    ll sum,lazy;
}tre[808666];
ll a[200001],n,f,l,r,k;
void build(ll l,ll r,ll now)
{
    if(l==r)
    {
        tre[now].sum=a[l];
        tre[now].lazy=0;
        return;
    }
    ll mid=(l+r)>>1;
    ll lson=now<<1;
    ll rson=lson|1;
    build(l,mid,lson);
    mid++;
    build(mid,r,rson);
    tre[now].lazy=0;
    tre[now].sum=tre[lson].sum+tre[rson].sum;
}
void down(ll l,ll r,ll now)
{
    if(tre[now].lazy)
    {
        ll k=tre[now].lazy;tre[now].lazy=0;
        ll mid=(l+r)>>1,lson=now<<1,rson=lson|1;
        tre[lson].lazy+=k;tre[rson].lazy+=k;
        tre[lson].sum+=(mid-l+1)*k;
        mid++;
        tre[rson].sum+=(r-mid+1)*k;
    }
}
void add(ll u,ll v,ll l,ll r,ll now,ll it)
{
    if(u<=l&&v>=r)
    {
        tre[now].lazy+=it;
        tre[now].sum+=(r-l+1)*it;
        return;
    }
    if(u>r||v<l) return;
    ll mid=(l+r)>>1,lson=now<<1,rson=lson|1;
    down(l,r,now);
    add(u,min(v,mid),l,mid,lson,it);
    mid++;
    add(max(u,mid),v,mid,r,rson,it);
    tre[now].sum=tre[lson].sum+tre[rson].sum;
}
ll ask(ll u,ll v,ll l,ll r,ll now)
{
    if(u<=l&&v>=r)
    {
        return tre[now].sum;
    }
    if(u>r||v<l) return 0;
    ll mid=(l+r)>>1,lson=now<<1,rson=lson|1;
    down(l,r,now);
    ll re=0;
    re+=ask(u,min(v,mid),l,mid,lson);
    mid++;
    re+=ask(max(u,mid),v,mid,r,rson);
    tre[now].sum=tre[lson].sum+tre[rson].sum;
    return re;
}
int main()
{
    scanf("%lld%lld",&n,&f);
    for(ll i=1;i<=n;++i) scanf("%lld",&a[i]);
    build(1,n,1);
    for(ll i=1;i<=f;++i)
    {
        ll opt;
        scanf("%lld",&opt);
        if(opt == 1)
        {
            scanf("%lld%lld%lld",&l,&r,&k);
            add(l,r,1,n,1,k);
        }
        if(opt == 2)
        {
            scanf("%lld",&k);
            add(1,1,1,n,1,k);
        }
        if(opt == 3)
        {
            scanf("%d",&k);
            add(1,1,1,n,1,-k);
        }
        if(opt == 4)
        {
            scanf("%lld%lld",&l,&r);
            printf("%lld\n",ask(l,r,1,n,1));
        }
        if(opt == 5)
        {
            printf("%lld\n",ask(1,1,1,n,1));
        }
    }
    return 0;
}
线段树

 


Codeforces #829 Make Nonzero Sum (version easy / hard)

题面传送门

 

为什么选这个?

是一个练习思维的一道题,涉及算法的知识很少,相比更考验对于题目的理解,以及“贪心”的策略选择;

此外应该还涉及到构造了吧(((

 

题目解释

给定了一个只有$[-1,0,1]$的数构成的数组,选取其不重不漏的子区间集合$s_i=[l_i,r_i]$使得这些子区间集$s_i$的总和为0。不要求子区间集的数量最小化。

 

  • $s_i = a[l_i] - a[l_{i+1}]+…(+ / -)a[r_i]$
  • $r[i] +1 == l[i+1], 1\leq i < n$
  • $l[1]=1, r[k]=n$

 

即每个数组的值均对应在某个子区间上,而每个子区间内的计算顺序是加减交替的。

如果不存在,输出$-1$,存在则输出一种可能的集合组成方案 

浅析

1. 可以把(非零的)数目个数总数分为奇数偶数来看,显然地有奇数个时,该式子无法求解,直接返回-1;

2. 对于一个数量大于等于3的集合,我们均可以有$a-b+c...=(a+b)+c$,所以对于大于3之上的部分我们是可以去改变组合的,我们只需要考虑的是数目小于等于2的一种情况;

3. 我们允许$[x,x]$形式的存在;

4.  每一组都凑成0,那么最后的结果就为0;

5. 对于$easy version$,只用考虑$[-1,1]$的情况,对于$hard version$,则需要考虑$[-1,0,1]$的情况,对于$easy version$我们只需要连续两个数凑成$0$即可,对于$hard version$,我们可以看每一个区域$[x_1,x_2]$的第二个数$x_2$其前面是否为$0$:如果为0,则令该$0$与其一同出现,即为$[x_1,x_1],[0,x_2]$;否则还是跟着前面那个一起出来就行。

 

Code:

为了拓展大家的视野,提供了两种编码习惯的代码,大家可以自行参考。

参考1
#include <bits/stdc++.h>
#define int long long
using namespace std;

int T;
const int L = 5e5 + 5;
int t, n, k, x, y, z, ans, cnt, a[L], flag[L];

signed main() {
	cin >> T;
	while (T--) {
		int sum = 0;
		cin >> n;
		vector<int> po;
		memset(flag, 0, sizeof(flag));
		for (int i = 1; i <= n; i++) {
			cin >> a[i];
			sum += a[i];
		}
		if (sum % 2) {
			cout << "-1" << endl;
			continue;
		}
		sum /= 2;
		for (int i = 2; i <= n; i++) {
			if (a[i] == 1 && sum > 0 && !flag[i - 1]) {
				flag[i] = true;
				po.push_back(i);
				sum--;
			}
			if (a[i] == -1 && sum < 0 && !flag[i - 1]) {
				flag[i] = true;
				po.push_back(i);
				sum++;
			}
			if (sum == 0) {
				break;
			}
		}
		if (sum != 0) {
			cout << "-1" << endl;
			break;
		}
		ans = 0;
		for (int i = 1; i <= n; i++) {
			if (flag[i + 1]) {
				continue;
			}
			ans++;
		}
		if (sum == 0) {
			cout << ans << endl;
			for (int i = 1; i <= n; i++) {
				if (flag[i + 1]) {
					continue;
				}
				if (flag[i]) {
					cout << i - 1 << " " << i << endl;
				} else {
					cout << i << " " << i << endl;
				}
			}
		}
	}
	return 0;
}
参考代码2
 #include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2e6 + 10;
int n,num,x,t,flag;
int a[N],b[N],last;
int main()
{ 
	 cin >> t;
	 while(t--)
	 {
		  cin >> n;
		  x=0; num=0; last=0;
		  for(int i=1;i<=n;i++)
		  {
			   b[i]=0;
			   cin >> a[i];
			   if(a[i]==0)
			   {
				    x++;
				    b[i]=1;
				    continue;
			   }
			   num++;
			   if(last != 0)
			   {
				    if(last!=a[i])
				    {
					     b[i] = 1;
					     x++;
				    }
				    last=0;
			   }
			   else
			   {
				    last=a[i];
				    b[i]=1;
				    x++;
			   }
		  }
		  if(num % 2 == 1)
		   printf("-1\n");
		  else 
		  {
			   flag = 0;
			   printf("%d\n",x);
			   for(int i=1;i<=n;i++)
			   {
				    if(b[i])
				    {
					     if(flag)
					     printf("%d\n%d ",i-1,i);
					     else
					     {
						      printf("%d ",i);
						      flag = 1;
					     }
				    }
			   }
			   printf("%d\n",n);
		  }
	 } 
 	return 0; 
}

标签:now,题目,int,sum,周末,flag,last,选解,include
From: https://www.cnblogs.com/lyp-Bird/p/16870471.html

相关文章

  • 第一次实验题目
    1.题目:最大连续子数组和(最大子段和)问题:给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数......
  • 2022NewStarCTF新生赛一些比较有意思的题目wp
    Misc_蚁剑流量分析Pcap的文件可以直接使用工具 编辑器打开目录,一个一个看,可以找到eval危险函数 看到n3wst4r,直接使用linux正则匹配,找出相关内容Url解码,了解一下蚁......
  • 手把手教你搭建消防安全答题小程序-将用云开发获取到的题目渲染到答题页面
    手把手教你搭建答题活动小程序系列文章,第一阶段为界面设计篇,分别描写了如何搭建答题小程序界面。现在已经进入第二阶段,功能交互篇。而上一篇文章描写了,如何用云开发实现查......
  • leetcode(34)优先队列系列题目
    218.天际线问题用SortedList存边界,每次删除或加入边界判断最高点是否变化classSolution:defgetSkyline(self,buildings:List[List[int]])->List[List[int]]:......
  • 题目要求
    请回顾和复习课堂讲授的单元测试部分内容。完成下面题目之一。题目一:最大连续子数组和(最大子段和)背景问题:给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求......
  • 10.31-11.4 周末总结
    目录一、ATM项目二、编程思想1.面向过程2.面向对象三、对象与类四、类与对象的创建1.类的语法结构2.类的定义与调用1.定义类2.查看名称空间的方法1__dict__方法2点号运算......
  • leetcode(33)图系列题目
    1615.最大网络秩直接模拟即可classSolution:defmaximalNetworkRank(self,n:int,roads:List[List[int]])->int:adjs=defaultdict(set)#注意......
  • PTA甲级题目分类
    题目考察点A1001A+BFormat数字相加格式化输出 简单模拟A1002A+BforPolynomials多项式相加 简单模拟A1003Emergency救援最短路径和最大救援部队 Dijkstra算法A100......
  • 【杂题汇总】NOIP 2022 杂题目录
    这里单纯的是一些题目,看到有意思的题会在这里记下来,也可以当做Todolist啦解析的话在这里[ARC147E]Examination[CF573E]BearandBowling[CF498D]TrafficJamsi......
  • 识别题目
    参考书籍 :1.五年高考三年模拟  2.一年好题  思路:ocr识别题目信息,提取出题干信息关键字?什么是关键字? 问题:公式不变形,有影响吗?公式如何容易化简为适合变形的样......