A. Three Arrays
列舉每個$a_i$,雙指標出$b$和$c$的範圍,對於$b$中每個預先雙指標出$c$的範圍,那麼對於每個$b$,在對應$c$的區間加$1$,在$a$處區間求和即可。
樹狀陣列維護,時間複雜度$O(n\log n)$。
#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 = 5e5 + 10, 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; int d; int na, nb, nc; int a[N], b[N], c[N]; int lc[N], rc[N]; LL suml[N], sumr[N]; struct BIT { LL v[N]; LL vx[N]; void init() { memset(v, 0, (n + 2) << 3); memset(vx, 0, (n + 2) << 3); } void add(int x, LL V) { LL VX = V * (x - 1); for (; x <= n; x += x & -x) { v[x] += V; vx[x] += VX; } } LL check(int x) { LL ret = 0; for(int i = x; i ; i -= i & -i) { ret += v[i] * x; ret -= vx[i]; } return ret; } }bit; void ADD(int p, int val) { int l = lc[p]; int r = rc[p]; bit.add(l, val); bit.add(r + 1, -val); } LL CHECK(int l, int r) { return bit.check(r) - bit.check(l - 1); } int main() { while(~scanf("%d", &d)) { bit.init(); scanf("%d%d%d", &na, &nb, &nc); n = nc + 10; for(int i = 1; i <= na; ++i)scanf("%d", &a[i]); for(int i = 1; i <= nb; ++i)scanf("%d", &b[i]); for(int i = 1; i <= nc; ++i)scanf("%d", &c[i]); int l = 1, r = 0; for(int i = 1; i <= nb; ++i) { while(r < nc && c[r + 1] <= b[i] + d)++r; while(l <= nc && c[l] < b[i] - d)++l; lc[i] = l; rc[i] = r; } for(int i = 1; i <= nb; ++i) { sumr[i] = sumr[i - 1] + rc[i]; suml[i] = suml[i - 1] + lc[i]; } l = 1, r = 0; int L = 1, R = 0; LL ans = 0; int ll = 1, rr = 0; for(int i = 1; i <= na; ++i) { while(rr < nc && c[rr + 1] <= a[i] + d)++rr; while(ll <= nc && c[ll] < a[i] - d)++ll; while(R < nb && b[R + 1] <= a[i] + d) { ADD(++R, 1); } while(L <= nb && b[L] < a[i] - d) { ADD(L++, -1); } if(rr >= ll)ans += CHECK(ll, rr); /* while(rr < nc && c[rr + 1] <= a[i] + d)++rr; while(ll <= nc && c[ll] < a[i] - d)++ll; while(R < nb && b[R + 1] <= a[i] + d)++R; while(L <= nb && b[L] < a[i] - d)++L; while(r < nb && rc[r + 1] <= a[i] + d)++r; while(l <= nb && lc[l] < a[i] - d)++l; if(L > R)continue; if(ll > rr)continue; if(r >= l) { ans += sumr[r] - sumr[L - 1]; ans += (LL)(R - r) * rr; ans -= suml[R] - suml[l - 1]; ans -= (LL)((l - 1) - (L - 1)) * ll; ans += (R - L + 1); } else { printf("LRlr llrr: %d %d %d %d %d %d %d\n", i, L, R, l, r, ll, rr); ans += (LL)(R - L + 1) * (rr - ll + 1); } printf("%lld\n", sumr[r] - sumr[L - 1]); printf("%lld\n", (LL)(R - r) * (a[i] + d)); printf("%lld\n", suml[R] - suml[l - 1]); printf("%lld\n", (LL)((l - 1) - (L - 1)) * (a[i] - d)); printf("%d %d %d %d\n", L, R, l, r); printf("%d %lld\n", i, ans); */ } printf("%lld\n", ans); } return 0; } /* 【trick&&吐槽】 1 3 3 3 1 2 3 1 2 3 1 2 3 1 6 6 6 1 1 2 2 3 3 2 2 3 3 4 4 3 3 4 4 5 5 【題意】 【分析】 【時間複雜度&&優化】 */
B. Expected Shopping
高精度。
C. Cover the Paths
貪心,按照LCA的深度從深到淺選擇。
#include<cstdio> #include<algorithm> using namespace std; const int N=100010,M=262150; int Case,cas,n,m,q,i,op,x,y,z; int g[N],v[N<<1],nxt[N<<1],ed; int size[N],son[N],f[N],d[N],st[N],top[N],dfn; int val[M]; int ans,choose[N]; struct E{int x,y,z;}e[N]; inline bool cmp(const E&a,const E&b){return d[a.z]<d[b.z];} inline void addedge(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} void dfs(int x){ size[x]=1; for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){ f[v[i]]=x,d[v[i]]=d[x]+1; dfs(v[i]),size[x]+=size[v[i]]; if(size[v[i]]>size[son[x]])son[x]=v[i]; } } void dfs2(int x,int y){ st[x]=++dfn;top[x]=y; if(son[x])dfs2(son[x],y); for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]); } void build(int x,int a,int b){ val[x]=0; if(a==b)return; int mid=(a+b)>>1; build(x<<1,a,mid),build(x<<1|1,mid+1,b); } void change(int x,int a,int b,int c){ val[x]++; if(a==b)return; int mid=(a+b)>>1; if(c<=mid)change(x<<1,a,mid,c); else change(x<<1|1,mid+1,b,c); } int ask(int x,int a,int b,int c,int d){ if(c<=a&&b<=d)return val[x]; int mid=(a+b)>>1,t=0; if(c<=mid)t=ask(x<<1,a,mid,c,d); if(d>mid)t+=ask(x<<1|1,mid+1,b,c,d); return t; } inline int lca(int x,int y){ for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]])swap(x,y); return d[x]<d[y]?x:y; } inline int chain(int x,int y){ int t=0; for(;top[x]!=top[y];x=f[top[x]]){ if(d[top[x]]<d[top[y]])swap(x,y); t+=ask(1,1,n,st[top[x]],st[x]); } if(d[x]<d[y])swap(x,y); t+=ask(1,1,n,st[y],st[x]); return t; } inline void gao(int x,int y,int z){ if(chain(x,y))return; choose[++ans]=z; change(1,1,n,st[z]); } int main(){ scanf("%d",&n); for(i=1;i<n;i++){ scanf("%d%d",&x,&y); addedge(x,y),addedge(y,x); } dfs(1); dfs2(1,1); build(1,1,n); scanf("%d",&m); for(i=1;i<=m;i++){ scanf("%d%d",&x,&y); e[i].x=x,e[i].y=y; e[i].z=lca(x,y); } sort(e+1,e+m+1,cmp); for(i=m;i;i--)gao(e[i].x,e[i].y,e[i].z); printf("%d\n",ans); for(i=1;i<=ans;i++)printf("%d ",choose[i]); }
D. Elevator
若$t_i<t_j$且$a_i\leq a_j$,那麼$j$可以順手帶走$i$,故可以剔除這些$i$使得$a$嚴格遞減。
設$f_i$表示前$i$個人分組的最短時間,則$f_i=\min(\max(f_j,t_i)+2a_{j+1})$,其中$0\leq j<i$,且$f_j<t_{i+1}$,不然不能將$i$和$i+1$分開。
討論$\max$的來源,Treap維護即可。
時間複雜度$O(n\log n)$。
#include<cstdio> #include<cstdlib> #include<algorithm> using namespace std; typedef long long ll; const int N=200010; const ll inf=1LL<<60; int n,m,i; ll f[N]; struct P{ ll t,a; }a[N],q[N]; inline bool cmp(const P&a,const P&b){ return a.t<b.t; } struct node{ ll val,dp,mi; int p; node*l,*r; node(){ val=0; dp=mi=inf; p=0; l=r=NULL; } void up(){ mi=min(dp,min(l->mi,r->mi)); } }*blank=new(node),*root[2],pool[N*2],*cur; inline void Rotatel(node*&x){node*y=x->r;x->r=y->l;x->up();y->l=x;y->up();x=y;} inline void Rotater(node*&x){node*y=x->l;x->l=y->r;x->up();y->r=x;y->up();x=y;} void Ins(node*&x,ll p,ll v){ if(x==blank){ x=new(node); x->val=p; x->dp=x->mi=v; x->l=x->r=blank; x->p=rand(); return; } x->mi=min(x->mi,v); if(p==x->val){ x->dp=min(x->dp,v); return; } if(p<x->val){ Ins(x->l,p,v); if(x->l->p>x->p)Rotater(x); }else{ Ins(x->r,p,v); if(x->r->p>x->p)Rotatel(x); } } ll Ask(node*x,ll a,ll b,ll c,ll d){ if(x==blank)return inf; if(c<=a&&b<=d)return x->mi; ll t=c<=x->val&&x->val<=d?x->dp:inf; if(c<x->val)t=min(t,Ask(x->l,a,x->val-1,c,d)); if(d>x->val)t=min(t,Ask(x->r,x->val+1,b,c,d)); return t; } inline void ins(int x){ if(f[x]>=inf)return; Ins(root[0],f[x],q[x+1].a); Ins(root[1],f[x],f[x]+q[x+1].a); } int main(){ blank->l=blank->r=blank; while(~scanf("%d",&n)){ for(i=1;i<=n;i++)scanf("%lld%lld",&a[i].t,&a[i].a),a[i].a*=2; sort(a+1,a+n+1,cmp); m=0; for(i=1;i<=n;i++){ while(m&&q[m].a<=a[i].a)m--; q[++m]=a[i]; } q[m+1].t=inf; root[0]=root[1]=blank; ins(0); for(i=1;i<=m;i++){ f[i]=min(Ask(root[0],0,inf,0,q[i].t)+q[i].t,Ask(root[1],0,inf,q[i].t,q[i+1].t-1)); ins(i); } printf("%lld\n",f[m]); } }
E. Code-Cola Plants
對於集合$a$,要滿足除$a$以外每個點恰有一條入邊,對於集合$b$,要滿足除$b$以外每個點恰有一條出邊。
建立二分圖,左邊$2n-2$個點,分別表示每個點需要入邊和出邊,右邊$m$個點,表示原圖一條邊,那麼右邊每個點可以供給左邊至多兩個點。
Hopcroft求最大匹配即可通過。
時間複雜度$O(n\sqrt{n})$。
#include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int N= 1e6 + 10; int n1, n2; vector<int> g[N]; int mx[N], my[N]; int que[N*5]; int head,tail; int dx[N], dy[N]; bool vis[N]; bool find(int u){ for(int i = 0; i < g[u].size(); i ++) if(! vis[g[u][i]] && dy[g[u][i]] == dx[u] + 1){ vis[g[u][i]] = true; if(! my[g[u][i]] || find(my[g[u][i]])){ mx[u] = g[u][i]; my[g[u][i]] = u; return true; } } return false; } int matching() { memset(mx, 0, (n1 + 5) << 2); memset(my, 0, (n2 + 5) << 2); int ans = 0; while(true){ bool flag = false; head=1,tail=0; memset(dx, 0, (n1 + 5) << 2); memset(dy, 0, (n2 + 5) << 2); for(int i = 1; i <= n1; i ++) if(! mx[i])que[++tail]=i; while(head<=tail){ int u = que[head++]; for(int i = 0; i < g[u].size(); i ++) if(! dy[g[u][i]]){ dy[g[u][i]] = dx[u] + 1; if(my[g[u][i]]){ dx[my[g[u][i]]] = dy[g[u][i]] + 1; que[++tail]=my[g[u][i]]; } else flag = true; } } if(! flag) break; memset(vis, 0, max(n1,n2)+5); for(int i = 1; i <= n1; i ++) if(! mx[i] && find(i)) ans ++; } return ans; } int n, m, a, b; int main(){ while(~ scanf("%d%d%d%d", &n, &m, &a, &b)){ n1 = m; n2 = 2 * n ; for(int i = 1; i <= m; i ++){ int x, y; scanf("%d%d", &x, &y); g[i].clear(); if(a != y) g[i].push_back(y); if(b != x)g[i].push_back(x + n); } int ans = matching(); if(ans == 2 * n - 2){ puts("YES"); for(int i = 1; i <= n; i ++){ if(i != a) printf("%d ", my[i]); }puts(""); for(int i = n + 1; i <= n + n ; i ++){ if(i != b + n) printf("%d ", my[i]); }puts(""); } else puts("NO"); } } /* 4 7 1 4 1 2 1 2 1 4 2 3 2 3 3 4 3 4 4 3 1 2 1 2 2 4 4 3 5 8 3 1 3 2 5 2 3 4 4 5 4 1 2 1 3 5 3 1 */
F. GCD
即保留至少$m-k$個數使得$\gcd$最大,因為$k\leq\frac{n}{2}$,故每個數屬於最優解的概率至少為$\frac{1}{2}$。
多次隨機選擇一個數,認為它必選,那麼將其分解質因數,高維字首和統計每個約數是多少個數的約數即可。
在$10^{18}$內約數個數最大值不超過$200000$,故可以通過。
#include<cstdio> #include<algorithm> #include<cstdlib> using namespace std; typedef long long ll; const int C=2730,S=5; const int N=222222; int n,m,i,j,k; int len[N]; ll pr[N][30][2]; ll pool[N]; ll ans; int cnt; ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;} ll mul(ll a, ll b, ll n){return (a * b - (ll)(a / (long double) n * b + 1e-3) * n + n) % n;} ll pow(ll a, ll b, ll n){ ll d = 1; a %= n; while(b){ if(b & 1) d = mul(d, a, n); a = mul(a, a, n); b >>= 1; } return d; } bool check(ll a, ll n){ ll m = n - 1, x, y; int i, j = 0; while(! (m & 1)) m >>= 1, j ++; x = pow(a, m, n); for(i = 1; i <= j; x = y, i ++){ y = pow(x, 2, n); if((y == 1) && (x != 1) && (x != n - 1)) return 1; } return y != 1; } bool miller_rabin(int times, ll n){ ll a; if(n == 1) return 0; if(n == 2) return 1; if(! (n & 1)) return 0; while(times --) if(check(rand() % (n - 1) + 1, n)) return 0; return 1; } ll pollard_rho(ll n, int c){ ll i = 1, k = 2, x = rand() % n, y = x, d; while(1){ i ++, x = (mul(x, x, n) + c) % n, d = gcd(y - x, n); if(d > 1 && d < n) return d; if(y == x) return n; if(i == k) y = x, k <<= 1; } } void findfac(ll n, int c){ if(n == 1) return; if(miller_rabin(S, n)){ pool[++cnt]=n; return; } ll m = n; while(m == n) m = pollard_rho(n, c --); findfac(m, c), findfac(n / m, c); } ll num[222222]; ll nowp[50]; ll pw[50][100]; int lim[50]; int tot; int now[50]; int cntdiv; ll divisor[3000000]; int f[3000000]; void dfs(int x,ll y){ if(x==tot){ divisor[++cntdiv]=y; return; } for(int i=0;i<=lim[x];i++)dfs(x+1,y*pw[x][i]); } inline int getid(ll x){return lower_bound(divisor+1,divisor+cntdiv+1,x)-divisor;} void solve(int S){ int i,j,k; if(num[S]==1)return; cnt=0; findfac(num[S],C); sort(pool+1,pool+cnt+1);len[S]=0; for(j=1;j<=cnt;j=k){ for(k=j;k<=cnt&&pool[j]==pool[k];k++); pr[S][len[S]][0]=pool[j]; pr[S][len[S]][1]=k-j; len[S]++; } tot=len[S]; for(j=0;j<tot;j++){ nowp[j]=pr[S][j][0]; lim[j]=pr[S][j][1]; for(pw[j][0]=k=1;k<=lim[j];k++){ pw[j][k]=pw[j][k-1]*nowp[j]; } } cntdiv=0; dfs(0,1); sort(divisor+1,divisor+cntdiv+1); for(i=1;i<=cntdiv;i++)f[i]=0; for(i=1;i<=n;i++){ ll val=1; ll x=num[i]; for(k=0;k<tot;k++)if(x%nowp[k]==0){ int B=0; while(x%nowp[k]==0)x/=nowp[k],B++; val*=pw[k][min(B,lim[k])]; } f[getid(val)]++; } for(i=0;i<tot;i++){ int x=nowp[i]; for(j=k=cntdiv;j;j--)if(divisor[j]%x==0){ ll goal=divisor[j]/x; while(divisor[k]>goal)k--; f[k]+=f[j]; } } for(i=1;i<=cntdiv;i++)if(f[i]>=m)ans=max(ans,divisor[i]); } int main(){ scanf("%d%d",&n,&m); m=n-m;//keep >=m numbers for(i=1;i<=n;i++){ ll x; scanf("%lld",&x); num[i]=x; /*cnt=0; findfac(x,C); sort(pool+1,pool+cnt+1); for(j=1;j<=cnt;j=k){ for(k=j;k<=cnt&&pool[j]==pool[k];k++); pr[i][len[i]][0]=pool[j]; pr[i][len[i]][1]=k-j; len[i]++; }*/ } ans=1; for(int ___=8;___;___--){ int x=rand()%n+1; solve(x); } printf("%lld",ans); }
G. Berland Post
二分$T$,差分約束判負環。
#include<cstdio> #include<algorithm> #include<cmath> using namespace std; const int N=1010,M=2010,LIM=1000000000; const double eps=1e-8; int n,m,i,unsure[N],a[N]; int e[M][3]; double L,R,ans,MID; char s[100]; double d[N]; bool in[N]; int vis[N]; int q[10000010],h,t; int g[N],v[M],nxt[M],ed; double w[M]; inline void add(int x,int y,double z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;} bool check(double T){ int i,j,x; ed=0; for(i=1;i<=n;i++)g[i]=0; for(i=1;i<=m;i++)add(e[i][1],e[i][0],T-e[i][2]); for(i=1;i<=n;i++)if(unsure[i])d[i]=LIM;else d[i]=a[i]; for(h=1,t=0,i=1;i<=n;i++)in[i]=1,q[++t]=i,vis[i]=0; while(h<=t){ x=q[h++]; for(i=g[x];i;i=nxt[i])if(d[x]+w[i]+eps<d[v[i]]){ d[v[i]]=d[x]+w[i]; if(!in[v[i]]){ in[v[i]]=1; vis[v[i]]++; if(vis[v[i]]>3100)return 0; q[++t]=v[i]; } } in[x]=0; } for(i=1;i<=n;i++){ if(d[i]+eps<-LIM)return 0; if(!unsure[i]&&fabs(d[i]-a[i])>eps)return 0; } return 1; } int main(){ while(~scanf("%d%d",&n,&m)){ for(i=1;i<=n;i++){ scanf("%s",s); if(s[0]=='?')unsure[i]=1;else{ unsure[i]=0; sscanf(s,"%d",&a[i]); } } for(i=1;i<=m;i++)scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]); L=0,R=LIM; for(int i=77;i--;){ MID=(L+R)/2; if(check(MID))R=(ans=MID);else L=MID; } check(ans); printf("%.10f\n",ans); for(i=1;i<=n;i++)printf("%.10f ",d[i]); puts(""); } }
H. Compressed Spanning Subtrees
對於每個點$i$詢問除它之外$n-1$個點的虛樹大小即可判斷出$i$是否是葉子。
選擇某個葉子$x$作為根,列舉一個葉子$y$以及一個非葉子$z$,詢問$x,y,z$的虛樹大小即可判斷出$y$是否在$z$的子樹中,並可以以此計算每個點子樹中的葉子個數。
因為題目保證不存在度數為$2$的點,故每個葉子往上到根路徑上每個點的子樹內葉子數嚴格遞增,排序即可完成構造。
#include<cstdio> #include<algorithm> #include<vector> using namespace std; const int N=110; int n,i,j,is[N],root,size[N]; vector<int>pre[N]; int fa[N],vis[N],ans[N]; inline bool cmp(int x,int y){return size[x]>size[y];} void dfs(int x,int y){ if(vis[x])return; vis[x]=1; ans[x]=y; for(int i=1;i<=n;i++)if(fa[i]==x)dfs(i,x); if(fa[x])dfs(fa[x],x); } int main(){ scanf("%d",&n); if(n==2)return puts("! 1"),0; for(i=1;i<=n;i++){ printf("? %d",n-1); for(j=1;j<=n;j++)if(i!=j)printf(" %d",j); puts(""); fflush(stdout); scanf("%d",&is[i]); is[i]=is[i]==n-1; if(is[i])root=i; } size[root]=N; for(i=1;i<=n;i++)if(!is[i])for(j=1;j<=n;j++)if(j!=root&&is[j]){ printf("? 3 %d %d %d\n",root,i,j); fflush(stdout); int t; scanf("%d",&t); if(t==3)size[i]++,pre[j].push_back(i); } for(i=1;i<=n;i++)if(i!=root&&is[i]){ pre[i].push_back(root); pre[i].push_back(i); sort(pre[i].begin(),pre[i].end(),cmp); for(j=1;j<pre[i].size();j++)fa[pre[i][j]]=pre[i][j-1]; } dfs(1,0); printf("!"); for(i=2;i<=n;i++)printf(" %d",ans[i]); }
I. Prefix-free Queries
對於一個$k$個子串的詢問,將這些串按照字典序排序,那麼可以用棧建出字首關係的樹結構,然後就是經典樹形DP。
時間複雜度$O(k\log k\log n)$。
#include<cstdio> #include<algorithm> using namespace std; typedef pair<int,int>E; const int N=400010,SEED=233,SEED2=13331,MO1=998244353,MO2=1000000007; int n,m,i,k,mo,q[N],cnt[N],t,g[N],nxt[N],dp[N]; char a[N]; int f[N],p[N]; struct P{ int l,r,len; }b[N]; inline int get(int l,int r){return ((f[r]-1LL*f[l-1]*p[r-l+1])%MO1+MO1)%MO1; } inline int lcp(int a,int b,int c,int d){ int l=1,r=min(b-a+1,d-c+1),mid,t=0; while(l<=r){ mid=(l+r)>>1; if(get(a,a+mid-1)==get(c,c+mid-1))l=(t=mid)+1;else r=mid-1; } //printf("[%d,%d] [%d,%d] %d\n",a,b,c,d,t); return t; } inline bool cmp(const P&_a,const P&_b){//a<b? int t=lcp(_a.l,_a.r,_b.l,_b.r); if(t==_a.len){ if(_a.len==_b.len)return 0;//equal return 1;//a is prefix of b } if(t==_b.len)return 0;//b is prefix of a return a[_a.l+t]<a[_b.l+t]; } inline bool equal(const P&a,const P&b){ int t=lcp(a.l,a.r,b.l,b.r); if(t==a.len&&a.len==b.len)return 1; return 0; } inline bool isprefix(const P&a,const P&b){ int t=lcp(a.l,a.r,b.l,b.r); if(t==a.len)return 1; return 0; } inline void add(int x,int y){ //printf("%d->%d\n",x,y); nxt[y]=g[x];g[x]=y;} void dfs(int x){ dp[x]=1; for(int i=g[x];i;i=nxt[i]){ dfs(i); dp[x]=1LL*dp[x]*dp[i]%mo; } dp[x]=(dp[x]+cnt[x])%mo; //printf("dp[%d]=%d\n",x,dp[x]); } int main(){ scanf("%d%d%s",&n,&m,a+1); for(p[0]=i=1;i<=n;i++){ p[i]=1LL*p[i-1]*SEED%MO1; f[i]=(1LL*f[i-1]*SEED+a[i])%MO1; } while(m--){ scanf("%d%d",&k,&mo); for(i=1;i<=k;i++)scanf("%d%d",&b[i].l,&b[i].r),b[i].len=b[i].r-b[i].l+1; sort(b+1,b+k+1,cmp); for(i=0;i<=k;i++)g[i]=0; cnt[0]=1; for(t=0,i=1;i<=k;i++){ if(t&&equal(b[q[t]],b[i])){ cnt[q[t]]++; continue; } while(t&&!isprefix(b[q[t]],b[i]))t--; add(q[t],i); q[++t]=i; cnt[i]=1; } //for(i=1;i<=k;i++)printf("cnt[%d]=%d\n",i,cnt[i]); dfs(0); printf("%d\n",((dp[0]-1)%mo+mo)%mo); } } /* 10 100 aabbaacaba 5 20 1 2 3 4 5 6 7 8 9 10 */
J. Subsequence Sum Queries
分治,預處理出$mid$到$[l,r]$每個點的揹包,然後利用這個資訊處理所有經過$mid$的詢問。
時間複雜度$O((nm+q)\log n+qm)$。
#include<cstdio> #include<vector> using namespace std; typedef vector<int>V; const int N=200010,M=25,P=1000000007; int n,m,Q,i,a[N],f[N][M],g[N][M],e[N][3]; inline void up(int&a,int b){a=a+b<P?a+b:a+b-P;} void solve(int l,int r,V v){ if(l>r||!v.size())return; int mid=(l+r)/2; //[i..mid] [mid+1..i] for(int i=l;i<=mid+1;i++)for(int j=0;j<m;j++)f[i][j]=0; f[mid+1][0]=1; for(int i=mid;i>=l;i--){ for(int j=0;j<m;j++){ up(f[i][j],f[i+1][j]); up(f[i][(j+a[i])%m],f[i+1][j]); } } for(int i=mid;i<=r;i++)for(int j=0;j<m;j++)g[i][j]=0; g[mid][0]=1; for(int i=mid+1;i<=r;i++){ for(int j=0;j<m;j++){ up(g[i][j],g[i-1][j]); up(g[i][(j+a[i])%m],g[i-1][j]); } } V vl,vr; for(int i=0;i<v.size();i++){ int x=v[i]; if(e[x][0]>mid)vr.push_back(x); else if(e[x][1]<mid)vl.push_back(x); else{ for(int j=0;j<m;j++)up(e[x][2],1LL*f[e[x][0]][j]*g[e[x][1]][(m-j)%m]%P); } } solve(l,mid-1,vl); solve(mid+1,r,vr); } int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++)scanf("%d",&a[i]),a[i]%=m; scanf("%d",&Q); V _; for(i=1;i<=Q;i++){ scanf("%d%d",&e[i][0],&e[i][1]); _.push_back(i); } solve(1,n,_); for(i=1;i<=Q;i++)printf("%d\n",e[i][2]); }
K. Consistent Occurrences
將詢問按長度分組,則最多隻有$O(\sqrt{n})$種長度,對於每種長度$O(n\log n)$計算答案即可。
#include<cstdio> #include<cstring> #include<algorithm> #include<map> using namespace std; typedef long long ll; typedef pair<ll,ll>PI; typedef pair<PI,int>P; const int N=100010,SEED=233,SEED2=13331,MO=998244353,MO2=1000000007; int n,m,i,j; char a[N],b[N]; ll p[N],f[N],p2[N],f2[N]; map<PI,int>T[N],have[N]; bool vis[N]; P e[N],q[N]; inline PI get(int l,int r){ return PI( ((f[r]-f[l-1]*p[r-l+1])%MO+MO)%MO, ((f2[r]-f2[l-1]*p2[r-l+1])%MO2+MO2)%MO2 ); } inline void make(int l,int r,const P&o,int len){ if(have[len].find(o.first)==have[len].end())return; int i,pre=-N,cnt=0; for(i=l;i<=r;i++){ if(e[i].second-pre>=len){ cnt++; pre=e[i].second; } } T[len][o.first]=cnt; } void pre(int len){ int i,j,cnt=0; vis[len]=1; for(i=len;i<=n;i++){ e[++cnt]=P(get(i-len+1,i),i); } sort(e+1,e+cnt+1); for(i=1;i<=cnt;i=j){ for(j=i;j<=cnt&&e[i].first==e[j].first;j++); make(i,j-1,e[i],len); } } inline int ask(int len,PI goal){ if(!vis[len])pre(len); return T[len][goal]; } int main(){ scanf("%d%d%s",&n,&m,a+1); for(p[0]=i=1;i<=n;i++)p[i]=p[i-1]*SEED%MO; for(i=1;i<=n;i++)f[i]=(f[i-1]*SEED+a[i])%MO; for(p2[0]=i=1;i<=n;i++)p2[i]=p2[i-1]*SEED2%MO2; for(i=1;i<=n;i++)f2[i]=(f2[i-1]*SEED2+a[i])%MO2; for(j=1;j<=m;j++){ scanf("%s",b+1); ll now=0,now2=0; int len=strlen(b+1); for(i=1;i<=len;i++)now=(now*SEED+b[i])%MO; for(i=1;i<=len;i++)now2=(now2*SEED2+b[i])%MO2; have[len][PI(now,now2)]=1; q[j]=P(PI(now,now2),len); } for(j=1;j<=m;j++){ printf("%d\n",ask(q[j].second,q[j].first)); } } /* 8 8888 abaabbab ab 3 aa 0 */
L. Increasing Costs
建出最短路圖後,將每條邊拆點,那麼答案就是每條邊代表的點在Dominator Tree中的子樹大小。
#include<cstdio> typedef long long ll; const int N=500010,M=1000010,K=22; const ll inf=1LL<<60; int n,m,S,cnt,i,x,deg[N],g[N],v[M],w[M],nxt[M],ed; int size[N],sum[N],id[N],h,t,q[N],dep[N],f[N][K],G[N],NXT[N],V[N]; ll d[N]; struct E{int x,y,w;}a[M>>1]; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} inline void addedge(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;} inline void add(int x,int y){deg[y]++;v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline void addtree(int x,int y){V[++ed]=y;NXT[ed]=G[x];G[x]=ed;} struct PI{ ll x;int y; PI(){} PI(ll _x,int _y){x=_x,y=_y;} inline PI operator+(const PI&b){return x<=b.x?PI(x,y):b;} }val[2222222]; void build(int x,int a,int b){ val[x]=PI(inf,a); if(a==b)return; int mid=(a+b)>>1; build(x<<1,a,mid),build(x<<1|1,mid+1,b); } inline void change(int x,int a,int b,int c,ll d){ if(a==b){val[x].x=d;return;} int mid=(a+b)>>1; c<=mid?change(x<<1,a,mid,c,d):change(x<<1|1,mid+1,b,c,d); val[x]=val[x<<1]+val[x<<1|1]; } inline int lca(int x,int y){ int i; if(dep[x]<dep[y])i=x,x=y,y=i; for(i=K-1;~i;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i]; if(x==y)return x; for(i=K-1;~i;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } void dfs(int x){ for(int i=G[x];i;i=NXT[i])dfs(V[i]),size[x]+=size[V[i]]; sum[id[x]]=size[x]; } int main(){ read(n),read(m); for(i=1;i<=m;i++){ read(a[i].x),read(a[i].y),read(a[i].w); addedge(a[i].x,a[i].y,a[i].w); addedge(a[i].y,a[i].x,a[i].w); } S=1; for(i=1;i<=n;i++)d[i]=inf; build(1,1,n),change(1,1,n,S,d[S]=0); while(val[1].x<inf)for(change(1,1,n,x=val[1].y,inf),i=g[x];i;i=nxt[i])if(d[x]+w[i]<d[v[i]])change(1,1,n,v[i],d[v[i]]=d[x]+w[i]); for(ed=0,i=1;i<=n;i++)g[i]=0,size[i]=d[i]<inf; for(cnt=n,i=1;i<=m;i++){ if(d[a[i].x]+a[i].w==d[a[i].y])id[++cnt]=i,add(a[i].x,cnt),add(cnt,a[i].y); if(d[a[i].y]+a[i].w==d[a[i].x])id[++cnt]=i,add(a[i].y,cnt),add(cnt,a[i].x); } for(cnt++,i=1;i<cnt;i++)if(!deg[i])add(cnt,i); q[h=t=1]=cnt,ed=0; while(h<=t){ x=q[h++]; if(f[x][0])addtree(f[x][0],x); for(dep[x]=dep[f[x][0]]+1,i=1;i<K;i++)f[x][i]=f[f[x][i-1]][i-1]; for(i=g[x];i;i=nxt[i]){ if(!f[v[i]][0])f[v[i]][0]=x;else f[v[i]][0]=lca(f[v[i]][0],x); if(!(--deg[v[i]]))q[++t]=v[i]; } } dfs(cnt); for(i=1;i<=m;i++)printf("%d\n",sum[i]); }