洛谷比赛 「EZEC」 Round 4

洛谷比赛 「EZEC」 Round 4

T1 zrmpaul Loves Array

题目描述

小 Z 有一个下标从 (1) 开始并且长度为 (n) 的序列,初始时下标为 (i) 位置的数字为 (i)。有 (m) 个操作,每个操作会是以下四种之一。

  • 1 对序列从小到大进行排序。
  • 2 对序列从小到大进行排序后将其翻转,(译者注:就是从大到小排序)。
  • 3 x y 将下标为 (x,y) 的数交换位置。保证 (x eq y)(1le x,yle n)
  • 4 将序列翻转。

你要输出在 (m) 次操作后的序列。

输入格式

第一行两个整数 (n,m) ,表示序列的长度以及操作的数量。

接下来 (m) 行,每行一个操作。保证操作合法。

输出格式

一行包含 (n) 个整数,表示操作后的序列。

输入输出样例

输入 #1

5 5
1
2
3 2 4
4
3 1 5

输出 #1

5 4 3 2 1
说明/提示

【数据范围】

【本题采用捆绑测试】

subtask 1(24pts): (1leq n,mleq 2 imes 10^3)

subtask 2(13pts): 没有操作三。

subtask 3(63pts): (1leq n,mleq 10^6)

【样例解释】

序列经过的操作为:

 1,2,3,4,5
 1,2,3,4,5
 5,4,3,2,1
 5,2,3,4,1
 1,4,3,2,5
 5,4,3,2,1

签到题还是有点思维难度的,但也比至于那么难,不过需要注意的点比较多。

自己在一个点上卡了一个小时(太菜了,该退役了)。

现讲一下思路吧。观察一下这四个操作,你就会发现一个神奇的规律。

在排序操作之前的操作是对答案没有任何影响的。

所以,我们只需要找到最后的一个操作(1) 或者操作 (2) ,如果最后一次是操作 (1) 原序列就正着排,反之倒着排序。

之后,我们就可以模拟操作三和操作四了。

操作三直接暴力 (swap) ,操作四的话可以打个标记,奇数的把这个序列暴力翻转。

一个比较坑的点就是一开始要把原序列赋为从 (1-n) 的排列,(因为他可能只有操作 (4))

随机数据的话还是跑的比较快的。

具体实现看代码把:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,type,maxn,res,cnt,a[1000010];
struct node
{
	int opt,x,y;
}q[1000010];
inline int read()
{
    int s = 0,w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
    return s * w;
}
int main()
{
	n = read(); m = read();
	for(int i = 1; i <= n; i++) a[i] = i;//一开始序列为1-n 的排列
	for(int i = 1; i <= m; i++)
	{
		q[i].opt = read();
		if(q[i].opt == 3)
		{
			cnt++;
			q[i].x = read(); q[i].y = read();
		}
		if(q[i].opt == 1 || q[i].opt == 2)//找最后一次排序操作
		{
			type = q[i].opt; maxn = i;
		}
	}
	if(type == 1)//如果是 1 就升序排列
	{
		for(int i = 1; i <= n; i++) a[i] = i;
	}
	if(type == 2)//反之降序排列
	{
		for(int i = n; i >= 1; i--) a[i] = n-i+1;
	}
	for(int i = maxn+1; i <= m; i++)
	{
		if(q[i].opt == 3)
		{			
		    if(res & 1)//如果前面序列反转了偶数次,就对序列暴力翻转
			{
				for(int j = 1; j <= n/2; j++) swap(a[j],a[n-j+1]);
			}
			swap(a[q[i].x],a[q[i].y]);//暴力交换
			res = 0;
		}
		else if(q[i].opt == 4) res += 1;//统计有多少个操作 4
	}
	if(res & 1) for(int i = 1; i <= n/2; i++) swap(a[i],a[n-i+1]);
	for(int i = 1; i <= n; i++) printf("%d ",a[i]);
	return 0;
}

T2「EZEC-4」可乐

