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分也很好写,还是应该把每道题都写一下啊。