LG7114 扭曲做法
2020 年 12 月 5 日……学习字符串……30min……折戟沉沙……以前的我!(大雾)
昨天躺床上无聊想出来这题的一个巨大扭曲做法,丢上来分享一下。
首先如果你考虑枚举
枚举
那么设最小循环节长度为
那么考虑对两种
最后在
线性筛太折磨了写了个调和级数筛日过去了……
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll readint(){
ll x=0;
bool f=0;
char c=getchar();
while(!isdigit(c)&&c!='-') c=getchar();
if(c=='-'){
f=1;
c=getchar();
}
while(isdigit(c)){
x=x*10+c-'0';
c=getchar();
}
return f?-x:x;
}
const int maxn=(1<<20)+5;
int d0[maxn],dd[maxn];
ll d1[maxn];
int n;
char s[maxn];
int nxt[maxn];
int cnt[26];
struct qr{
int x;
ll k;
};
vector<qr> q[maxn];
inline int lowbit(int x){
return x&-x;
}
int c[27];
void modify(int x,int k){
while(x<=26){
c[x]+=k;
x+=lowbit(x);
}
}
int query(int x){
int s=0;
while(x){
s+=c[x];
x-=lowbit(x);
}
return s;
}
int main(){
#ifdef LOCAL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
for(int i=1;i<maxn;i++) for(int j=1;i*j<maxn;j++){
d0[i*j]++;
d1[i*j]+=i;
}
for(int i=1;i<maxn;i++) dd[i]=d0[i]-(i%2==0?d0[i/2]:0);
int T=readint();
while(T--){
scanf("%s",s+1);
n=strlen(s+1);
int u=0;
for(int i=2;i<=n;i++){
while(u&&s[u+1]!=s[i]) u=nxt[u];
if(s[u+1]==s[i]) u++;
nxt[i]=u;
}
int cnt2=0;
for(int i=1;i<=n;i++) vector<qr>().swap(q[i]);
ll ans=0;
memset(cnt,0,sizeof(cnt));
for(int i=n-1;i>0;i--){
cnt[s[i+1]-'a']++;
cnt2+=cnt[s[i+1]-'a']%2==1?1:-1;
int x=i%(i-nxt[i])==0?i-nxt[i]:i;
q[x-1].push_back({cnt2,(d1[i/x]+dd[i/x])/2});
if(x!=i){
q[x-1].push_back({cnt2,(dd[i/x]-d1[i/x])/2});
q[x*2-1].push_back({cnt2,(d1[i/x]-dd[i/x])/2});
ans+=(d1[i/x]-dd[i/x])/2-d0[i/x]+dd[i/x];
}
}
memset(cnt,0,sizeof(cnt));
memset(c,0,sizeof(c));
cnt2=0;
for(int i=1;i<n;i++){
cnt[s[i]-'a']++;
cnt2+=cnt[s[i]-'a']%2==1?1:-1;
modify(cnt2+1,1);
for(qr x:q[i]) ans+=1ll*query(x.x+1)*x.k;
}
printf("%lld\n",ans);
}
#ifdef LOCAL
fprintf(stderr,"%d\n",(int)clock());
#endif
return 0;
}
我感觉这个做法巨大扭曲,不知道为什么混进来一点数论内容。直觉上整循环次数的约数应该有更优美的性质,有没有好一点的做法把这玩意去掉啊。
有 没 有 老 哥 教 教 我 啊
感觉是第一万次说这句话了,蒋队这样真正的强者都是自己搞清楚两个牛逼做法的联系的。