题目背景

很久以前,有一个 pigstd,非常迷恋美味的可乐。为了得到美味的可乐,他几乎用尽了所有的钱,他甚至对自己的 npy 也漠不关心其实是因为他没有npy,更不爱好看戏。除非买了新可乐,才会坐上马车出门炫耀一番。每一天,每个钟头他都要喝上一瓶新可乐。

pigstd 最近又买了许多箱新可乐——当然,这些可乐只有聪明的人才能喝到。

题目描述

pigstd 现在有 (n) 箱可乐,第 (i) 箱可乐上标着一个正整数 (a_{i})

若 pigstd 的聪明值为一个非负整数 (x),对于第 (i) 箱可乐,如果 ((a_{i} oplus x )le k) 那么 pigstd 就能喝到这箱可乐。

现在 pigstd 告诉了你 (k) 与序列 (a),你可以决定 pigstd 的聪明值 (x),使得他能喝到的可乐的箱数最大。求出这个最大值。

输入格式

第一行两个由空格分隔开的整数 (n,k)

接下来 (n) 行,每行一个整数 (a_i),表示第 (i) 箱可乐上标的数。

输出格式
一行一个正整数,表示 pigstd 最多能喝到的可乐的箱数。
输入输出样例

输入 #1

3 5
2
3
4

输出 #1

3

输入 #2

4 625
879
480
671
853

输出 #2

4
说明/提示

提示

pigstd 的聪明值 (x) 可以为 (0)

样例解释

样例 1 解释:容易构造当 (x=0) 时,可以喝到所有可乐。

样例 2 解释:容易构造 (x=913),可以喝到所有可乐。

样例解释未必是唯一的方法。

数据范围

本题采用捆绑测试。

  • Subtask 1(29 points):(1 le n,k,a_{i} le 1000)
  • Subtask 2(1 point):(a_{i} le k)
  • Subtask 3(70 points):无特殊限制。

对于所有数据,保证 (1 le n,k,a_{i} le 10^6)

一开始看错题了,连交了几发全是 WA,差点把我心态搞崩了

这道题我和 STd 的解法不太一样。

这道题看到异或,我们会想到什么。 没错,就是 (01tire)

我们可以对每个 (a_i) 都插入 (tire) 树中,然后暴力枚举 (x)

找在异或起来不小于 (k) 的情况下,能喝到的可乐最多。

