牛子老师认为 xmz 回来了他终于有打的原因了,这就是神吗!
T1 挂了 45,又垫底了!
突然发现 tlecoders 和题库考试都叫冲刺国赛模拟。那到时候 url 好像会重。
宝石
首先套路拆成每个颜色的贡献。然后对于每个颜色分开考虑,用全部方案减掉没有这个颜色的方案。这个容易统计,可以对于每个不是这个颜色的连通块在最高的位置统计答案,线段树合并维护一下连通块大小就完事。
挂成狗,我只能说好似。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <vector>
using namespace std;
const int mod=998244353;
struct gra{
int v,next;
}edge[1000010];
int tot,head[500010];
void add(int u,int v){
edge[++tot].v=v;edge[tot].next=head[u];head[u]=tot;
}
int n,m,k,jc[500010],inv[500010],w[500010];
int A(int n,int m){
if(n<m)return 0;
return 1ll*jc[n]*inv[n-m]%mod;
}
struct node{
#define lson tree[rt].ls
#define rson tree[rt].rs
int ls,rs,sum;
}tree[500010<<6];
int t,rt[500010];
void update(int &rt,int L,int R,int pos,int val){
if(!rt)rt=++t;
if(L==R){
tree[rt].sum=val;return;
}
int mid=(L+R)>>1;
if(pos<=mid)update(lson,L,mid,pos,val);
else update(rson,mid+1,R,pos,val);
}
int query(int rt,int L,int R,int pos){
if(!rt)return 0;
if(L==R)return tree[rt].sum;
int mid=(L+R)>>1;
if(pos<=mid)return query(lson,L,mid,pos);
else return query(rson,mid+1,R,pos);
}
int merge(int x,int y,int l,int r){
if(!x||!y)return x|y;
if(l==r){
tree[x].sum+=tree[y].sum;return x;
}
int mid=(l+r)>>1;
tree[x].ls=merge(tree[x].ls,tree[y].ls,l,mid);
tree[x].rs=merge(tree[x].rs,tree[y].rs,mid+1,r);
return x;
}
int size[500010],ans;
void dfs(int x,int f){
size[x]=1;
for(int i=head[x];i;i=edge[i].next){
if(edge[i].v!=f){
dfs(edge[i].v,x);
size[x]+=size[edge[i].v];
rt[x]=merge(rt[x],rt[edge[i].v],1,m);
}
}
update(rt[x],1,m,w[x],size[x]);
if(f){
int s=size[x]-query(rt[x],1,m,w[f]);
ans=(ans-A(s,k)+mod)%mod;
}
else{
for(int i=1;i<=m;i++){
int s=size[x]-query(rt[x],1,m,i);
ans=(ans-A(s,k)+mod)%mod;
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);jc[0]=inv[0]=inv[1]=1;
for(int i=2;i<=n;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=1;i<=n;i++)jc[i]=1ll*jc[i-1]*i%mod,inv[i]=1ll*inv[i-1]*inv[i]%mod;
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
for(int i=1;i<n;i++){
int u,v;scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
ans=1ll*m*A(n,k)%mod;
dfs(1,0);
printf("%d\n",ans);
return 0;
}
序列
原题:LOJ Round #9 CommonAnts 的调和数。
很强!这一套的 T4 是典题,q-binomial 的 lucas 定理。然而我没补 LOJ Round,估计也没时间补了。但是题面挂了导致我全程以为这玩意不可做。很乐!
首先显然有狄利克雷前缀和做法:直接做一遍狄利克雷前缀和,转移的时候乘个对应位置的质数,然后再后缀和一次,同样乘对应位置的质数,然后直接找对应位置的答案。
设 \(\text{lcm}=z\),考虑到只有 \(10\) 个质因子,因此暴力把它们扫下来然后 dfs 出所有能拼出来的数,对它们做狄利克雷前缀和然后在 \(x\) 计算以 \(x\) 为 \(\gcd\) 的所有数的贡献,乘上一个贡献函数 \(f(\dfrac nx)\),其中
\[f(n)=\sum_{i=1}^ni^2[\gcd(i,z)=1] \]这个可以暴力容斥也可以冲一发洲阁筛,效率相近。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <unordered_map>
#define int long long
using namespace std;
const int mod=998244353,inv6=166374059;
int n,m,q,sq,cnt,M,p[20],a[200010];
int pos[200010],x[200010],k[200010];
unordered_map<int,int>sum,mp;
void ins(int x){
for(int i=1;i<=cnt;i++)while(x%p[i]==0)x/=p[i];
for(int i=2;i*i<=x;i++){
if(x%i==0){
p[++cnt]=i;
while(x%i==0)x/=i;
}
}
if(x!=1)p[++cnt]=x;
}
void dfs(int x,int val){
a[++a[0]]=val;
for(int i=x;i<=cnt&&1ll*p[i]*val<=n;i++)dfs(i,val*p[i]);
}
int g1[200010],g2[200010];
int find(int x){
return x<=sq?g1[x]:g2[n/x];
}
int s(int n){
n%=mod;
return 1ll*n*(n+1)%mod*(2*n+1)%mod*inv6%mod;
}
signed main(){
scanf("%lld%lld%lld",&n,&m,&q);sq=sqrt(n);
for(int i=1;i<=m;i++){
scanf("%lld%lld",&pos[i],&x[i]);sum[pos[i]]=(sum[pos[i]]+x[i])%mod;
ins(pos[i]);
}
for(int i=1;i<=q;i++)scanf("%lld",&k[i]),ins(k[i]);
sort(p+1,p+cnt+1);
dfs(1,1);
sort(a+1,a+a[0]+1);
int blo=n/(sq+1);
for(int i=1;i<=sq;i++)g1[i]=s(i);
for(int i=1;i<=blo;i++)g2[i]=s(n/i);
for(int i=1;i<=cnt;i++){
int t=1ll*p[i]*p[i]%mod,lim=blo/p[i];
for(int j=1;j<=lim;j++)g2[j]=(g2[j]-t*g2[j*p[i]]%mod+mod)%mod;
for(int j=lim+1;j<=blo;j++)g2[j]=(g2[j]-t*g1[n/(j*p[i])]%mod+mod)%mod;
for(int j=sq;j>=1;j--)g1[j]=(g1[j]-t*g1[j/p[i]]%mod+mod)%mod;
}
for(int i=1;i<=cnt;i++){
for(int j=1;j<=a[0];j++){
if(a[j]%p[i]==0){
sum[a[j]]=(sum[a[j]]+1ll*sum[a[j]/p[i]]*p[i])%mod;
}
}
}
for(int i=1;i<=a[0];i++)sum[a[i]]=1ll*sum[a[i]]*find(n/a[i])%mod;
for(int i=1;i<=cnt;i++){
for(int j=a[0];j>=1;j--){
if(a[j]%p[i]==0){
sum[a[j]/p[i]]=(sum[a[j]/p[i]]+1ll*sum[a[j]]*p[i])%mod;
}
}
}
for(int i=1;i<=q;i++)printf("%lld\n",sum[k[i]]);
return 0;
}
制胡窜
原题:CF1043G。*3500。
首先显然答案 \(-1,1,2,3,4\)。一个一个判。
\(-1\) 比较简单,爆扫一遍判没有字符相等就是 \(-1\)。
\(1\) 也好,暴力枚举因子,如果有整周期就是 \(1\)。
然后是 \(2\)。有三种情况是 \(2\):\(aab,baa,aba\)。前两种是相同的,可以用优秀的拆分那题的方法预处理每个位置开头 / 结尾的形如 \(aa\) 的串的最短长度,每次更新一段因此可以用并查集维护。
第三个需要判有无 border。可以用 border 的四种求法的方法,但是有更简单的方法。
根号分治,小于根号长度爆扫。大于根号长度的可以发现最短 border 在 \(sa\) 数组上到左端点 \(l\) 的距离小于根号,因为它在原串中位置不交。于是在 rk 上扫就行了。
然后是 \(3\)。\(3\) 的情况是 \(abac,baca,baac\)。第一个和第二个直接预处理每个字符的前后出现位置判断。第三个可以用之前的并查集挂上个 st 表查最小值。
如果以上都不是那就是 \(4\) 了。
代码体积有点大。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
int n,sq,m;
char s[200010];
struct SA{
int sa[200010],rk[200010],cnt[200010],rk2[200010],id[200010],key[200010];
int height[200010],st[200010][20];
void getsa(){
int m=127;
for(int i=1;i<=n;i++){
rk[i]=s[i];cnt[rk[i]]++;
}
for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sa[cnt[rk[i]]--]=i;
for(int w=1;;w<<=1){
int p=0;
for(int i=n;i>n-w;i--)id[++p]=i;
for(int i=1;i<=n;i++)if(sa[i]>w)id[++p]=sa[i]-w;
for(int i=1;i<=m;i++)cnt[i]=0;
for(int i=1;i<=n;i++)key[i]=rk[id[i]],cnt[key[i]]++;
for(int i=1;i<=m;i++)cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)sa[cnt[key[i]]--]=id[i];
for(int i=1;i<=n;i++)rk2[i]=rk[i];
p=0;
for(int i=1;i<=n;i++){
if(rk2[sa[i]]==rk2[sa[i-1]]&&rk2[sa[i]+w]==rk2[sa[i-1]+w])rk[sa[i]]=p;
else rk[sa[i]]=++p;
}
if(p==n){
for(int i=1;i<=n;i++)sa[rk[i]]=i;break;
}
m=p;
}
for(int i=1,k=0;i<=n;i++){
if(k)k--;
while(s[i+k]==s[sa[rk[i]-1]+k])k++;
height[rk[i]]=k;
}
for(int i=1;i<=n;i++)st[i][0]=height[i];
for(int j=1;j<=__lg(n);j++){
for(int i=1;i+(1<<j)-1<=n;i++){
st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
}
}
int query(int x,int y){
x=rk[x];y=rk[y];
if(x>y)swap(x,y);
x++;
int k=__lg(y-x+1);
return min(st[x][k],st[y-(1<<k)+1][k]);
}
}sa1,sa2;
int lcp(int x,int y){
return sa1.query(x,y);
}
int lcs(int x,int y){
return sa2.query(n-x+1,n-y+1);
}
struct Uni{
int fa[200010],val[200010];
int find(int x){
return x==fa[x]?fa[x]:fa[x]=find(fa[x]);
}
void merge(int l,int r,int v){
if(l>r)return;
for(int i=find(l);i<=r;i=find(i+1)){
val[i]=v;fa[i]=i+1;
}
}
}L,R;
struct ST{
int st[200010][20];
void build(){
for(int i=1;i<=n;i++)st[i][0]=i+L.val[i]-1;
for(int j=1;j<=__lg(n);j++){
for(int i=1;i+(1<<j)-1<=n;i++){
st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
}
}
int query(int l,int r){
int k=__lg(r-l+1);
return min(st[l][k],st[r-(1<<k)+1][k]);
}
}st;
int pre[200010],nxt[200010],pos[26];
int solve(int l,int r){
int len=r-l+1;
bool jud=false;
int cnt[26];
for(int i=0;i<26;i++)cnt[i]=0;
for(int i=l;i<=r;i++){
if(cnt[s[i]-'a']){
jud=true;break;
}
cnt[s[i]-'a']++;
}
if(!jud)return -1;
for(int i=1;i*i<=len;i++){
if(len%i==0){
int d=i;
if(lcp(l,l+d)>=len-d)return 1;
if(i!=1){
d=len/i;
if(lcp(l,l+d)>=len-d)return 1;
}
}
}
if(l+L.val[l]-1<=r||r-R.val[r]+1>=l)return 2;
for(int i=1;i<=min(sq,len-1);i++)if(lcp(l,r-i+1)>=i)return 2;
int lc=n;
for(int i=sa1.rk[l]-1;i>=1;i--){
int pos=sa1.sa[i];
if(sa1.st[i+1][0]<sq)break;
lc=min(lc,sa1.st[i+1][0]);
if(pos>l&&pos<=r&&pos+lc-1>=r)return 2;
}
lc=n;
for(int i=sa1.rk[l]+1;i<=n;i++){
int pos=sa1.sa[i];
if(sa1.st[i][0]<sq)break;
lc=min(lc,sa1.st[i][0]);
if(pos>l&&pos<=r&&pos+lc-1>=r)return 2;
}
if(nxt[l]<=r||pre[r]>=l||st.query(l,r)<=r)return 3;
return 4;
}
int main(){
scanf("%d%s%d",&n,s+1,&m);sq=sqrt(n);
for(int i=1;i<=n;i++)pre[i]=pos[s[i]-'a'],pos[s[i]-'a']=i;
for(int i=0;i<26;i++)pos[i]=n+1;
for(int i=n;i>=1;i--)nxt[i]=pos[s[i]-'a'],pos[s[i]-'a']=i;
sa1.getsa();reverse(s+1,s+n+1);sa2.getsa();reverse(s+1,s+n+1);
for(int i=1;i<=n+1;i++)L.fa[i]=R.fa[i]=i,L.val[i]=R.val[i]=0x3f3f3f3f;
for(int i=1;i<=(n>>1);i++){
for(int j=i;j+i<=n;j+=i){
int x=j,y=j+i;
int l=lcs(x,y),r=lcp(x,y);
x=x-l+1,y=y+r-1;
L.merge(x,y-2*i+1,i*2);R.merge(x+2*i-1,y,i*2);
}
}
st.build();
while(m--){
int l,r;scanf("%d%d",&l,&r);
printf("%d\n",solve(l,r));
}
return 0;
}
标签:return,int,冲刺,200010,--,国赛,include,模拟,mod
From: https://www.cnblogs.com/gtm1514/p/17429141.html