Codechef July Challenge 2020
PTMSSNG
先当于求 x 里出现奇数次的,y 里出现奇数次的,异或和一下就好了
#include<bits/stdc++.h>
#define D(...) fprintf(stderr,__VA_ARGS__)
#define int long long
#define LL long long
#define MP make_pair
#define PB push_back
#define fi first
#define se second
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef pair<int,int>PII;typedef vector<int>VI;typedef vector<PII>VII;
template<typename T>void rd(T&x){int f=0,c;while(!isdigit(c=getchar()))f^=!(c^45);x=(c&15);while(isdigit(c=getchar()))x=x*10+(c&15);if(f)x=-x;}
template<typename T>void pt(T x,int c=-1){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);if(c!=-1)putchar(c);}
int T;
signed main(){
rd(T);
while(T--){
int n;rd(n),n=n*4-1;
int x=0,y=0;
rep(i,1,n){
int k1,k2;rd(k1),rd(k2);
x^=k1,y^=k2;
}
pt(x,' '),pt(y,'\n');
}
return 0;
}
CHFNSWPS
把 A 和 B 共有的删掉,那么只要把剩下的最小的一半和最大的一半 swap 即可,注意代价还要对序列里的最小值的两倍取 min,因为可以通过最小值实现两倍代价交换两个数
#include<bits/stdc++.h>
#define D(...) fprintf(stderr,__VA_ARGS__)
#define int long long
#define LL long long
#define MP make_pair
#define PB push_back
#define fi first
#define se second
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef pair<int,int>PII;typedef vector<int>VI;typedef vector<PII>VII;
template<typename T>void rd(T&x){int f=0,c;while(!isdigit(c=getchar()))f^=!(c^45);x=(c&15);while(isdigit(c=getchar()))x=x*10+(c&15);if(f)x=-x;}
template<typename T>void pt(T x,int c=-1){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);if(c!=-1)putchar(c);}
const int N=200005;
int T,n,A[N],B[N],C[N*2];
signed main(){
rd(T);
while(T--){
rd(n);
rep(i,1,n)rd(A[i]),C[i]=A[i];
rep(i,1,n)rd(B[i]),C[i+n]=B[i];
sort(A+1,A+1+n);
sort(B+1,B+1+n);
sort(C+1,C+1+n*2);
int k1=1,k2=1;
VI num;
int ans=0;
for(int i=1;i<=n*2;i+=2){
if(C[i]!=C[i+1]){
puts("-1");
goto qaq;
}
while(k1<=n&&A[k1]<C[i])++k1;
while(k2<=n&&B[k2]<C[i])++k2;
if(k1>n||A[k1]!=C[i]||k2>n||B[k2]!=C[i]){
num.PB(C[i]);
}else{
++k1,++k2;
}
}
assert(SZ(num)%2==0);
rep(i,0,SZ(num)/2-1){
ans+=min(num[i],C[1]*2);
}
pt(ans,'\n');
qaq:;
rep(i,1,n)A[i]=0,B[i]=0,C[i]=C[i+n]=0;
}
return 0;
}
DRCHEF
感觉做法写烦了,暴力的模拟这个过程:每次查找有没有数字在 [(x+1)/2,x] 的,如果有,干最小的满足条件的,否则干最大的一个数
#include<bits/stdc++.h>
#define D(...) fprintf(stderr,__VA_ARGS__)
#define int long long
#define LL long long
#define MP make_pair
#define PB push_back
#define fi first
#define se second
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef pair<int,int>PII;typedef vector<int>VI;typedef vector<PII>VII;
template<typename T>void rd(T&x){int f=0,c;while(!isdigit(c=getchar()))f^=!(c^45);x=(c&15);while(isdigit(c=getchar()))x=x*10+(c&15);if(f)x=-x;}
template<typename T>void pt(T x,int c=-1){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);if(c!=-1)putchar(c);}
const int N=100005,INF=0X3F3F3F3F;
int TT,n,x,a[N],lim[N];
struct cmp{bool operator()(const int&k1,const int&k2)const{return a[k1]^a[k2]?a[k1]<a[k2]:k1>k2;}};
set<int,cmp>S;
set<int>T;
signed main(){
rd(TT);
while(TT--){
S.clear(),T.clear();
rd(n);rd(x);
rep(i,1,n)rd(a[i]);
sort(a+1,a+1+n);
rep(i,1,n)lim[i]=a[i],S.insert(i);
sort(a+1,a+1+n);
int ans=0;
while(SZ(S)){
++ans;
a[n+1]=(x+1)/2;
auto tmp=S.lower_bound(n+1);
if(tmp==S.end())--tmp;
else{
if(a[*tmp]>x)tmp=--S.end();
}
int i=*tmp;
// printf(">>> %lld %lld:",i,x);
// rep(i,1,n){
// printf("%lld ",a[i]);
// }
// puts("");
if(a[i]<=x){
S.erase(i);
T.erase(i);
x=a[i]*2;
a[i]=0;
}else{
S.erase(i);
a[i]-=x;
S.insert(i);
T.insert(i);
x*=2;
}
for(auto it=T.begin();it!=T.end();){
S.erase(*it);
a[*it]=min(a[*it]*2,lim[*it]);
S.insert(*it);
if(a[*it]<0||a[*it]==lim[*it]){
T.erase(it++);
}else{
++it;
}
}
}
pt(ans,'\n');
}
return 0;
}
DRGNDEN
用线段树正着,反着维护区间从左端点开始到右端点及其右边终止的权值和,update 的时候稍微操作一下,是个经典的套路
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
typedef long long LL;
const int N=200005;
int n,Q,h[N],a[N],mxx;LL res;
struct SEG{
int pd,mx[N*4];LL sum[N*4];
void upd1(int k1){
mx[k1]=max(mx[k1*2],mx[k1*2+1]);
}
LL qry(int k1,int k2,int k3,int k4){
if(mx[k1]<=k4)return 0;
int al=pd?h[n-k2+1]:h[k2];
if(al>k4)return sum[k1];
if(k2==k3)return 0;
int mid=(k2+k3)>>1;
if(mx[k1*2]<=k4)return qry(k1*2+1,mid+1,k3,k4);
else return qry(k1*2,k2,mid,k4)+sum[k1]-sum[k1*2];
}
void bud(int k1,int k2,int k3){
if(k2==k3){
if(pd){
mx[k1]=h[n-k2+1];
sum[k1]=a[n-k2+1];
}else{
mx[k1]=h[k2];
sum[k1]=a[k2];
}
return;
}
int mid=(k2+k3)>>1;
bud(k1*2,k2,mid),bud(k1*2+1,mid+1,k3);
upd1(k1);
sum[k1]=sum[k1*2]+qry(k1*2+1,mid+1,k3,mx[k1*2]);
}
void mdf(int k1,int k2,int k3,int k4){
if(k2==k3){
if(pd){
mx[k1]=h[n-k2+1];
sum[k1]=a[n-k2+1];
}else{
mx[k1]=h[k2];
sum[k1]=a[k2];
}
return;
}
int mid=(k2+k3)>>1;
if(k4<=mid)mdf(k1*2,k2,mid,k4);else mdf(k1*2+1,mid+1,k3,k4);
upd1(k1);
sum[k1]=sum[k1*2]+qry(k1*2+1,mid+1,k3,mx[k1*2]);
}
void ask(int k1,int k2,int k3,int k4,int k5){
if(k2>k5||k3<k4)return;
if(k4<=k2&&k3<=k5){
res+=qry(k1,k2,k3,mxx);
mxx=max(mxx,mx[k1]);
return;
}
int mid=(k2+k3)>>1;
ask(k1*2,k2,mid,k4,k5),ask(k1*2+1,mid+1,k3,k4,k5);
}
}A,B;
int main(){
scanf("%d%d",&n,&Q);
rep(i,1,n)scanf("%d",&h[i]);
rep(i,1,n)scanf("%d",&a[i]);
B.pd=1;
A.bud(1,1,n),B.bud(1,1,n);
while(Q--){
int k1,k2,k3;scanf("%d%d%d",&k1,&k2,&k3);
if(k1==1){
a[k2]=k3;
A.mdf(1,1,n,k2);
B.mdf(1,1,n,n-k2+1);
}else{
if(k2<k3){
mxx=0,res=0;
B.ask(1,1,n,n-k3+1,n-(k2+1)+1);
if(h[k2]>mxx){
res+=a[k2];
}else res=-1;
printf("%lld\n",res);
}else if(k2>k3){
mxx=0,res=0;
A.ask(1,1,n,k3,k2-1);
if(h[k2]>mxx){
res+=a[k2];
}else res=-1;
printf("%lld\n",res);
}else{
printf("%d\n",a[k2]);
}
}
}
return 0;
}
LCMCONST
这题拿了一血,好开心啊。
考虑把质数分开来考虑,对于每一个质数,发现只有次数相同的连通块是不确定的,对于次数相同的连通块,折半搜索,fwt 算贡献
#include<bits/stdc++.h>
#define D(...) fprintf(stderr,__VA_ARGS__)
//#define int long long
#define LL long long
#define MP make_pair
#define PB push_back
#define fi first
#define se second
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef pair<int,int>PII;typedef vector<int>VI;typedef vector<PII>VII;
template<typename T>void rd(T&x){int f=0,c;while(!isdigit(c=getchar()))f^=!(c^45);x=(c&15);while(isdigit(c=getchar()))x=x*10+(c&15);if(f)x=-x;}
template<typename T>void pt(T x,int c=-1){if(x<0)putchar('-'),x=-x;if(x>9)pt(x/10);putchar(x%10+48);if(c!=-1)putchar(c);}
template<typename T>void umin(T&x,const T&y){if(y<x)x=y;}
const int M=10005,P=1000000007;
int T,n,m,len,p[7],X[M],Y[M];
bool used[40];
struct Num{
int e[7];bool bad;
void clear(){memset(e,0,sizeof(e)),bad=0;}
bool operator!=(const Num&b)const{
rep(i,1,len)if(e[i]!=b.e[i])return 1;
return 0;
}
void push(int k1,int k2){
int pid=-1;
rep(k,1,len)if(p[k]==k1)pid=k;
if(pid==-1)p[pid=++len]=k1,assert(len<=5);
e[pid]+=k2;
}
void umin(const Num&b){
if(bad)*this=b;
else{
rep(i,1,len)::umin(e[i],b.e[i]);
}
}
}A[M],mx[M],G[40][40];
namespace xay5421{
int num[40],pid;
int f[(1<<19)+5],lim;
int in1[40],in2[40];
VI nd;
void dfs(int k1){
num[k1]=114514,nd.PB(k1);
rep(j,1,n)if(G[k1][j].bad==0&&mx[k1].e[pid]==mx[j].e[pid]&&num[j]==-1){
dfs(j);
}
}
void fwt_and(int*a,int lim){
for(int i=1;i<lim;i<<=1)for(int j=0;j<lim;j+=i<<1)rep(k,0,i-1){
a[j+k]+=a[i+j+k];
if(a[j+k]>=P)a[j+k]-=P;
}
}
int fun(int pid){
xay5421::pid=pid;
memset(in1,-1,sizeof(in1));
memset(in2,-1,sizeof(in2));
memset(num,-1,sizeof(num));
rep(i,1,m){
if(mx[X[i]].e[pid]<mx[Y[i]].e[pid]){
num[Y[i]]=A[i].e[pid];
}else if(mx[X[i]].e[pid]>mx[Y[i]].e[pid]){
num[X[i]]=A[i].e[pid];
}
}
nd.clear();
int res=1;
rep(k1,1,n)if(num[k1]==-1){
dfs(k1);
int mid=SZ(nd)/2,now=mx[k1].e[pid];// [0,mid),[mid,SZ(nd))
rep(i,0,mid-1)in1[nd[i]]=i;
rep(i,mid,SZ(nd)-1)in2[nd[i]]=i-mid;
VI eid1,eid2,eid3;
rep(i,1,m){
if(in1[X[i]]!=-1&&in1[Y[i]]!=-1)eid1.PB(i);else
if(in2[X[i]]!=-1&&in2[Y[i]]!=-1)eid2.PB(i);else
if((in1[X[i]]!=-1&&in2[Y[i]]!=-1)||(in2[X[i]]!=-1&&in1[Y[i]]!=-1))eid3.PB(i);
}
auto chk1=[&](int s)->bool{
for(auto i:eid1){
if(!((s>>in1[X[i]]&1)|(s>>in1[Y[i]]&1))){
return 0;
}
}
return 1;
};
auto chk2=[&](int s)->bool{
for(auto i:eid2){
if(!((s>>in2[X[i]]&1)|(s>>in2[Y[i]]&1))){
return 0;
}
}
return 1;
};
rep(i,0,(1<<mid)-1){
if(chk1(i)){
f[i]=1;
rep(j,0,mid-1)if(~i>>j&1)f[i]=1LL*f[i]*now%P;
}else f[i]=0;
}
fwt_and(f,1<<mid);
int sz=SZ(nd)-mid;
int cur=0;
rep(s,0,(1<<sz)-1){
if(chk2(s)){
int t=0;
for(auto i:eid3){
int k1=X[i],k2=Y[i];
if(in1[k1]==-1)swap(k1,k2);
if(~s>>in2[k2]&1){
if(~t>>in1[k1]&1)t^=1<<in1[k1];
}
}
int x=f[t];
rep(j,0,sz-1)if(~s>>j&1)x=1LL*x*now%P;
(cur+=x)%=P;
}
}
res=1LL*res*cur%P;
rep(i,0,mid-1)in1[nd[i]]=-1;
rep(i,mid,SZ(nd)-1)in2[nd[i]]=-1;
}
return res;
}
}
signed main(){
rd(T);
int ans=1;
while(T--){
rd(n),rd(m);len=0;
rep(i,1,n)mx[i].bad=1;
rep(i,1,n)rep(j,1,n)G[i][j].bad=1;
bool flg=0;
rep(i,1,m){
A[i].clear();
rd(X[i]),rd(Y[i]);
if(X[i]>Y[i])swap(X[i],Y[i]);
int tmp;rd(tmp);
while(tmp--){
int k1,k2;rd(k1),rd(k2);
A[i].push(k1,k2);
}
mx[X[i]].umin(A[i]);
mx[Y[i]].umin(A[i]);
if(G[X[i]][Y[i]].bad==1)G[X[i]][Y[i]]=G[Y[i]][X[i]]=A[i];
else if(G[X[i]][Y[i]]!=A[i])flg=1;
}
rep(i,1,m)rep(j,1,len)if(mx[X[i]].e[j]!=A[i].e[j]&&mx[Y[i]].e[j]!=A[i].e[j])flg=1;
if(flg){puts("0");goto GG;}
rep(i,1,n)if(mx[i].bad==1){puts("-1");goto GG;}
ans=1;
rep(i,1,len)ans=1LL*ans*xay5421::fun(i)%P;
printf("%d\n",ans);
GG:;
}
return 0;
}
WEIRDMUL
发现这个 W 的计算和哈希很像,先用哈希的方法搞出后缀和,把 W(i,j) 转化为 sum[i]-sum[j+1]*pw[j-i+1],然后再稍加处理,把 W(i,j) 乘上 pw[i],这个乘的次数是可以算的,到最后除掉即可,于是 W(i,j) 变成了 sum[i]*pw[i]-sum[j+1]*pw[j+1],然后令 a[i]=sum[i]*pw[i],我们要求的
由于原来求的式子里有个平方,所以可以变成更友好的形式
我们令
我们要求的是所有的 i 的 \(F_i(a[i])\),然后乘积一下
发现这个过程和多项式多点求值十分相似,魔改一下板子就行了,我写的常数有点大,要卡卡
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define SZ(x) ((int)(x).size())
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
#define D(...) fprintf(stderr,__VA_ARGS__)
//#define D(...) ((void)0)
template<typename T>void rd(T&x){int f=0,c;while(!isdigit(c=getchar()))f^=!(c^45);x=(c&15);while(isdigit(c=getchar()))x=x*10+(c&15);if(f)x=-x;}
const int P=998244353;typedef vector<int>poly;
vector<poly>p;poly ans;poly a;
inline int fpow(int a,int b){int res=1;for(;b;b>>=1,a=1LL*a*a%P)if(b&1)res=1LL*res*a%P;return res;}inline int add(int x,int y){x+=y;if(x>=P)x-=P;return x;}inline int sub(int x,int y){x-=y;if(x<0)x+=P;return x;}inline int mul(int x,int y){return 1LL*x*y%P;}
//void print(const poly&a,const string s=""){
// cerr<<s<<":";rep(i,0,SZ(a)-1)D("%d ",a[i]);D("\n");
//}
namespace NTT{
int base=1,root=-1,maxbase=-1;std::vector<int>roots={0,1},rev={0,1};
void init(){int tmp=P-1;maxbase=0;while(!(tmp&1)){tmp>>=1,maxbase++;}root=2;while(1){if(fpow(root,1<<maxbase)==1&&fpow(root,1<<(maxbase-1))!=1)break;root++;}}
void ensure_base(int nbase){if(maxbase==-1)init();if(nbase<=base)return;rev.resize(1<<nbase);for(int i=1;i<(1<<nbase);++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(nbase-1));roots.resize(1<<nbase);while(base<nbase){int z=fpow(root,1<<(maxbase-base-1));for(int i=(1<<(base-1));i<(1<<base);++i)roots[i<<1]=roots[i],roots[i<<1|1]=mul(roots[i],z);base++;}}
void dft(std::vector<int>&a){int n=a.size(),zeros=__builtin_ctz(n);ensure_base(zeros);int shift=base-zeros;for(int i=0;i<n;++i)if(i<(rev[i]>>shift))std::swap(a[i],a[rev[i]>>shift]);for(int mid=1;mid<n;mid<<=1)for(int i=0;i<n;i+=(mid<<1))for(int j=0;j<mid;++j){int x=a[i+j],y=mul(a[i+j+mid],roots[mid+j]);a[i+j]=add(x,y);a[i+j+mid]=sub(x,y);}}
std::vector<int>pmul(std::vector<int>a,std::vector<int>b){int need=a.size()+b.size()-1,nbase=0;while((1<<nbase)<need)nbase++;ensure_base(nbase);int size=1<<nbase;a.resize(size);b.resize(size);dft(a);dft(b);int inv=fpow(size,P-2);for(int i=0;i<size;++i)a[i]=mul(a[i],mul(b[i],inv));std::reverse(a.begin()+1,a.end());dft(a);a.resize(need);return a;}
}
using NTT::pmul;
inline void ntt(poly&a,int g,int lim){
a.resize(lim);
for(int i=0,j=0;i<lim;++i){if(i>j)swap(a[i],a[j]);for(int k=lim>>1;(j^=k)<k;k>>=1);}
static poly w;w.resize(lim>>1);
for(int i=1;i<lim;i<<=1){
for(int j=w[0]=1,wn=fpow(g,(P-1)/(i<<1));j<i;++j)w[j]=1ll*w[j-1]*wn%P;
for(int j=0;j<lim;j+=i<<1){
for(int k=0;k<i;++k){
int x=a[j+k],y=1ll*a[i+j+k]*w[k]%P;
a[j+k]=(x+y)%P,a[i+j+k]=(x-y+P)%P;
}
}
}
if(g==332748118)for(int i=0,inv=fpow(lim,P-2);i<lim;++i)a[i]=1ll*a[i]*inv%P;
}
inline int getlim(int x){int n=1;while(n<=x)n<<=1;return n;}
inline poly pinv(const poly&a,int n=-1){
if(n==-1)n=a.size();if(n==1)return poly(1,fpow(a[0],P-2));
poly ans(pinv(a,(n+1)>>1)),tmp(&a[0],&a[0]+n);
int lim=getlim(n*2-2);
ntt(ans,3,lim),ntt(tmp,3,lim);
for(int i=0;i<lim;++i)ans[i]=1ll*ans[i]*(2-1ll*ans[i]*tmp[i]%P+P)%P;
ntt(ans,332748118,lim);
return ans.resize(n),ans;
}
inline void pdiv(const poly&a,const poly&b,poly&d,poly&r){
if(b.size()>a.size())return d.clear(),r=a,void();
poly A(a),B(b),invB;int n=a.size(),m=b.size();
reverse(A.begin(),A.end()),reverse(B.begin(),B.end());
B.resize(n-m+1),invB=pinv(B,n-m+1);
d=pmul(A,invB),d.resize(n-m+1),reverse(d.begin(),d.end());
r=pmul(b,d);for(int i=0;i<m-1;++i)r[i]=(a[i]-r[i]+P)%P;r.resize(m-1);
}
inline void evaluate_init(int u,int l,int r){
if(l==r)return p[u]=(poly){P-a[l],1},void();int mid=(l+r)>>1;
evaluate_init(u<<1,l,mid),evaluate_init(u<<1|1,mid+1,r);
p[u]=pmul(p[u<<1],p[u<<1|1]);
}
poly A[100];
inline void evaluate(int u,int l,int r,const poly&f,int dep){
if(r-l+1<=512){
// D("%d %d\n",l,r);
for(int i=l;i<=r;++i){
int x=0;
for(int j=SZ(f)-1;j>=0;--j)x=(1ll*x*a[i]%P+f[j])%P;
rep(j,l,r)if(j!=i)x=1ll*x*(a[i]-a[j])%P;
ans[i]=(x+P)%P;
}
return;
}
poly tmp;
A[dep]=pmul(f,p[u<<1]);
pdiv(A[dep],p[u<<1|1],tmp,tmp);
int mid=(l+r)>>1;
evaluate(u<<1|1,mid+1,r,tmp,dep+1);
A[dep]=pmul(f,p[u<<1|1]);
pdiv(A[dep],p[u<<1],tmp,tmp);
evaluate(u<<1,l,mid,tmp,dep+1);
}
inline void evaluate(){
p.resize(SZ(a)<<2),evaluate_init(1,0,SZ(a)-1);
ans.resize(SZ(a)),evaluate(1,0,SZ(a)-1,{1},1);
}
int T,n,X;
int main(){
// freopen("a.in","r",stdin);
rd(T);
while(T--){
rd(n),rd(X);
a.resize(n+1);a[n]=0;
rep(i,0,n-1)rd(a[i]);
per(i,n-1,0)a[i]=(1LL*a[i+1]*X+a[i])%P;
int pw=1;
rep(i,0,n-1){
a[i]=1LL*a[i]*pw%P;
pw=1LL*pw*X%P;
}
int res=1,tmp=0;
sort(a.begin(),a.end());
rep(i,0,n-1)if(a[i]==a[i+1]){puts("0");goto GG;}
evaluate();
rep(i,0,SZ(ans)-1)res=1LL*res*ans[i]%P;
rep(i,0,n-1)(tmp+=1LL*i*(n-i)%(P-1))%=P-1;
res=1LL*res*fpow(fpow(X,P-2),tmp*2)%P;
if((1LL*n*(n+1)/2)&1)res=-res;
printf("%d\n",(res+P)%P);GG:;
}
return 0;
}
EXPREP
在末尾加上一个特殊字符,观察一下发现,要求的其实是 \(\sum_{1<=i<=n,i<=j<=n+1}(sum[j-1]-sum[i-1])(lcp(i,j)+1)\)
然后因为后缀自动机上的 lca 就是两个串的 lcs,所以在倒串上搞搞,具体的,其实是求每个状态的 endpos 集合两两 sum 的差,SAM 上线段树合并一下
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
const int N=1000005,M=N*40,P=998244353;
int T,n,wei[26],sum[N];char s[N];
int fpow(int k1,int k2){
int k3=1;
for(;k2;k2>>=1,k1=1LL*k1*k1%P)if(k2&1)k3=1LL*k3*k1%P;
return k3;
}
struct SAM{
int lst,cnt,fa[N],ch[N][27],len[N],pos[N],c[N],a[N],ans[N];
int ind,rt[N],lc[M],rc[M],val[M],tot[M];
void clear(){
rep(i,1,cnt){
fa[i]=0,memset(ch[i],0,sizeof(ch[i]));
len[i]=pos[i]=c[i]=a[i]=ans[i]=rt[i]=0;
}
rep(i,1,ind){
lc[i]=rc[i]=val[i]=tot[i]=0;
}
lst=cnt=1,len[1]=1;//
ind=0;
}
void push(int c,int _pos){
int p=lst,np=lst=++cnt;len[np]=len[p]+1,pos[np]=_pos;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p){fa[np]=1;return;}
int q=ch[p][c];
if(len[p]+1==len[q]){fa[np]=q;return;}
int nq=++cnt;len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q],fa[q]=fa[np]=nq;
for(;p&&ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
void mdf(int&k1,int k2,int k3,int k4,int&res){
if(!k1)k1=++ind;
++tot[k1],(val[k1]+=sum[k4])%=P;
if(k2==k3)return;
int mid=(k2+k3)>>1;
if(k4<=mid){
(res+=val[rc[k1]]-1LL*tot[rc[k1]]*sum[k4]%P+P)%=P;
mdf(lc[k1],k2,mid,k4,res);
}else{
(res+=1LL*tot[lc[k1]]*sum[k4]%P-val[lc[k1]]+P)%=P;
mdf(rc[k1],mid+1,k3,k4,res);
}
}
void mer(int&k1,int k2,int l,int r,int&res){
if(!k2)return;
if(!k1){
k1=k2;
return;
}
if(l==r){
return;
}
int mid=(l+r)>>1;
// assert(res>=0);
(res+=(1LL*val[rc[k1]]*tot[lc[k2]]%P-1LL*tot[rc[k1]]*val[lc[k2]]%P+P)%P)%=P;
(res+=(1LL*tot[lc[k1]]*val[rc[k2]]%P-1LL*val[lc[k1]]*tot[rc[k2]]%P+P)%P)%=P;
// assert(res>=0);
mer(lc[k1],lc[k2],l,mid,res);
mer(rc[k1],rc[k2],mid+1,r,res);
val[k1]=(val[lc[k1]]+val[rc[k1]])%P;
tot[k1]=(tot[lc[k1]]+tot[rc[k1]])%P;
}
void sol(){
rep(i,1,cnt)++c[len[i]];
rep(i,1,cnt)c[i]+=c[i-1];
rep(i,1,cnt)a[c[len[i]]--]=i;
int res=0;
per(i,cnt,1){
int k1=a[i];
if(pos[k1]){
mdf(rt[k1],1,cnt,pos[k1],ans[k1]);
}
if(k1!=1){
mer(rt[fa[k1]],rt[k1],1,cnt,ans[fa[k1]]);
}
// printf("%d %d %d\n",k1,pos[k1],(P-ans[k1])%P);
(res+=1LL*ans[k1]*(len[k1]-/*len[fa[k1]]*/0)%P)%=P;
}
printf("%lld\n",1LL*(P-res)*fpow(1LL*n*(n-1)/2%P,P-2)%P);
}
}A;
int main(){
// freopen("a.in","r",stdin);
// freopen("d.out","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%s",s+1);n=strlen(s+1),s[++n]='z'+1;
rep(i,0,25)scanf("%d",&wei[i]);
rep(i,1,n)sum[i]=(sum[i-1]+wei[s[i]-'a'])%P;
per(i,n,1)sum[i]=sum[i-1];
reverse(s+1,s+1+n);
reverse(sum+1,sum+1+n);
A.clear();
rep(i,1,n)A.push(s[i]-'a',i);
A.sol();
}
return 0;
}
EXPTREES
是个有趣的题目,我们首先可以求出 \(f_i\) 表示有 i 条初始边的生成树的个数,然后由于初始边数相同,期望也是一样的,可以算一下贡献
对于求 f 的过程,是个矩阵树定理,不过稍稍修改,变成多项式的运算,但是这样复杂度略大,考虑带入插值进去,然后高消求解系数
对于算有 i 条边的期望的过程,考虑其指数型生成函数,是
然后把这个东西的 e^i 的系数预处理出来,这样每次询问的复杂度就是 O(n) 了
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include<bits/stdc++.h>
#define D(...) fprintf(stderr,__VA_ARGS__)
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
using namespace std;
typedef long long LL;
const int N=105,P=1e9+7,I2=(P+1)/2;
int n,m,q,ans[N],f[N*2],g[N*2],h[N*2];bool G[N][N];
int fpow(int k1,LL k2){
int k3=1;for(;k2;k2>>=1,k1=1LL*k1*k1%P)if(k2&1)k3=1LL*k3*k1%P;return k3;
}
namespace xay5421{
int A[N][N],B[N][N];
int det(){
int res=1;
rep(i,1,n-1){
int pos=-1;
rep(j,i,n-1)if(A[i][j]){pos=j;break;}
if(pos==-1)return 0;
swap(A[i],A[pos]),res=-res;
int I=fpow(A[i][i],P-2);
rep(j,1,n-1)if(i!=j&&A[j][i]){
int t=1LL*A[j][i]*I%P;
rep(k,1,n)(A[j][k]-=1LL*A[i][k]*t%P)%=P;
}
res=1LL*res*A[i][i]%P;
}
return res;
}
void sol(){
rep(x,1,n){
rep(i,1,n)rep(j,1,n)A[i][j]=0;
rep(i,1,n)rep(j,1,n)if(i!=j){
if(G[i][j]){
(A[i][i]-=x)%=P;
(A[i][j]+=x)%=P;
}else{
(A[i][i]-=1)%=P;
(A[i][j]+=1)%=P;
}
}
B[x][1]=1;
rep(i,2,n){
B[x][i]=1LL*B[x][i-1]*x%P;
}
B[x][n+1]=det();
}
rep(i,1,n){
int pos=-1;
rep(j,i,n)if(B[i][j]){pos=j;break;}
assert(pos!=-1);
swap(B[i],B[pos]);
int I=fpow(B[i][i],P-2);
rep(j,1,n)if(i!=j&&B[j][i]){
int t=1LL*B[j][i]*I%P;
rep(k,1,n+1)(B[j][k]-=1LL*B[i][k]*t%P)%=P;
}
}
rep(i,1,n)ans[i-1]=1LL*B[i][n+1]*fpow(B[i][i],P-2)%P,(ans[i-1]+=P)%=P;
}
}
int main(){
scanf("%d%d%d",&n,&m,&q);
rep(i,1,m){
int k1,k2;scanf("%d%d",&k1,&k2);
G[k1][k2]=G[k2][k1]=1;
}
xay5421::sol();
rep(a,0,n-1)if(ans[a]){
memset(f,0,sizeof(f));
f[0+N]=1;
rep(_,1,a){
memcpy(g,f,sizeof(g));
memset(f,0,sizeof(g));
rep(i,-n+1,n-1){
(f[i-1+N]+=1LL*g[i+N]*I2%P)%=P;
(f[i+1+N]+=1LL*g[i+N]*I2%P)%=P;
}
}
rep(_,1,n-1-a){
memcpy(g,f,sizeof(g));
memset(f,0,sizeof(f));
rep(i,-n+1,n-1){
(f[i-1+N]+=P-1LL*g[i+N]*I2%P)%=P;
(f[i+1+N]+=1LL*g[i+N]*I2%P)%=P;
}
}
rep(i,-n+1,n-1){
f[i+N]=1LL*f[i+N]*ans[a]%P;
(h[i+N]+=f[i+N])%=P;
}
}
const int K=n*(n-1)/2-n+1,KK=1LL*n*(n-1)/2;
while(q--){
LL T;scanf("%lld",&T);
int res=0;
rep(i,-n+1,n-1){
(res+=1LL*h[i+N]*fpow((i+K+P)%P,T%(P-1))%P)%=P;
}
res=1LL*res*fpow(KK,1LL*T%(P-1)*(P-2)%(P-1))%P;
printf("%d\n",res);
}
return 0;
}
本文来自博客园,作者:xay5421,转载请注明原文链接:https://www.cnblogs.com/xay5421/p/CCJULY20.html