主要是分情况讨论比较难写,但其他的还算可以。

Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,k,tot = 1,siz[20000100],x,tr[20000100][2];
int ans,kk,maxn;
inline int read()
{
    int s = 0,w = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){s =s * 10+ch - '0'; ch = getchar();}
    return s * w;
}
void insert(int x)//建 tire 树
{
	int p = 1;
	for(int i = 20; ~i; i--)
	{
		int c = (x>>i)&1;
		if(!tr[p][c]) tr[p][c] = ++tot;
		p = tr[p][c];
        siz[p]++;
	}
}
int query(int x)
{
	int p = 1, res = 0;
	for(int i = 20; i; i--)
	{
		int a = (k>>i)&1, b = (x>>i)&1;
		if(a == 0)//如果说 k的这一位为0,那么tire树上的节点只能和 b 相同
		{
			if(b == 1) p = tr[p][1];
			if(b == 0) p = tr[p][0];
		}
		if(a == 1)
		{
			if(b == 0)
			{
				res += siz[tr[p][0]];//节点为 0 的话,异或起来小于 1可以直接记录答案
				p = tr[p][1];
			}
			else
			{
				res += siz[tr[p][1]];//同理
				p = tr[p][0];
			}
		}
	}
	int a =k & 1, b =x & 1;//最后一位的情况
	if(a == 0)
	{
		if(b == 1) p = tr[p][1];
		if(b == 0) p = tr[p][0];
	}
	if(a == 1)
	{
		if(b == 0)
		{
			res += siz[tr[p][0]];
			p = tr[p][1];
		}
		else
		{
			res += siz[tr[p][1]];
			p = tr[p][0];
		}
	}
	return res;
}
int main()
{
	n = read(); k = read(); maxn = k;
	for(int i = 1; i <= n; i++)
	{
		x = read();
		maxn = max(maxn,x);//上限设为 a[i] 和 k 的最大值
		insert(x);
	}
	for(int i = 0; i <= maxn<<1; i++)
    {
		ans=max(ans,query(i));//暴力枚举 x
	}
	printf("%d
",ans);
}

后面的几道题就比较仙了。

T3 Excalibur

题目描述

已知有 (n) 个血量均为 (k) 的敌人,编号(1sim n).

给你一柄圣剑,选择一个敌人,砍向他并对其造成 (p) 点伤害。

这柄圣剑还附有 (m) 条属性,第 (i) 条属性可表示为:

  • 1、若 (x_i) 号敌人受到了伤害,记此伤害为 (r_{x_i}) 那么 (y_i) 号敌人就会相应受到 (q imes r_{x_i})点伤害。
  • 2、若 (y_i) 号敌人受到了伤害,记此伤害为(r_{y_i}),那么 (x_i) 号敌人就会相应受到 (q imes r_{y_i}) 点伤害。
  • 3、上述两点若有任意一点被触发,该属性立即失效。

现规定,当某个敌人血量 (le 0) 时,即视为你击杀了这个敌人。击杀与否不影响伤害大小。

求你最多能击杀几个敌人。

数据保证:

1、每条属性中 (x_i eq y_i)

2、所有属性本质上不重复。

3 砍向任一敌人后,每个敌人最多受到一次伤害。

输入格式

第一行五个整数 (n,m,k,p),。

(m) 行每行两个整数 (x,y)

输出格式

一行一个整数表示答案。

输入输出样例

输入 #1

3 2 2 3 1
1 2
2 3

输出 #1

3

输入 #2

3 2 2 1 1
1 2
2 3

输出 #2

0

输入 #3

3 2 4 1 2
1 2
2 3

输出 #3

1
说明/提示

【样例 1 说明】

砍向 1 号敌人,对其造成 3 点伤害;22号敌人随之受到 3 点伤害;3 号敌人随之受到 3 点伤害。共击杀 3 个敌人。

【样例 2 说明】

无法击杀任何敌人。

【样例 3 说明】

砍向 1 号敌人,对其造成 1 点伤害;2 号敌人随之受到 2 点伤害;3 号敌人随之受到 4 点伤害。共击杀 1个敌人。

【数据规模与约定】

对于 100% 的数据,(1le nle 10^5)(0le mle 10^5)(1le k,p,qle 10^9)(1le x,yle n)

本题采用捆绑测试。

子任务 分值 n q 特殊性质
1 10 (≤10)
2 5 =1
3 5 对于每条属性都有 (xi=yi−1)
4 20 保证数据随机生成
5 60

这道题我开始以为是道图论题。

但,当我得知正解是树形 (dp) 时,我的表情是这样的:

算了,贴正解吧:

简化问题

给你若干棵树,选一颗树上的一个点作为根,根节点深度为 1,问树上有多少节点深度 (h)

[h leq egin{cases} log_q{k over p} + 1& ext{q > 1}\ infin & ext{q = 1, p <k}\ 1 & ext{q = 1, $ple k$} end{cases} ]

观察数据范围,注意到 (q>1)(log_q(frac{k}{p})<30),故可以从这个切入点解决问题。

考虑 ( ext{DP})

(sz[i][j]) 为以节点 (i) 为根,深度等于 (j) 的节点数。

(f[i][j]) 为以节点 (i) 为根,深度小于等于 (j) 的节点数。(最后求答案时用个容斥即可)

易求得 (sz) 数组的递推式为 (sz[u][j]= displaystyle sum sz[v][j−1]),其中 (u)(v) 为父子关系,下同。

当由父节点为根转为由子节点为根时,粗略观察到 (f[v][j])(f[u][j−1]) 很相似,进一步观察可得 (f[v][j]=f[u][j−1]+sz[v][j]+sz[v][j−1])

在每棵树中任选一个节点为根,先求出这个节点的 (sz) 和 $ f$ 数组后,再换根 ( ext{DP}) 即可。

别忘了特判 (q=1) 的情况哦!

时间复杂度为 (O(nlog_q(frac{k}{p})))

Code(正解代码):

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m,k,p,q,x,y,l,max_,vis[100009],trsz[100009],sz[100009][39],f[100009][39];
vector <ll> root,to[100009];
inline ll read(){
   ll s = 0,w = 1;
   char ch = getchar();
   while (ch < '0'|| ch > '9'){ if (ch == '-') w = -1; ch = getchar();}
   while (ch >= '0'&& ch <= '9') s = (s << 1) + (s << 3) + (ch ^ 48),ch = getchar();
   return s * w;
}
void dfs1(ll u,ll fa,ll pos){
    trsz[pos] += 1,sz[u][1] = vis[u] = 1;
    for (ll i = 0;i < to[u].size();i += 1){
        ll v = to[u][i];
        if (v ^ fa){
            dfs1(v,u,pos);
            for (ll j = 2;j <= l;j += 1) sz[u][j] += sz[v][j - 1];
        }
    }
}
void dfs2(ll u,ll fa,ll pos){
    for (ll i = 0;i < to[u].size();i += 1){
        ll v = to[u][i];
        if (v ^ fa){
            f[v][1] = 1;
            for (ll j = 2;j <= l;j += 1) f[v][j] = f[u][j - 1] + sz[v][j] + sz[v][j - 1];
            max_ = max(max_,trsz[pos] - f[v][l]);
            dfs2(v,u,pos);
        }
    }
}
int main(){
    n = read(),m = read(),k = read(),p = read(),q = read();
    for (ll i = 0;i < m;i += 1) x = read(),y = read(),to[x].push_back(y),to[y].push_back(x);
    if (q == 1 && p < k){cout<<0; return 0;}
    while (p * pow(q,l) < k) l += 1;
    for (ll i = 1;i <= n;i += 1) if (!vis[i]) root.push_back(i),dfs1(i,0,i);
    for (ll i = 0;i < root.size();i += 1) for (ll j = 1;j <= l;j += 1)
        f[root[i]][j] = f[root[i]][j - 1] + sz[root[i]][j];
    for (ll i = 0;i < root.size();i += 1) max_ = max(max_,trsz[root[i]] - f[root[i]][l]);
    for (ll i = 0;i < root.size();i += 1) dfs2(root[i],0,root[i]);
    cout<<max_;
    return 0;
}

T4「EZEC-4」求和

一句话题意:

T组询问,每次询问输出

(displaystylesum_{i=1}^{n}sum_{j=1}^{n} gcd(i,j)^{i+j} mod p) 的结果。

看完题直接弃疗了,打了 (10pts) 就滚粗了

直接甩个 (std) 的链接吧 link

这么复杂的柿子谁推的出来啊。

T5 「EZEC-4」月下轻花舞

std

一句话题意: (T) 组询问,每次给定三个整数 (l,r,n),求出下式的值:

(displaystyle sum_{i=l}^{r} (i-1) imes sum_{j=1}^{n} ceil(log_i^{j}) mod 998244353)

看到这个柿子,我直接大呼毒瘤。

这考的时候,一点思路都没有。并且考到的还是一些自己没学过的东西(比如什么拉格朗日差值)。果然还是自己太菜了。

T6 括号

不会写,没看。

好像正解是分块(毒瘤lxl逃

总结

这次打的还是很不错的,切了两道题。

总分 100 + 100 + 0 + 10 + 0 + 0 = 210;

混了个你谷 Rnk 17 。以后还要继续努力呀。

原文地址:https://www.cnblogs.com/genshy/p/13623550.html