A.字符串识别
Description
Solution
这题问题出在了对于一个后缀自动机,这里如果在 (DAG) 上面进行操作,复杂度是 (O(n^2)) 的
所以我们换个思路做,还是考虑那些 (endpos) 集合大小为 (1) 的字符串
看看贡献:
[iin [i-len_{fa_i}+1,i] ans_i=min(ans_i,len_{fa_i}+1)
]
[iin[i-len_i+1,i-len_{fa_i}] ans_i=min(ans_i,len_p-i+1)
]
对于第一种情况,线段树统计答案
对于第二种情况,观察到 (ans_i) 这一项总是加着一个 (i)
那么先统一加上,然后最后面减掉,对位置减就好的
另外,区间取 (min) 的线段树,需要好好记得怎么写……
Code
#include<bits/stdc++.h>
using namespace std;
namespace yspm{
inline int read()
{
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int N=5e5+10,M=15e5+10;
int ans[N],le;
struct SGT{
int maxx[N<<2];
#define maxx(p) maxx[p]
inline void build(int p,int l,int r,int val)
{
if(l==r) return maxx(p)=le+l*val,void();
int mid=(l+r)>>1; build(p<<1,l,mid,val); build(p<<1|1,mid+1,r,val);
return maxx(p)=2e9+10,void();
}
inline void update(int p,int st,int ed,int l,int r,int val)
{
if(l<=st&&ed<=r) return maxx(p)=min(maxx(p),val),void();
int mid=(st+ed)>>1;
if(l<=mid) update(p<<1,st,mid,l,r,val);
if(r>mid) update(p<<1|1,mid+1,ed,l,r,val);
return ;
}
inline void work(int p,int st,int ed,int val)
{
if(st==ed)
{
ans[st]=min(ans[st],maxx(p)-val*st);
return ;
}
int mid=(st+ed)>>1;
maxx(p<<1)=min(maxx(p<<1),maxx(p));
maxx(p<<1|1)=min(maxx(p<<1|1),maxx(p));
work(p<<1,st,mid,val); work(p<<1|1,mid+1,ed,val);
return ;
}
}T1,T2;
struct edg{
int to,nxt;
}e[M<<1];
int head[M],cnt;
inline void add(int u,int v)
{
e[++cnt].to=v; e[cnt].nxt=head[u];
return head[u]=cnt,void();
}
int son[M][26],fa[M],len[M],tot=1,r[M],las=1,sz[M];
inline void copy(int x,int y)
{
for(int i=0;i<26;++i) son[x][i]=son[y][i];
return ;
}
inline void extend(int x,int id)
{
int tmp=las,np=las=++tot; len[np]=len[tmp]+1; sz[np]=1; r[np]=id;
while(tmp&&!son[tmp][x]) son[tmp][x]=np,tmp=fa[tmp];
if(!tmp) return fa[np]=1,void();
int q=son[tmp][x];
if(len[q]==len[tmp]+1) return fa[np]=q,void();
int clone=++tot; len[clone]=len[tmp]+1;
fa[clone]=fa[q],fa[np]=fa[q]=clone; copy(clone,q); r[clone]=x;
while(tmp&&son[tmp][x]==q) son[tmp][x]=clone,tmp=fa[tmp];
return ;
}
inline void dfs(int x)
{
for(int i=head[x];i;i=e[i].nxt)
{
int t=e[i].to; dfs(t);
sz[x]+=sz[t];
}
if(sz[x]==1)
{
T1.update(1,1,le,r[x]-len[fa[x]]+1,r[x],len[fa[x]]+1);
T2.update(1,1,le,r[x]-len[x]+1,r[x]-len[fa[x]],len[x]+1);
}
return ;
}
char s[N];
signed main()
{
memset(ans,0x3f,sizeof(ans));
scanf("%s",s+1); le=strlen(s+1);
T1.build(1,1,le,0);
T2.build(1,1,le,1);
for(int i=1;i<=le;++i) extend(s[i]-'a',i);
for(int i=1;i<=tot;++i) add(fa[i],i); dfs(1);
T1.work(1,1,le,0);
T2.work(1,1,le,1);
for(int i=1;i<=le;++i) printf("%d
",ans[i]);
return 0;
}
}
signed main(){return yspm::main();}
B. CQOI2015 选数
Descripion
求
[sum_{a_1=L}^R sum_{a_2=L}^Rdotssum_{a_n=L}^R [gcd(a_1,a_2,a_3,dots,a_n)=k] (mod 10^9+7)
]
所有参量满足 (le 10^9)
Solution
式子真难看,反手一推变成
[sum_{i=1}^{lfloor frac R k
floor} mu(i) (lfloor frac {lfloor frac R k
floor} i
floor-frac {lfloor frac L k
floor} i
floor)^n
]
(考试的时候推错式子了……然后外加一些手残就爆零了)
其实搞几个变量代替一下就没这么麻烦了……
然后我们发现这种做法需要开不下的空间做 (mu)
然后去学了一下杜教筛就做完了
或者容斥大佬会这种做法
Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf (int)1e18+10
namespace yspm{
inline int read()
{
int res=0,f=1; char k;
while(!isdigit(k=getchar())) if(k=='-') f=-1;
while(isdigit(k)) res=res*10+k-'0',k=getchar();
return res*f;
}
const int mod=1e9+7;
inline int ksm(int x,int y)
{
int res=1;
for(;y;y>>=1,x=x*x%mod) if(y&1) res=res*x%mod;
return res;
}
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
const int N=7e6+10;
bool fl[N];
int pri[N],cnt,mu[N],n,l,h,k,ans,low,up;
inline void prework()
{
mu[1]=1;
for(int i=2;i<N;++i)
{
if(!fl[i]) pri[++cnt]=i,mu[i]=mod-1;
for(int j=1;j<=cnt&&i*pri[j]<N;++j)
{
fl[i*pri[j]]=1;
if(i%pri[j]==0){mu[i*pri[j]]=0; break;}
else mu[i*pri[j]]=-mu[i],mu[i*pri[j]]=(mu[i*pri[j]]+mod)%mod;
}
}
for(int i=1;i<N;++i) mu[i]=add(mu[i],mu[i-1]);
return ;
}
map<int,int> mp;
inline void calc(int x,int &ans)
{
if(x<N) return ans=mu[x],void();
if(mp[x]) return ans=mp[x],void();
ans=1;
for(int l=2,r,s=0;l<=x;l=r+1)
{
r=x/(x/l); s=0;
calc(x/l,s);
(s+=mod)%mod;
ans-=(r-l+1)*s%mod;
ans=add(ans,mod);
} return mp[x]=ans,void();
}
inline int ask(int x)
{
int ans=0; calc(x,ans); return ans;
}
signed main()
{
n=read(); k=read(); l=read(); h=read();
low=(l-1)/k; up=h/k;
if(low>up) return puts("0"),0;
prework();
for(int i=1,j;i<=up;i=j+1)
{
j=min(low/i?low/(low/i):inf,up/(up/i));
ans=add(ans,(ask(j)-ask(i-1)+mod)%mod*ksm(up/i-low/i,n)%mod);
} cout<<ans<<endl;
return 0;
}
}
signed main(){return yspm::main();}
C.树点涂色
最 (sb) 的一个题目了,不到二十天之前刚刚做完,然后今天就忘记了
不写了,直接贴link
总结
感觉上午状态很诡异,上手记错了一个结论,线段树写了一个多小时,还妄想自己能 (AK) 结果呢?
就 (20)
好好的 (lct) 不会打,反演的式子还推错
我服了我自己,慢慢考试慢慢进步吧