题意:
给你一个字符串,判断他是否由若干个
对称串
拼接而成,对称串的定义为:
- 空串
- 为o|s|x|z任一字母
- 在
对称串
的两端分别加对称字母,即S=bSq∣dSp∣pSd∣qSb∣nSu∣uSn|oSo∣sSs∣xSx∣zSz
思路:
其实就是
字符串哈希判回文
,这个东西的原理非常显然:如果某一段是回文,那么他正反哈希值是一样的。对于本题,判对称,我们可以搞一个与它对称的字符串,对称字符串倒序和原字符串是段段相等的,然后哈希就可以了。
jls的做法为把对称字符串倒序加到原字符串后,直接一段段判相等即可,例如:
s b z z q − > s b z z q b z z q s sbzzq -> sbzzqbzzqs sbzzq−>sbzzqbzzqs
在转变后的字符串中,每段对称串都是前后相等的,当该段不为对称段时,是不相等的,例如:
b x s x x q − > b x s x x q b x x s x q bxsxxq->bxsxxqbxxsxq bxsxxq−>bxsxxqbxxsxq
这个太好写了但是我看不懂jls的哈希板子,所以用一个常见一点的哈希板子。
(ghs1是正着哈希,ghs2是反着哈希,所以这个板子的t字符串不用翻转。
AC代码
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
const int mod = 998244353;
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 50;
const int B=1331;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
string a="bdpqnuosxz";
string b="qpdbunosxz";
map<char,char>mp;
struct Hash {
ll p, MOD, pp[N];
ll pre[N], suf[N];
Hash(int P, int mod) {
p = P; MOD = mod;
}
void init(char *s, char *t, int n) {
pp[0] = 1; pre[0]=0;suf[n + 1] = 0;
for (int i = 1; i <= n; i++) {
pp[i] = pp[i - 1] * p % MOD;
pre[i] = (pre[i - 1] * p + s[i]) % MOD;
}
for (int i = n; i >= 1; i--) {
suf[i] = (suf[i + 1] * p + t[i]) % MOD;
}
}
ll ghs1(int l, int r) {
return (pre[r] - pre[l - 1] * pp[r - l + 1] % MOD + MOD) % MOD;
}
ll ghs2(int l, int r) {
return (suf[l] - suf[r + 1] * pp[r - l + 1] % MOD + MOD) % MOD;
}
bool equal(int l1, int r1, int l2, int r2) {
return ghs1(l1, r1) == ghs2(l2, r2);
}
}hs1(233, 998244353), hs2(1331, 1000000007);
char s[N],t[N];
void work() {
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=1;i<=n;++i){
if(a.find(s[i])==-1){
cout<<"No\n";return;
}
}
for(int i=1;i<=n;++i){
t[i]=b[a.find(s[i])];
}
hs1.init(s,t,n);
hs2.init(s,t,n);
int l=1;
for(int i=1;i<=n;++i){
if(hs1.equal(l,i,l,i)&&hs2.equal(l,i,l,i))l=i+1;
}
if(l==n+1)cout<<"Yes\n";
else cout<<"No\n";
}
signed main() {
//io;
int t;
cin >> t;
while (t--) {
work();
}
return 0;
}
题意:
对于一个01字串x有以下两个操作:
- A:将x中的0和1翻转
- B:执行x=x+1,特别的当x全为1时,加1变全0
现在给定一个AB串,有以下q个询问,每个询问给出l,r,x,求出经过AB串的l->r操作后,x会变成什么。
强制在线做法,每次询问的真实l,r由上次的答案决定。
思路:
前缀和
。首先注意到先做B再做A相当于先做A再减1,对于每次A翻转有 ( x + x ′ + 1 ) = 0 (x+x'+1)=0 (x+x′+1)=0
考虑若干个操作后AB对于结果的影响:
偶数
个A操作代表不翻转
,奇数
个A操作代表翻转
;在奇数
个A前的B对结果的影响是-1
,在偶数
个A前的B对结果的影响是+1
。做出A,前面有偶数个A的B,前面有奇数个A的B的前缀和。
对于某一段,前缀和直接得出该段中A的奇偶性判断是否需要翻转。设o=Bi->r中A的个数的奇偶性,x=1->Bi中A的个数的奇偶性,g=1->r中A的个数的奇偶性,易得 o = x ⊕ g o=x\oplus g o=x⊕g
g是确定的,我们已经前缀出了符合x特性的B的个数,假定g为奇数,则add为本段里使x为1的B个数-使x为0的B个数
,如果g是偶数,则把add取负即可。注意到add有可能是极小的数,所以需要先取模。(呜呜呜被这卡了一晚上)
AC代码
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
//#define int ll
#define pb push_back
#define eb emplace_back
#define m_p make_pair
const int mod = 998244353;
#define mem(a,b) memset(a,b,sizeof a)
#define pii pair<int,int>
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 3e5 + 50;
//__builtin_ctzll(x);后导0的个数
//__builtin_popcount计算二进制中1的个数
int a[N];
int b[N];//b_0
int c[N];//b_1
void work() {
int n,q;cin>>n>>q;
string s;cin>>s;
for(int i=1;i<=n;++i){
if(s[i-1]=='A')a[i]=1;
else b[i]=1;
}
for(int i=2;i<=n;++i){
a[i]+=a[i-1];
if((a[i]&1)&&b[i]==1){
c[i]=1;
}
b[i]+=b[i-1];
}
for(int i=1;i<=n;++i){
c[i]+=c[i-1];
b[i]-=c[i];
}
ll lst=0;
while(q--){
ll l,r;
cin>>l>>r;
l=(l^lst)%n;r=(r^lst)%n;
if(l>r)swap(l,r);
l++;r++;
string ss;
cin>>ss;
int sl=ss.length();
ll x=0,m=1ll<<sl;
for(int i=0;i<sl;++i){
x*=2;x+=(ss[i]-'0');
}
//翻转
if((a[r]-a[l-1])&1){
x=m-1-x;
}
//o=x^g
ll add=b[r]-c[r]-b[l-1]+c[l-1];//偶-奇,偶加奇减
if(a[r]&1){//g为a[r]的奇偶性
add*=(-1);
};
add%=m;
x=(x+add+m)%m;
lst=x;
for(int i=sl-1;i>=0;--i){
if(x>>i&1)cout<<"1";
else cout<<"0";
}
cout<<'\n';
}
}
signed main() {
io;
int t=1;
//cin >> t;
while (t--) {
work();
}
return 0;
}
E签到的枚举
F队友都不知道怎么莫名其妙就过了的博弈
I五子棋!
K队友指导我写的dp
队友带飞我呜呜呜浅补两个好了()