2021/5/2 考试总结

T1洛谷P6225 [eJOI2019]异或橙子

题目描述

Janez 喜欢橙子!他制造了一个橙子扫描仪,但是这个扫描仪对于扫描的每个橙子的图像只能输出一个 32 位整数。

他一共扫描了 n 个橙子,但有时他也会重新扫描一个橙子,导致这个橙子的 32 位整数发生更新。

Janez 想要分析这些橙子,他觉得异或操作非常有趣,他每次选取一个区间从 l 至 u,他想要得到这个区间内所有子区间的异或和的异或和。

例如l=2,u=4 的情况,记橙子序列 A 中第 i个橙子的整数是 ,那么他要求的就是:

a2⊕a3⊕a4⊕(a2⊕a3)⊕(a3⊕a4)⊕(a2⊕a3⊕a4)

输入格式

第一行输入两个正整数 n,q,表示橙子数量和操作次数。

接下来一行 n 个非负整数,表示每个橙子扫描得到的数值 ,从 1 开始编号。

接下来 q行,每行三个数:

  • 如果第一个数是 1,接下来输入一个正整数 i与非负整数 j,表示将第 i 个橙子的扫描值ai​ 修改为 j。

  • 如果第一个数是 2,接下来输入两个正整数 u,l 表示询问这个区间的答案。

输出格式

对于每组询问,输出一行一个非负整数,表示所求的总异或和。

输入输出样例

输入 #1

3 3
1 2 3
2 1 3
1 1 3
2 1 3

输出 #1

2
0

考场思路

首先先想暴力,因为异或有一个性质,就是同一个数异或偶数次结果为0,奇数次为自己。

所以枚举区间内的每一个点,对于 i 来说,包含它的区间有(i-l+1)(r-i+1)个,所以判断一下奇偶性就可以了。

然后就拿了55分。

但是考试时怎么也想不到正解。

正解

如果多找几组数据推一下就会发现,当区间两个端点奇偶性不同时,异或的结果为0;

当奇偶性相同时,异或的结果就是a[l]^a[l+2]^……a[r],因此可以开两个树状数组,一个存奇数位的异或和,一个存偶数。

查询的时候因为刚才说的性质,所以直接用r的异或值异或上l-1的异或值,前面重复的就抵消了。

更新时也用到刚才的性质,先把这个位置异或自己,变为0,再异或更新的值。

#include<bits/stdc++.h>
using namespace std;
long long a[200061],b[200040];
int t[200060],n;
int lowbit(int x)
{
	return x&(-x);
}
void updatea(int x,int v)
{
	for(int i=x;i<=n;i+=lowbit(i))
	{
		a[i]^=v;
	}
}
void updateb(int x,int v)
{
	for(int i=x;i<=n;i+=lowbit(i))
	{
		b[i]^=v;
	}
}
long long querya(int x)
{
	long long res=0;
	for(int i=x;i;i-=lowbit(i))
	{
		res^=a[i];
	}
	return res;
}
long long queryb(int x)
{
	long long res=0;
	for(int i=x;i;i-=lowbit(i))
	{
		res^=b[i];
	}
	return res;
}
int main()
{
	int q;
	cin>>n>>q;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&t[i]);
		if(i&1) updatea(i,t[i]);
		else updateb(i,t[i]);
	}
	while(q--)
	{
		int opt,x,y;
		scanf("%d%d%d",&opt,&x,&y);
		if(opt==1)
		{
			if(x&1) updatea(x,t[x]^y);
			else updateb(x,t[x]^y);
			t[x]=y; 
			
		}
		else
		{
			if((y+x)&1) printf("0\n");
			else
			{
				if(x&1)  printf("%ld\n",querya(y)^querya(x-1));
				else printf("%ld\n",queryb(y)^queryb(x-1));
			}
		}
	}
	return 0;
} 

T2 -魔法 

题目描述 

给定一个长度为 n 的字符串s。设s中不同的字符数为k  。
定义字符串的子串为该字符串某一连续段。
而 有魔法的子串 被定义为s的某一非空子串,满足该子串中不同的字符数为k  ,且每个字符的出现的 次数都相同。
你需要求出给定字符串s的不同的 有魔法的子串 的个数。
若两个子串的左右端点不同,则这两个子串不同。

输入格式 

第一行:一个整数 n 表示字符串长度。
第二行:一个字符串  s。

输出格式 

一个整数表示答案 mod(1e9+7) 的值。

考场思路

30分的暴力还是很好拿的,于是就先写了30分。

然后看第三档分数,k=2;

于是就开始推,如果 设cnt[i][0/1]分别表示在第i个位置及之前的位置上这两种字符分别出现了多少次,也就是前缀和。

那么目标就是求所有的 j (0<=j < i)满足

cnt[i][0]-cnt[j][0]=cnt[i][1]-cnt[j][1];

也就是

cnt[i][0]-cnt[i][1]=cnt[j][0]-cnt[j][1]

然后可以把每一个为值上的差值统计出来存出来最后在加上,30分就拿到了

	if(k==2)
	{
		cal[n]=1;//cal就是存储差值的数组,为了防止下标为负因此都加上了n,所以这个相当于                                      
                         //cal[0]=1;
 		for(int i=1;i<=n;i++)
		{
			int q=cnt[i][ord[1]]-cnt[i][ord[2]]+n; //ord是存储那两种字符
			ans=(ans+cal[q])%mod;
			cal[q]++;
		}
		cout<<ans;
	}
 

T3 T4 dp和数学,直接放弃

总结

总的来说,这场比赛只要是写的分都拿到了,不过好像T4的40分也很好写,还是应该把每道题都写一下啊。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值