Codeforces Round #742 (Div. 2) CF1567

A.水题

很明显对于某一列如果已知行为 D D D,那么填 U U U

如果已知行为 U U U,那么填 D D D

否则要不填 L L L,要不填 R R R

一定是 L R L R … LRLR\dots LRLR交替填的

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+100;
char s[maxn];
char ch[2] = {'L','R'};
int main()
{
  int t;
  scanf("%d",&t);
  while (t--)
  {
    int cur = 0;
    int n;scanf("%d",&n);
    scanf("%s",&s);
    for (int i=0;i<n;++i)
    {
      if (s[i]=='U')printf("D");
      else if (s[i]=='D')printf("U");
      else 
      {
        printf("%c",ch[cur]);
        cur^=1;
      }
    }printf("\n");
  }
}

B.分类讨论

因为 m e x mex mex的限制, 0 0 0 a − 1 a-1 a1必须包含

  1. 0 ⊕ 1 ⊕ 2 ⊕ ⋯ ⊕ a − 1 = = b 0\oplus1\oplus2\oplus\dots\oplus a-1==b 012a1==b:答案就是 a a a
  2. 0 ⊕ 1 ⊕ 2 ⊕ ⋯ ⊕ a ! = b : 0\oplus1\oplus2\oplus\dots\oplus a!=b: 012a!=b:答案就是 a + 1 a+1 a+1
  3. 0 ⊕ 1 ⊕ 2 ⊕ ⋯ ⊕ a = = b : 0\oplus1\oplus2\oplus\dots\oplus a==b: 012a==b:答案为 a + 2 a+2 a+2
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5+100;
int pre[maxn];
int main()
{
  for (int i=1;i<maxn;++i)pre[i]=pre[i-1]^i;
  int t;scanf("%d",&t);
  while (t--)
  {
    int a,b;scanf("%d %d",&a,&b);
    if (pre[a-1]==b)printf("%d\n",a);
    else if (pre[a]==b)printf("%d\n",a+2);
    else printf("%d\n",a+1);
  }
}

C.思维

进位相隔 1 1 1使得一切都变得麻烦了

可以发现,奇数位是绝对不会进位到偶数位上的

奇偶是相对独立的

因此我们可以 n n n的奇数位和偶数位分别抽出来观察

n 1 n_1 n1 奇数位组成的数

n 2 n_2 n2 偶数位组成的数

对于 n 1 , n 2 n_1,n_2 n1,n2遵循的就是经典的加减法了

因此,将 n n n分成两个数 n u m 1 , n u m 2 num_1,num_2 num1,num2

其实就是将 n 1 , n 2 n_1,n_2 n1,n2分别分为两个数,一个给 n u m 1 num_1 num1一个给 n u m 2 num_2 num2

再减去出现 0 0 0的情况

#include<bits/stdc++.h>
using namespace std;
vector<int> ns;
vector<int> res;
map<int,int> mp,mmp;
int main()
{
  int t;scanf("%d",&t);
  while (t--)
  {
    res.clear();
    ns.clear();
    int n;scanf("%d",&n);
    while (n)
    {
      ns.push_back(n%10);
      n/=10;
    }reverse(ns.begin(),ns.end());
    int tmp1 = 0,tmp2 = 0;
    for (int i=0;i<ns.size();i+=2)
      tmp1 = tmp1*10+ns[i];
    for (int i=1;i<ns.size();i+=2)
      tmp2 = tmp2*10+ns[i];
    if (tmp1==0)
    {
      printf("%d\n",tmp2-1);
    }
    else if (tmp2==0)
    {
      printf("%d\n",tmp1-1);
    }
    else
    {
      printf("%lld\n",1LL*(tmp1+1)*(tmp2+1)-2);
    }
  }
}

D.思维+贪心

我们想一想,十进制和十一进制有什么区别?

十进制比十一进制更加容易向高位进位

如果我们按照十进制的方式计算向高位进了一位,那么十一进制下我们是赚了的!

进的位数越高我们赚的越多。

因此,这一题我们的策略是:贪心地保留高位!

例如: 211   5 211\ 5 211 5

