A. Donut
掃描線+線段樹。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=800010,M=2222222; int n,m,cnt,i,j;ll L,R,D,a[N]; int tag[M],v[M],ans; struct E{ ll x,l,r;int s; E(){} E(ll _x,ll _l,ll _r,int _s){x=_x,l=_l,r=_r,s=_s;} }e[N]; inline bool cmp(const E&a,const E&b){return a.x<b.x;} inline void tag1(int x,int p){tag[x]+=p;v[x]+=p;} void add(int x,int a,int b,int c,int d,int p){ if(c<=a&&b<=d){ tag1(x,p); return; } if(tag[x])tag1(x<<1,tag[x]),tag1(x<<1|1,tag[x]),tag[x]=0; int mid=(a+b)>>1; if(c<=mid)add(x<<1,a,mid,c,d,p); if(d>mid)add(x<<1|1,mid+1,b,c,d,p); v[x]=max(v[x<<1],v[x<<1|1]); } int main(){ scanf("%d%lld%lld",&n,&L,&R);L--; while(n--){ ll x,y; int s; scanf("%lld%lld%d",&x,&y,&s); e[++m]=E(x-R,y-R,y+R,s); e[++m]=E(x+R+1,y-R,y+R,-s); e[++m]=E(x-L,y-L,y+L,-s); e[++m]=E(x+L+1,y-L,y+L,s); } for(i=1;i<=m;i++)a[++cnt]=e[i].l,a[++cnt]=e[i].r; sort(a+1,a+cnt+1); sort(e+1,e+m+1,cmp); for(i=1;i<=m;i=j){ for(j=i;j<=m&&e[i].x==e[j].x;j++)add(1,1,cnt,lower_bound(a+1,a+cnt+1,e[j].l)-a,lower_bound(a+1,a+cnt+1,e[j].r)-a,e[j].s); ans=max(ans,v[1]); } printf("%d",ans); }
B. Circular Arrangement
留坑。
C. Earthquake
對於一條路徑內部來說,最優策略肯定是從存在概率最小的開始詢問。
對於不同路徑之間來說,考慮排序不等式貪心即可。
#include<stdio.h> #include<iostream> #include<string.h> #include<string> #include<ctype.h> #include<math.h> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre() { } #define MS(x, y) memset(x, y, sizeof(x)) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; } template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; } const int N = 1010, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f; template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; } int casenum, casei; int n; double a[N]; long double win[N], fail[N]; struct A { long double failp; long double step; bool operator < (const A & b) const { long double afirst = step + failp * b.step; long double bfirst = b.step + b.failp * step; return afirst < bfirst; } }v[N]; int main() { while(~scanf("%d",&n)) { for(int i = 1; i <= n; ++i) { int g; scanf("%d", &g); for(int j = 1; j <= g; ++j) { scanf("%lf", &a[j]); a[j] /= 1000; } sort(a + 1, a + g + 1); win[0] = 1; fail[0] = 0; v[i].step = 0; for(int j = 1; j <= g; ++j) { win[j] = win[j - 1] * a[j]; fail[j] = fail[j - 1] + win[j - 1] * (1 - a[j]); v[i].step += win[j - 1] * (1 - a[j]) * j; } v[i].step += win[g] * g; v[i].failp = fail[g]; } sort(v + 1, v + n + 1); long double ans = 0; long double p = 1; for(int i = 1; i <= n; ++i) { ans += p * v[i].step; p *= v[i].failp; } printf("%.12f\n", (double)ans); } return 0; } /* 【trick&&吐槽】 2 3 900 900 900 2 100 100 3 1 240 1 310 1 50 【題意】 【分析】 【時間複雜度&&優化】 */
D. Dynamic Input Tool
貪心,若不是子序列則進行一次操作。
#include<cstdio> #include<cstring> const int N=1000010; int n,i,j,f[N][26]; int l,r,x,ans; int vis[26]; char a[N]; int main(){ scanf("%s",a+1); n=strlen(a+1); for(i=1;i<=n;i++)a[i]-='a'; for(j=0;j<26;j++)f[n+1][j]=n+1; for(i=n;i;i--){ for(j=0;j<26;j++)f[i][j]=f[i+1][j]; f[i][a[i]]=i; } for(i=1;i<=n;i++){ if(!vis[a[i]]){ if(l&&l<i)ans++; vis[a[i]]=1; ans++; l=i+1; x=1; }else{ x=f[x][a[i]]; if(x>=l){ ans++; x=f[1][a[i]]; l=i; } x++; } } if(l<=n)ans++; printf("%d",ans); }
E. Central Lake
答案即為圓周上距離最遠的兩個點的距離,求出兩個點後可以求切線得出答案。
對於求最遠點對,可以使用set支援插入操作,對於刪除則按時間分治即可。
時間複雜度$O(n\log^2n)$。
#define ms(x, y) memset(x, y, sizeof(x)) #define mc(x, y) memcpy(x, y, sizeof(x)) #define mid (l+r>>1) #define lson o << 1, l, mid #define rson o << 1 | 1, mid + 1, r #define ls o << 1 #define rs o << 1 | 1 #define rt 1,1,Q+1 #include<stdio.h> #include<string.h> #include<math.h> #include<queue> #include<map> #include<stack> #include<vector> #include<list> #include<set> #include<string> #include<algorithm> #pragma comment(linker,"/STACK:102400000,102400000") template <class T> inline void gmax(T &a, T b){if(b > a) a = b;} template <class T> inline void gmin(T &a, T b){if(b < a) a = b;} using namespace std; const int N = 4e5 + 10, M = 1e6 + 10, Z = 1e9 + 7, maxint = 2147483647, ms1 = 16843009, ms31 = 522133279, ms63 = 1061109567, ms127 = 2139062143; const double PI = acos(-1.0), eps = 1e-8; double R, r; int n; int LL, RR, X; vector<int> w[N], v[N * 4]; int a[N]; /* const int BS = 360000; struct point{ long double x, y, z; point(){} point (long double x_,long double y_,long double z_=1.0){x=x_,y=y_,z=z_;} point operator -(const point &p)const{ return {x-p.x,y-p.y}; } point operator +(const point &p)const{ return {x+p.x,y+p.y}; } point operator *(const long double &k)const{ return {x*k,y*k}; } long double Norm(){ return sqrtl(x*x+y*y); } point Rotate(const long double &AA)const{ long double c = cosl(AA); long double s = sinl(AA); return {c*x-s*y, x*s+c*y}; } }O; point Seg(point a, point b){ return {b.y-a.y,a.x-b.x,-a.x*b.y+a.y*b.x}; } long double val(int d){ long double AA = (long double)d/BS * 2*PI; point p = {(long double)R, (long double)0}; point q = {R*cosl(AA), R*sinl(AA)}; point S = Seg(p,q); long double dis = S.z/sqrtl(S.x*S.x+S.y*S.y); if(dis<0)dis=-dis; if(dis > r){ return (q-p).Norm(); } long double BB = asinl((long double)r/R); point v1 = (O-q).Rotate(BB); long double dd = sqrtl((long double)R*R-(long double)r*r); v1 = q + v1*((long double)1.0/v1.Norm()*dd); point v2 = (O-p).Rotate(-BB); v2 = p + v2*((long double)1.0/v2.Norm()*dd); return (q-v1).Norm() + (p-v2).Norm() + (atan2l(v1.y,v1.x) - atan2l(v2.y,v2.x))*r; } */ struct Point { double x, y; Point(double x_, double y_){ x = x_; y = y_; } friend Point operator - (const Point &a, const Point &b){ return Point(a.x - b.x, a.y - b.y); } friend Point operator + (const Point &a, const Point &b){ return Point(a.x + b.x, a.y + b.y); } friend Point operator * (const Point &a, const double &b){ return Point(a.x * b, a.y * b); } friend Point operator / (const Point &a, const double &b){ return Point(a.x / b, a.y / b); } double len2(){ return x * x + y * y; } double len() { return sqrt(len2()); } Point rotate(const double ang){ return Point(cos(ang) * x - sin(ang) * y, cos(ang) * y + sin(ang) * x); } Point turn90(){ return Point(-y, x); } }; struct Circle { Point o; double r; Circle(Point o = Point(0, 0), double r = 0) : o(o), r(r) {} }; vector<Point> circleTangentPoint(const Circle &c, const Point &p0) { double x = (p0 - c.o).len2(), r = c.r; double d = x - r * r; vector<Point> ret; if(d < -eps){ return ret; } if(d < 0){ d = 0; } Point p = (p0 - c.o) * (r * r / x), delta = ((p0 - c.o) * (-r * sqrt(d) / x)).turn90(); ret.push_back(c.o + p + delta); ret.push_back(c.o + p - delta); return ret; } double TH(double x) { return x * PI / 180; } int sgn(double x) { if(fabs(x) < eps) return 0; return x > 0 ? 1 : -1; } double L(Point aa, Point bb) { if(sgn((aa - bb).len2() - 4.0 * r * r) == 0){ return PI * r; } Point cc = {0, 0}; double a = (bb - cc).len(), b = (aa - cc).len(), c = (aa - bb).len(); double cosc = (b * b + a * a - c * c) / (2 * a * b); double th = acos(cosc); return th * r; } double val(int n) { //printf("%d\n", n); Point o = {0, 0}; Point p = {0, -R}; Point pp = p; p = p.rotate(TH(1.0 * n / 1000)); double dist = (pp - p).len(); if(dist <= sqrt(R * R - r * r) * 2){ return dist; } Circle c; c.o = {0, 0}; c.r = r; vector<Point> inter = circleTangentPoint(c, p); vector<Point> origi = circleTangentPoint(c, pp); Point p1 = inter[0], p2 = inter[1], p3 = origi[0], p4 = origi[1]; double ans = min(min(L(p1, p3), L(p1, p4)), min(L(p2, p3), L(p2, p4))); return ans + 2 * (p - p1).len(); } void build(int o, int l, int r) { v[o].clear(); if(l == r)return; build(lson); build(rson); } void update(int o, int l, int r) { if(LL <= l && r <= RR) { v[o].push_back(X); return; } if(LL <= mid)update(lson); if(RR > mid)update(rson); } set<int>sot; const int A = 360000; const int H = 180000; double ans[N]; int minn(int x) { if(x<0)x=-x; x %= A; x += A; x %= A; return min(x, A - x); } void solve(int o, int l, int r, int maxD) { for(auto x : v[o]) { set<int>::iterator it = sot.lower_bound(x + H); if(it != sot.end())gmax(maxD, minn(x + A - *it)); if(it != sot.begin())gmax(maxD, minn(*(--it) - x)); sot.insert(x); sot.insert(x + A); } if(l == r) { ans[l] = val(maxD); } else { solve(lson, maxD); solve(rson, maxD); } for(auto x : v[o]) { sot.erase(x); sot.erase(x + A); } v[o].clear(); } int main() { while(~scanf("%lf%lf", &R, &r)) { scanf("%d", &n); for(int i = 1; i <= n; ++i) { scanf("%d", &a[i]); w[a[i]].push_back(1); } int Q; scanf("%d", &Q); build(rt); for(int i = 1; i <= Q; ++i) { int op, x; scanf("%d%d", &op, &x); w[x].push_back(i + 1); } for(int i = 0; i < 360000; ++i) { X = i; for(int j = 0; j < w[i].size(); j += 2) { LL = w[i][j]; if(j == w[i].size() - 1)RR = Q + 1; else RR = w[i][j + 1] - 1; update(rt); } w[i].clear(); } solve(rt, 0); for(int i = 1; i <= Q + 1; ++i)printf("%.10f\n", ans[i]); } return 0; } /* 題意: 10 5 2 0 90000 4 1 180000 1 240000 2 0 2 90000 型別: 分析: 優化: trick: 資料: Sample Input Sample Output */
F. Computing MDSST
設$f[S][i]$表示$S$集合的點形成一棵樹,根為$i$的最小代價,列舉補集的子集轉移。
時間複雜度$O(3^nn^2)$。
#include<cstdio> typedef long long ll; const int N=15; const ll inf=1LL<<60; int n,i,j,S,U,T,c[1<<N],g[N][N];ll f[1<<N][N]; inline void up(ll&a,ll b){a>b?(a=b):0;} int main(){ scanf("%d",&n); for(i=0;i<n;i++)for(j=i+1;j<n;j++){ scanf("%d",&g[i][j]); g[j][i]=g[i][j]; } for(S=0;S<1<<n;S++)for(i=0;i<n;i++)f[S][i]=inf; for(i=0;i<1<<n;i++)c[i]=__builtin_popcount(i); for(i=0;i<n;i++)f[1<<i][i]=0; for(S=0;S<1<<n;S++)for(i=0;i<n;i++)if(S>>i&1){ for(j=0;j<n;j++)if(j!=i&&(S>>j&1)){ T=S^(1<<i)^(1<<j); for(U=T;;U=(U-1)&T){ up(f[S][i],f[(T-U)|(1<<i)][i]+f[U|(1<<j)][j]+1LL*g[i][j]*(c[U]+1)*(n-c[U]-1)); if(!U)break; } } } for(i=1;i<n;i++)up(f[(1<<n)-1][0],f[(1<<n)-1][i]); printf("%lld",f[(1<<n)-1][0]); }
G. MST with Metropolis
首先求出最小生成樹,那麼對於每個點可以看成加入$deg_i$條邊權為$0$的邊,LCT維護即可。
時間複雜度$O(m\log m)$。
#include<cstdio> #include<algorithm> #include<vector> using namespace std; typedef long long ll; const int N=1000000; struct edge{int x,y,a;}e[N]; bool cmp(const edge&a,const edge&b){return a.a<b.a;} int n,m,i,j,fa[N]; int f[N],son[N][2],val[N],sum[N],from[N],tmp[N];bool rev[N]; vector<int>vg[N]; ll all; int F(int x){return fa[x]==x?x:fa[x]=F(fa[x]);} inline bool isroot(int x){return !f[x]||(son[f[x]][0]!=x&&son[f[x]][1]!=x);} inline void rev1(int x){if(!x)return;swap(son[x][0],son[x][1]);rev[x]^=1;} inline void pb(int x){if(rev[x])rev1(son[x][0]),rev1(son[x][1]),rev[x]=0;} inline void up(int x){ sum[x]=val[x],from[x]=x; if(son[x][0])if(sum[son[x][0]]>sum[x])sum[x]=sum[son[x][0]],from[x]=from[son[x][0]]; if(son[x][1])if(sum[son[x][1]]>sum[x])sum[x]=sum[son[x][1]],from[x]=from[son[x][1]]; } inline void rotate(int x){ int y=f[x],w=son[y][1]==x; son[y][w]=son[x][w^1]; if(son[x][w^1])f[son[x][w^1]]=y; if(f[y]){ int z=f[y]; if(son[z][0]==y)son[z][0]=x; if(son[z][1]==y)son[z][1]=x; } f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y); } inline void splay(int x){ int s=1,i=x,y;tmp[1]=i; while(!isroot(i))tmp[++s]=i=f[i]; while(s)pb(tmp[s--]); while(!isroot(x)){ y=f[x]; if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);} rotate(x); } up(x); } inline void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);} inline void makeroot(int x){access(x);splay(x);rev1(x);} inline void link(int x,int y){makeroot(x);f[x]=y;access(x);} inline void cutf(int x){access(x);splay(x);f[son[x][0]]=0;son[x][0]=0;up(x);} inline void cut(int x,int y){makeroot(x);cutf(y);} inline int askfrom(int x,int y){makeroot(x);access(y);splay(y);return from[y];} inline ll solve(int x){ ll sum=0; vector<int>v; for(int i=0;i<vg[x].size();i++){ int k=vg[x][i]; sum+=e[k].a; int A=x,B=e[k].x+e[k].y-A; int j=askfrom(A,B); sum-=e[j-n].a; v.push_back(j); cut(j,e[j-n].x),cut(j,e[j-n].y); link(A,n+m+k),link(B,n+m+k); } for(int i=0;i<vg[x].size();i++){ int k=vg[x][i]; int A=x,B=e[k].x+e[k].y-A; cut(A,n+m+k),cut(B,n+m+k); } for(int i=0;i<v.size();i++){ int j=v[i]; link(j,e[j-n].x),link(j,e[j-n].y); } return sum+all; } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=m;i++)scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].a); sort(e+1,e+m+1,cmp); for(i=1;i<=n;i++)fa[i]=i,val[i]=-1,from[i]=i; for(i=1;i<=m;i++)val[n+i]=e[i].a,from[n+i]=n+i; for(i=1;i<=m;i++)val[n+m+i]=0,from[n+m+i]=n+m+i; for(i=1;i<=m;i++)if(F(e[i].x)!=F(e[i].y)){ fa[fa[e[i].x]]=fa[e[i].y]; link(e[i].x,n+i); link(e[i].y,n+i); all+=e[i].a; } for(i=1;i<=m;i++)vg[e[i].x].push_back(i),vg[e[i].y].push_back(i); for(i=1;i<=n;i++)printf("%lld\n",solve(i)); }
H. Number of Cycles
留坑。
I. Sum of Squares of the Occurrence Counts
建立字尾自動機,設$w_i=ml_i-ml_{pre_i},v_i$表示$i$點的$right$集合大小,則$ans=\sum w_iv_i^2$。
每次加入一個新點時,需要將該點到根路徑上所有$v$值加$1$,LCT打標記維護即可。
時間複雜度$O(n\log n)$。
#include<cstdio> #include<cstring> #define N 200010 typedef long long ll; char s[N];int n,i;ll ans; int tot=1,last=1,pre[N],son[N][26],ml[N]; namespace LCT{ int f[N],son[N][2],a[N]; ll tag[N]; bool rev[N]; ll w[N],v[N],sw[N],swv[N]; inline void swap(int&a,int&b){int c=a;a=b;b=c;} inline bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;} inline void reverse(int x){if(x)swap(son[x][0],son[x][1]),rev[x]^=1;} inline void add(int x,ll y){ if(x){ v[x]+=y; swv[x]+=sw[x]*y; tag[x]+=y; } } inline void up(int x){ sw[x]=w[x]+sw[son[x][0]]+sw[son[x][1]]; swv[x]=w[x]*v[x]+swv[son[x][0]]+swv[son[x][1]]; } inline void pb(int x){ if(rev[x])reverse(son[x][0]),reverse(son[x][1]),rev[x]=0; if(tag[x])add(son[x][0],tag[x]),add(son[x][1],tag[x]),tag[x]=0; } inline void rotate(int x){ int y=f[x],w=son[y][1]==x; son[y][w]=son[x][w^1]; if(son[x][w^1])f[son[x][w^1]]=y; if(f[y]){ int z=f[y]; if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x; } f[x]=f[y];son[x][w^1]=y;f[y]=x;up(y); } inline void splay(int x){ int s=1,i=x,y;a[1]=i; while(!isroot(i))a[++s]=i=f[i]; while(s)pb(a[s--]); while(!isroot(x)){ y=f[x]; if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);} rotate(x); } up(x); } inline void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);} inline void makeroot(int x){access(x);splay(x);reverse(x);} inline void link(int x,int y){makeroot(x);f[x]=y;access(x);} inline void cutf(int x){access(x);splay(x);f[son[x][0]]=0;son[x][0]=0;} inline void cut(int x,int y){makeroot(x);cutf(y);} inline void changew(int x,ll p){ access(x); splay(x); w[x]=p; up(x); } inline void maketag(int x){ makeroot(1); access(x); splay(x); ans+=2*swv[x]+sw[x]; add(x,1); } inline void copy(int y,int x){ access(x);splay(x); w[y]=w[x]; v[y]=v[x]; up(y); } inline void setv(int x,int p){ w[x]=p; up(x); } } inline void add(int w){ int p=++tot,x=last,r,q; ml[p]=ml[x]+1;last=p; for(;x&&!son[x][w];x=pre[x])son[x][w]=p; if(!x){ pre[p]=1; LCT::setv(p,ml[p]-ml[pre[p]]); LCT::link(p,pre[p]); }else if(ml[x]+1==ml[q=son[x][w]]){ pre[p]=q; LCT::setv(p,ml[p]-ml[pre[p]]); LCT::link(p,pre[p]); }else{ pre[r=++tot]=pre[q]; LCT::copy(r,q); LCT::link(r,pre[q]); memcpy(son[r],son[q],sizeof son[r]); ml[r]=ml[x]+1; LCT::cut(q,pre[q]); LCT::link(q,r); pre[p]=pre[q]=r; LCT::setv(p,ml[p]-ml[pre[p]]); LCT::link(p,r); LCT::changew(r,ml[r]-ml[pre[r]]); LCT::changew(q,ml[q]-ml[pre[q]]); for(;x&&son[x][w]==q;x=pre[x])son[x][w]=r; } LCT::maketag(p); } int main(){ scanf("%s",s+1);n=strlen(s+1); for(i=1;i<=n;i++){ add(s[i]-'a'); printf("%lld\n",ans); } }
J. Game of Sorting
對於一個區間$[l,r]$,若它是單調的,則顯然先手必敗。
若去掉$l$或者$r$後變成了單調的,則顯然先手必勝。
若$[l+1,r-1]$是單調的,那麼同理先手必敗。
若$[l,r-2]$是單調的,那麼先手若取$r$會導致後手必勝,後手同理,故兩人會一直取$l$直到出現上面第一種或者第二種情況,可以根據奇偶性判斷。
同理可以得出$[l+2,r]$是單調的情形的處理方法。
除此之外,有$[l,r]$的答案等於$[l+1,r-1]$的答案,二分找到最小的$k$滿足$[l+k,r-k]$可以由上面方法直接判斷出勝負即可。
時間複雜度$O(m\log n)$。
#include<cstdio> #include<algorithm> using namespace std; const int N=1000010; int n,i,a[N],fl[N],fr[N],gl[N],gr[N],m,x,y; inline int check(int l,int r){ if(l>=r)return -1; if(fr[l]>=r)return -1; if(fr[l]==r-1)return 1; if(fl[r]==l+1)return 1; if(fr[l+1]==r-1)return -1; int x; if(fr[l]==r-2){ if(fl[r-1]<fl[r])x=(l&1)^(fl[r-1]&1)^1; else x=(l&1)^(fl[r]&1); return x?1:-1; } if(fl[r]==l+2){ if(fr[l+1]>fr[l])x=(r&1)^(fr[l+1]&1)^1; else x=(r&1)^(fr[l]&1); return x?1:-1; } return 0; } inline int cal(int x,int y){ int l=0,r=(y-x)/2+5,mid,t,o; while(l<=r){ mid=(l+r)>>1; t=check(x+mid,y-mid); if(!t)l=mid+1;else r=mid-1,o=t; } return o; } int main(){ scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d",&a[i]); for(fr[n]=gr[n]=n,i=n-1;i;i--){ fr[i]=a[i]<=a[i+1]?fr[i+1]:i; gr[i]=a[i]>=a[i+1]?gr[i+1]:i; } for(fl[1]=gl[1]=1,i=2;i<=n;i++){ fl[i]=a[i]<=a[i-1]?fl[i-1]:i; gl[i]=a[i]>=a[i-1]?gl[i-1]:i; } for(i=1;i<=n;i++)fl[i]=min(fl[i],gl[i]),fr[i]=max(fr[i],gr[i]); scanf("%d",&m); while(m--)scanf("%d%d",&x,&y),puts(cal(x,y)>0?"Alice":"Bob"); }
K. Subsequence Queries
設$f_i$表示以字元$i$為結尾的本質不同子序列的個數,$sum$為所有本質不同子序列(包含空串)的個數,則一開始$f_i=0,sum=1$。
若往序列末尾新增一個字元$x$,則有$f_x=sum,sum=2sum-f_x$,可以將其寫成轉移矩陣$G_x$。
考慮將上述式子倒過來,則可以得出刪除末尾字元$x$的轉移,也就是$G_x^{-1}$。
則區間$[l,r]$的答案$=G_rG_{r-1}...G_{l+1}G_l=G_{r+1}^{-1}G_{r+2}^{-1}G_{n-1}^{-1}G_n^{-1}G_nG_{n-1}...G_{l+1}G_l$。
故預處理出$G$的字尾積的最後一列以及$G^{-1}$的字尾積的最後一行即可在$O(S)$的時間裡求出$sum$。
對於預處理,注意到$G$和$G^{-1}$與單位矩陣$E$相比只有$4$個位置不同,故可以只考慮這$4$個位置的線性貢獻在$O(S)$的時間裡完成矩陣乘法。
時間複雜度$O((n+q)S)$。
#include<cstdio> #include<cstring> #include<algorithm> #define rep(i) for(int i=0;i<M;i++) using namespace std; const int N=1000010,M=53,P=1000000007; int n,i,j,g[M][M],w[M][M],tmp[M],b[N][M],c[N][M];char a[N]; inline int getid(char x){ if(x>='a'&&x<='z')return x-'a'; return x-'A'+26; } inline void apply(int x){ //x,x,-1 //x,M-1,1 //M-1,x,-1 //M-1,M-1 1 rep(i)w[i][x]=g[i][x],w[i][M-1]=g[i][M-1]; rep(i){ (g[i][x]-=w[i][x])%=P; (g[i][M-1]+=w[i][x])%=P; (g[i][x]-=w[i][M-1])%=P; (g[i][M-1]+=w[i][M-1])%=P; } } inline void applyinv(int x){ rep(i)tmp[i]=g[x][i],g[x][i]=(2LL*g[x][i]-g[M-1][i])%P; rep(i)g[M-1][i]=tmp[i]; /*newf[x]=olds news=olds*2-oldf[x] olds=newf[x] oldf[x]=newf[x]*2-news*/ } inline int cal(int l,int r){ int ret=0; rep(i)ret=(1LL*b[l][i]*c[r+1][i]+ret)%P; return (ret+P)%P; } const int Base=1000000000; int len[M+1]; int num[M+1][N]; inline void copy(int x,int y){ len[x]=len[y]; for(int i=len[x];i;i--)num[x][i]=num[y][i]; } inline void mul2(int x){ int l=len[x],i; num[x][++l]=0; for(i=1;i<=l;i++)num[x][i]<<=1; for(i=1;i<l;i++)if(num[x][i]>=Base)num[x][i+1]++,num[x][i]-=Base; while(l>1&&!num[x][l])l--; len[x]=l; } inline void sub(int x,int y){ int l=len[x],i; for(i=len[y];i;i--)num[x][i]-=num[y][i]; for(i=1;i<l;i++)if(num[x][i]<0)num[x][i+1]--,num[x][i]+=Base; while(l>1&&!num[x][l])l--; len[x]=l; } inline void gao(int x){ copy(M,x); copy(x,M-1); mul2(M-1); sub(M-1,M); } void write(int x){ printf("%d",num[x][len[x]]); for(int i=len[x]-1;i;i--)printf("%09d",num[x][i]); } int main(){ scanf("%s",a+1); n=strlen(a+1); rep(i)g[i][i]=1; rep(j)b[n+1][j]=g[j][M-1]; for(i=n;i;i--){ apply(getid(a[i])); rep(j)b[i][j]=g[j][M-1]; } rep(i)rep(j)g[i][j]=0; rep(i)g[i][i]=1; rep(j)c[n+1][j]=g[M-1][j]; for(i=n;i;i--){ applyinv(getid(a[i])); rep(j)c[i][j]=g[M-1][j]; } /*int x,y; while(~scanf("%d%d",&x,&y)){ printf("%d\n",cal(x,y)); }*/ int q,a0,b0,pp,qq,rr,last=0; int L,R; scanf("%d%d%d%d%d%d",&q,&a0,&b0,&pp,&qq,&rr); while(q--){ int na=(1LL*pp*a0+1LL*qq*b0+last+rr)%P; int nb=(1LL*pp*b0+1LL*qq*a0+last+rr)%P; int x=min(na%n+1,nb%n+1); int y=max(na%n+1,nb%n+1); L=x,R=y; last=cal(x,y); a0=na,b0=nb; } printf("%d",last); return 0; rep(i)len[i]=1; num[M-1][1]=1; for(i=L;i<=R;i++)gao(getid(a[i])); write(M-1); } /* aadhuihGDSTYFSJHJUYFRgdgjesfgAGFDHSFHDTedsjydaJdgHSdfhyjFhsGvfhdzgfjsfjySfgyjhzdfgjzdfgjzfgjhdfgjGfyjhzdgfzjyfgzdjhgfdjdfdfdfdfdfdfdfdfdffdfdfzgjSGHFDsyJHFGhGFujyFGTjySFyJGDjSdgjyhSFhgZFGjhSDF 100 100 245 463 356 234243 85192723079788 */
L. XOR Transformation
將$T$二進位制拆分,將每個$1$拿出來單獨做$2^t$步操作。
對於$2^t$步操作,每個點的值會變成往後$2^t$一步$k$個點的異或和,將$k$模以兩倍的環長後用字首異或和計算即可。
時間複雜度$O(n\log T)$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=222222; int n,m,i,a[N];ll T; int v[N],q[N],b[N]; void solve(int k){ ll t=1LL<<k; t%=n; int i,j; for(i=0;i<n;i++)v[i]=0; for(i=0;i<n;i++)if(!v[i]){ int len=0; for(j=i;!v[j];j=(j+t)%n)v[q[len++]=j]=1; for(j=0;j<len;j++)b[j]=b[j+len]=a[q[j]]; for(j=1;j<len+len;j++)b[j]^=b[j-1]; for(j=0;j<len;j++){ int val=b[((j+m-1)%(len*2)+len*2)%(len*2)]; if(j)val^=b[j-1]; a[q[j]]=val; } } } int main(){ scanf("%d%d%lld",&n,&m,&T); for(i=0;i<n;i++)scanf("%d",&a[i]); for(i=0;i<62;i++)if(T>>i&1)solve(i); for(i=0;i<n;i++)printf("%d ",a[i]); }