我们此时有 2 2 2个百位, 1 1 1个十位, 1 1 1个个位

在全部保留地情况下也只能划分为 4 4 4个数,因此,我们要拆位数

位数越高,保留下来我们是越赚地

因此,我们从低位开始去拆,但是个位已经拆到头了

所以我们去拆十位,拆成 9 9 9个个位

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct cmp
{
	bool operator()(int na,int nb)
	{
		if (nb==1)return false;
		else if (na==1)return true;
		return na>nb;
	}
};
int main()
{
	int t;scanf("%d",&t);
	while (t--)
	{
		priority_queue<int,vector<int>,cmp> que;
		int s,n;scanf("%d %d",&s,&n);
		int tmp=1;
		while (s)
		{
			int cnt=s%10;
			if (cnt)while(cnt--)que.push(tmp);
			s/=10;
			tmp*=10;
		}
		while(que.size()<n)
		{
			int num = que.top();
			num/=10;
			que.pop();
			for (int i=1;i<=10;++i)que.push(num);
		}
		vector<int> res;
		while (!que.empty())
		{
			res.push_back(que.top());
			que.pop();
		}
		sort(res.begin(),res.end());
		reverse(res.begin(),res.end());
		while (res.size()>n)
		{
			int sum = res.back();
			res.pop_back();
			sum += res.back();
			res.pop_back();
			res.push_back(sum);
		}
		for (int num:res)printf("%d ",num);
		printf("\n");
	}
}

E.线段树+思维

看题目的形式我们便会发现,这题我们八成是要上线段树了。

考虑线段树维护答案!

对于线段树的每个节点 r t : [ l , r ] rt:[l,r] rt:[l,r]

我们维护三个值

  1. s u m [ r t ] : c o u n t ( { q , p } ∣ l ≤ q ≤ p ≤ r   a n d   a q ≤ a q + 1 ≤ ⋯ ≤ a p ) sum[rt]:count(\{q,p\}|l\le q\le p\le r\ and\ a_q\le a_{q+1}\le \dots \le a_p) sum[rt]:count({q,p}lqpr and aqaq+1ap)
  2. l e 1 [ r t ] : c o u n t ( q ∣ l ≤ q ≤ r   a n d   a l ≤ a l + 1 ≤ ⋯ ≤ a q ) le1[rt]:count(q|l\le q\le r\ and\ a_l\le a_{l+1}\le \dots \le a_q) le1[rt]:count(qlqr and alal+1aq)
  3. l e 2 [ r t ] : c o u n t ( q ∣ l ≤ q ≤ r   a n d   a q ≤ a q + 1 ≤ ⋯ ≤ a r le2[rt]:count(q|l\le q\le r\ and\ a_q\le a_{q+1}\le \dots\le a_r le2[rt]:count(qlqr and aqaq+1ar

也就是说, s u m [ r t ] sum[rt] sum[rt]为答案, l e 1 [ r t ] le1[rt] le1[rt]为以 a [ l ] a[l] a[l]为开头在 [ l , r ] [l,r] [l,r]中最长不下降子序列的长度

l e 2 [ r t ] le2[rt] le2[rt]为以 a [ r ] a[r] a[r]为结尾在 [ l , r ] [l,r] [l,r]中最长不下降子序列的长度

利用这三个值,我们可以更新出他们父节点区间的答案

重点说明一下区间合并答案 [ l , r ] [l,r] [l,r]

我们合并KaTeX parse error: Undefined control sequence: \[ at position 1: \̲[̲l,mid\](rt_1),\…的答案,记为 a n s ans ans

首先 a n s + = s u m [ r t 1 ] + s u m [ r t 2 ] ans+=sum[rt_1]+sum[rt_2] ans+=sum[rt1]+sum[rt2]是肯定的

然后我们要计算跨区间的不下降序列的个数:KaTeX parse error: Undefined control sequence: \* at position 21: …d]\le a[mid+1])\̲*̲le2[rt_1]\*le1[…

即:KaTeX parse error: Undefined control sequence: \* at position 48: …] \le a[mid+1])\̲*̲le2[rt_1]\*le1[…

而, l e 1 , l e 2 le1,le2 le1,le2的更新就更加简单了

至于 u p d a t e update update因为是单点修改,所以没有难度,直接递归到底层然后一路重建即可

关键是 q u e r y query query 大体的步骤和上面建造树时合并KaTeX parse error: Undefined control sequence: \[ at position 1: \̲[̲l,mid\](rt_1),\…答案类似

KaTeX parse error: Undefined control sequence: \* at position 47: …d]\le a[mid+1])\̲*̲le2[rt_1]\*le1[…

但是要注意的是,这里的 l e 2 [ r t 1 ] , l e 1 [ r t 2 ] le2[rt_1],le1[rt_2] le2[rt1],le1[rt2]很可能是错的

因为我们所要求的并不是区间 [ l , r ] [l,r] [l,r]的答案,而是 [ q l , q r ] [ql,qr] [ql,qr]的答案

其中 l ≤ q l ≤ m i d < m i d + 1 ≤ q r ≤ r l\le ql\le mid< mid+1\le qr\le r lqlmid<mid+1qrr

因此, l e 2 [ r t 1 ] le2[rt_1] le2[rt1]改为 m i n ( l e 2 [ r t 1 ] , m i d − q l + 1 ) min(le2[rt_1],mid-ql+1) min(le2[rt1],midql+1)

l e 1 [ r t 2 ] le1[rt_2] le1[rt2]改为 m i n ( l e 1 [ r t 2 ] , q r − m i d ) min(le1[rt_2],qr-mid) min(le1[rt2],qrmid)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
int n,q;
struct stree
{
	int a[maxn];
	ll sum[maxn<<2];
	int le1[maxn<<2],le2[maxn<<2];
	inline void push_up(int rt,int l,int r)
	{
		int mid = l+r>>1;
		sum[rt]=sum[rt<<1|1]+sum[rt<<1];
		le1[rt] = le1[rt<<1];
		le2[rt] = le2[rt<<1|1];
		if (a[mid+1]>=a[mid])
		{
			sum[rt] += 1LL*le2[rt<<1]*le1[rt<<1|1];
			if (le1[rt<<1]==mid-l+1)le1[rt] += le1[rt<<1|1];
			if (le2[rt<<1|1]==r-mid)le2[rt] += le2[rt<<1];
		}
	}
	void build(int rt,int l,int r)
	{
		if (l==r)
		{
			sum[rt]=1;
			le1[rt]=le2[rt]=1;
			return;
		}int mid = l+r>>1;
		build(rt<<1,l,mid);
		build(rt<<1|1,mid+1,r);
		push_up(rt,l,r);
	}
	void update(int rt,int l,int r,int tar,int val)
	{
		if (l==r)
		{
			a[tar]=val;
			return;
		}
		int mid = l+r>>1;
		if (mid>=tar)update(rt<<1,l,mid,tar,val);
		else update(rt<<1|1,mid+1,r,tar,val);
		push_up(rt,l,r);
	}
	ll query(int rt,int l,int r,int ql,int qr)
	{
		if (l>=ql&&r<=qr)return sum[rt];
		
		int mid = l+r>>1;
		if (mid>=qr)return query(rt<<1,l,mid,ql,qr);
		if (mid+1<=ql)return query(rt<<1|1,mid+1,r,ql,qr);
		ll ans = query(rt<<1,l,mid,ql,qr)+query(rt<<1|1,mid+1,r,ql,qr);
		int len2 = min(mid-ql+1,le2[rt<<1]);
		int len1 = min(qr-mid,le1[rt<<1|1]);
		if (a[mid+1]>=a[mid])
			ans+=1LL*len1*len2;
		return ans;
	}
}S1;
int main()
{
	ios::sync_with_stdio(0);
	cin>>n>>q;
	for (int i=1;i<=n;++i)cin>>S1.a[i];
	S1.build(1,1,n);
	while (q--)
	{
		int opt;cin>>opt;
		if (opt==1)
		{
			int x,y;cin>>x>>y;
			S1.update(1,1,n,x,y);
		}
		else
		{
			int ql,qr;cin>>ql>>qr;
			cout<<S1.query(1,1,n,ql,qr)<<endl;
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值