XIII Open Cup named after E.V. Pankratiev. GP of Ukraine
A. Automaton
后缀自动机可以得到O(2n+1)个状态,但是后缀自动机会拒绝接收所有不是S的子串的串,所以在建立后缀自动机的时候不复制节点即可得到n+1个状态的DFA。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | #include<stdio.h> #include<algorithm> #include<math.h> #include<string.h> #include<string> #include<vector> #include<set> #include<map> #include<queue> #include<time.h> #include<assert.h> #include<iostream> using namespace std; typedef long long LL; typedef pair< int , int >pi; const int Maxn=200020; int n,q,sz,last,len; int ml[Maxn],f[Maxn],ch[Maxn][26]; vector< int >G[Maxn]; string ss[Maxn]; string s; void add( int c) { int p=last; if (ch[p][c]) { int q=ch[p][c]; if (ml[q]==ml[p]+1)last=q; else { int nq=++sz; memcpy (ch[nq],ch[q], sizeof (ch[q])); ml[nq]=ml[p]+1; last=nq; f[nq]=f[q]; f[q]=nq; for (;p&&ch[p][c]==q;p=f[p])ch[p][c]=nq; } } else { int np=++sz;last=np; memset (ch[np],0, sizeof ch[np]); ml[np]=ml[p]+1; for (;p&&!ch[p][c];p=f[p])ch[p][c]=np; if (!p){f[np]=1; return ;} int q=ch[p][c]; f[np]=q; return ; // if (ml[q]==ml[p]+1){f[np]=q; return ;} int nq=++sz; memcpy (ch[nq],ch[q], sizeof (ch[q])); ml[nq]=ml[p]+1; f[nq]=f[q]; f[q]=f[np]=nq; for (;p&&ch[p][c]==q;p=f[p])ch[p][c]=nq; } } void init(){ sz=1; memset (ch[sz],0, sizeof ch[sz]); } void insert(string &s) { last=1; for ( int i=0;i<s.size();i++) { add(s[i]- 'a' ); } } struct Node{ int u,v; char c; Node(){} Node( int u, int v, char c):u(u),v(v),c(c){} }; bool check( int st){ int cur=1; for ( int i=st;i<s.size();i++){ if (!ch[cur][s[i]- 'a' ]) return 0; cur=ch[cur][s[i]- 'a' ]; } return 1; } int main(){ while (cin>>s){ //s.assign(20,'a'); //for(int i=0;i<20;i++)s[i]=rand()%5+s[i]; memset (ch,0, sizeof ch); init(); insert(s); vector<Node>rep; for ( int i=1;i<=sz;i++){ for ( int j=0;j<26;j++){ if (ch[i][j])rep.push_back(Node(i,ch[i][j],j+ 'a' )); } } /* for(int i=0;i<s.size();i++){ if(!check(i)){ puts("wa"); cout<<s<<endl; printf("%d\n",i); while(1); } } */ printf ( "%d %d\n" ,sz,( int )rep.size()); for ( int i=0;i<rep.size();i++) printf ( "%d %d %c\n" ,rep[i].u,rep[i].v,rep[i].c); } return 0; } |
B. Beinz
用Lucas定理计算组合数即可,时间复杂度O(p+tlogn)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include<stdio.h> #include<algorithm> #include<math.h> #include<string.h> #include<string> #include<vector> #include<set> #include<map> #include<queue> #include<time.h> #include<assert.h> #include<iostream> using namespace std; typedef long long LL; typedef pair< int , int >pi; const int mod=1e9+7; const int Maxn=1002000; int fac[Maxn],rev[Maxn]; int C( int x, int y){ if (x<y||y<0) return 0; return 1LL*fac[x]*rev[y]%mod*rev[x-y]%mod; } int main(){ int k,p,t; LL n; fac[0]=fac[1]=rev[0]=rev[1]=1; for ( int i=2;i<Maxn;i++)fac[i]=1LL*i*fac[i-1]%mod; for ( int i=2;i<Maxn;i++)rev[i]=1LL*(mod-mod/i)*rev[mod%i]%mod; for ( int i=2;i<Maxn;i++)rev[i]=1LL*rev[i-1]*rev[i]%mod; while ( scanf ( "%d%d%d" ,&k,&p,&t)!=EOF){ while (t--){ scanf ( "%lld" ,&n); LL ans=1; while (n){ ans=1LL*ans*C(n%p+k-1,k-1)%mod; n/=p; } printf ( "%lld\n" ,ans); } } return 0; } |
C. Cutting
首先将串插入Trie中,可以去掉一些冗余状态。
设dp[l][r][x][y]表示[l,r]这个区间切成串x的前缀y的最少步数,可以枚举下一个匹配点转移。
设f[l][r]表示[l,r]消完的最小步数,可以暴力转移求解。
在计算dp的时候加上可行性剪枝即可,复杂度达不到理论上界O(n5)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | #include <bits/stdc++.h> using namespace std ; typedef long long LL ; typedef pair < int , int > pii ; #define clr( a , x ) memset ( a , x , sizeof a ) const int N = 105 ; const int MAXN = 10005 ; const int INF = 0x3f3f3f3f ; int node[MAXN][26] ; int word[MAXN] ; char s[N] , p[N] ; int dp[N][MAXN] ; int jump[N][26] , pre[26] ; int f[N][N] ; int n , m , cur ; int newnode () { clr ( node[cur] , 0 ) ; word[cur] = 0 ; return cur ++ ; } void insert ( char s[] ) { int now = 0 ; for ( int i = 0 ; s[i] ; ++ i ) { int x = s[i] - 'a' ; if ( !node[now][x] ) node[now][x] = newnode () ; now = node[now][x] ; } word[now] = 1 ; } void solve () { n = strlen ( s + 1 ) ; for ( int i = 0 ; i < 26 ; ++ i ) { pre[i] = n + 1 ; } for ( int i = n ; i >= 0 ; -- i ) { for ( int j = 0 ; j < 26 ; ++ j ) { jump[i][j] = pre[j] ; } pre[s[i] - 'a' ] = i ; } cur = 0 ; newnode () ; for ( int i = 0 ; i < m ; ++ i ) { scanf ( "%s" , p ) ; insert ( p ) ; } clr ( f , INF ) ; for ( int l = n ; l >= 1 ; -- l ) { clr ( dp , INF ) ; dp[l - 1][0] = 0 ; for ( int r = l ; r <= n ; ++ r ) { for ( int now = 0 ; now < cur ; ++ now ) if ( dp[r - 1][now] < INF ) { for ( int i = 0 ; i < 26 ; ++ i ) if ( node[now][i] ) { int nxt = node[now][i] ; int x = jump[r - 1][i] ; while ( x <= n ) { int tmp = r <= x - 1 ? f[r][x - 1] : 0 ; dp[x][nxt] = min ( dp[x][nxt] , dp[r - 1][now] + tmp ) ; if ( word[nxt] ) f[l][x] = min ( f[l][x] , dp[x][nxt] + 1 ) ; x = jump[x][i] ; } } } } for ( int r = l ; r <= n ; ++ r ) { for ( int i = l ; i < r ; ++ i ) { f[l][r] = min ( f[l][r] , f[l][i] + f[i + 1][r] ) ; } } } printf ( "%d\n" , f[1][n] ) ; } int main () { while ( ~ scanf ( "%s%d" , s + 1 , &m ) ) solve () ; return 0 ; } |
D. Disclosure
对于一条边x−>y,若去掉之后x不能到达y,那么它是必需的。
首先拓扑排序求出拓扑序,然后按照终点拓扑序为第一关键字,起点拓扑序为第二关键字从小到大加边。
对于每个点,维护一个bitset,表示当前从哪些点可以到达自己。
时间复杂度O(nm32)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include<cstdio> #include<bitset> #include<algorithm> using namespace std; typedef pair< int , int >P; const int N=50002,M=50002; int n,m,i,j,x,y,d[N],g[N],g2[N],v[M],v2[M],nxt[M],nxt2[M],ed,h,t,q[N],ans;bitset<N>f[N];P b[M]; inline void add( int x, int y){d[y]++;v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline void add2( int x, int y){v2[++ed]=y;nxt2[ed]=g2[x];g2[x]=ed;} int main(){ scanf ( "%d%d" ,&n,&m); for (i=1;i<=n;i++)f[i][i]=1; while (m--) scanf ( "%d%d" ,&x,&y),add(x,y); for (ed=0,i=h=1;i<=n;i++) if (!d[i])q[++t]=i; while (h<=t) for (i=g[x=q[h++]];i;add2(v[i],x),i=nxt[i]) if (!(--d[v[i]]))q[++t]=v[i]; for (i=1;i<=n;i++) for (j=g2[x=q[i]];j;f[x]|=f[v2[j]],j=nxt2[j]) if (!f[x][v2[j]])b[++ans]=P(v2[j],x); sort(b+1,b+ans+1); for (i=1;i<=ans;i++) printf ( "%d %d\n" ,b[i].first,b[i].second); return 0; } |
E. Embedded circles
如果询问i的范围完全包含询问j,那么i向j连边,这样可以得到一棵树。
因为题目的限制,所以它给出的是这棵树的dfs序列。
维护一个栈,按深度维护当前询问到根的路径,每次读入一个新的询问,如果它的圆心不被栈顶询问包含,那么弹栈,这样就可以找到当前询问的父亲。
每次弹栈的时候计算被弹询问的答案,为了保证复杂度均摊线性,需要找到若干个属于它的且不属于它任何孩子的一个起点,然后从这些起点开始BFS染色。
每次BFS染色出界的时候,在栈中二分查找出深度最大的包含了这个位置的询问,那么这个点就是那个询问的一个可能的起点。
时间复杂度O(RC+QlogQ)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #include<cstdio> const int N=2010,M=1000010,E=N*N*4; int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1}; int n,m,i,j,cq,e[M][3],q[M],t,ans[M]; char a[N][N]; int que[N*N],head,tail; bool vis[N][N]; int g[M],v[E],nxt[E],ed; long long all; 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 int sqr( int x){ return x*x;} inline bool check( int x, int y, int p){ return sqr(x-e[p][0])+sqr(y-e[p][1])<=e[p][2]; } inline void add( int x, int y){ int l=1,r=t-1,mid,o=0; while (l<=r) if (check(x,y,q[mid=(l+r)>>1]))l=(o=mid)+1; else r=mid-1; if (o)v[++ed]=x<<11|y,nxt[ed]=g[q[o]],g[q[o]]=ed; } inline void bfs( int o, int x, int y){ if (vis[x][y]) return ; que[head=tail=1]=x<<11|y; vis[x][y]=1; while (head<=tail){ x=que[head++]; y=x&2047;x>>=11; ans[o]+=a[x][y]; for ( int d=0;d<4;d++){ int nx=x+dx[d],ny=y+dy[d]; if (nx<1||nx>n||ny<1||ny>m||vis[nx][ny]) continue ; if (check(nx,ny,o)){ que[++tail]=nx<<11|ny; vis[nx][ny]=1; } else { add(nx,ny); } } } } inline void pop(){ int o=q[t]; bfs(o,e[o][0],e[o][1]); for ( int i=g[o];i;i=nxt[i])bfs(o,v[i]>>11,v[i]&2047); ans[q[--t]]+=ans[o]; } int main(){ read(n),read(m); for (i=1;i<=n;i++){ scanf ( "%s" ,a[i]+1); for (j=1;j<=m;j++)a[i][j]-= '0' ; } read(cq); for (i=1;i<=cq;q[++t]=i++){ read(e[i][0]),read(e[i][1]),read(e[i][2]);e[i][2]*=e[i][2]; while (t&&!check(e[i][0],e[i][1],q[t]))pop(); } while (t)pop(); for (i=1;i<=cq;i++)all+=ans[i]; printf ( "%lld" ,all); return 0; } |
F. False figures
留坑。
G. Grouping
首先将a从小到大排序,那么相当于要求把a分成k段,每段计算所有数到区间中位数的差的绝对值之和,要求最小化这个和。
因为a有序,所以计算一段的代价可以用前缀和做到O(1)。
设f[i][j]表示前j个数分成i段的最小代价,那么f[i][j]=min,(0\leq k<j)。
转移具有决策单调性,所以可以分治求解。
时间复杂度O(nk\log n)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #include<cstdio> #include<algorithm> const int N=5005,inf=2100000000; typedef unsigned int U; int n,m,i,o,a[N],s[N];U f[2][N]; inline U cal( int l, int r){ int m=(l+r)>>1; return (m+m-l-r)*a[m]-s[m]+s[l-1]+s[r]-s[m-1]; } void dp( int l, int r, int dl, int dr){ int m=(l+r)>>1,dm=dl; U tmp=inf; for ( int i=dl;i<=dr&&i<m;i++){ U now=f[o^1][i]+cal(i+1,m); if (now<tmp)tmp=now,dm=i; } f[o][m]=tmp; if (l<m)dp(l,m-1,dl,dm); if (r>m)dp(m+1,r,dm,dr); } int main(){ scanf ( "%d%d" ,&n,&m); for (i=1;i<=n;i++) scanf ( "%d" ,&a[i]); std::sort(a+1,a+n+1); for (i=1;i<=n;i++)s[i]=s[i-1]+a[i]; for (i=1;i<=n;i++)f[0][i]=inf; while (m--){ o^=1; for (i=0;i<=n;i++)f[o][i]=inf; dp(1,n,0,n); } printf ( "%u" ,f[o][n]); return 0; } |
H. Hidden triangles
留坑。
I. Interactive
用FFT加速多项式乘法即可,注意需要开long double。
时间复杂度O(n\log n)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int N=131100; int n,i,j,k,pos[N],a[N],b[N]; long long c[N]; namespace FFT{ struct comp{ long double r,i;comp( long double _r=0, long double _i=0){r=_r,i=_i;} comp operator+( const comp&x){ return comp(r+x.r,i+x.i);} comp operator-( const comp&x){ return comp(r-x.r,i-x.i);} comp operator*( const comp&x){ return comp(r*x.r-i*x.i,r*x.i+i*x.r);} comp conj(){ return comp(r,-i);} }A[N],B[N]; const long double pi= acos (-1.0); void FFT(comp a[], int n, int t){ for ( int i=1;i<n;i++) if (i<pos[i])swap(a[i],a[pos[i]]); for ( int d=0;(1<<d)<n;d++){ int m=1<<d,m2=m<<1; long double o=pi*2/m2*t;comp _w( cos (o), sin (o)); for ( int i=0;i<n;i+=m2){ comp w(1,0); for ( int j=0;j<m;j++){ comp&A=a[i+j+m],&B=a[i+j],t=w*A; A=B-t;B=B+t;w=w*_w; } } } if (t==-1) for ( int i=0;i<n;i++)a[i].r/=n; } void mul( int *a, int *b, long long *c){ int i,j; for (i=0;i<k;i++)A[i]=comp(a[i],b[i]); FFT(A,k,1); for (i=0;i<k;i++){ j=(k-i)&(k-1); B[i]=(A[i]*A[i]-(A[j]*A[j]).conj())*comp(0,-0.25); } FFT(B,k,-1); for (i=0;i<k;i++)c[i]=( long long )(B[i].r+0.5); } } int main(){ scanf ( "%d" ,&n); for (k=1;k<n;k<<=1);k<<=1; j=__builtin_ctz(k)-1; for (i=0;i<k;i++)pos[i]=pos[i>>1]>>1|((i&1)<<j); for (i=0;i<n;i++) scanf ( "%d" ,&a[i]); for (i=0;i<n;i++) scanf ( "%d" ,&b[i]); FFT::mul(a,b,c); for (i=0;i<n+n-1;i++) printf ( "%lld%c" ,c[i],i<n+n-2? ' ' : '\n' ); return 0; } |
J. Joinery
枚举长方体的8个端点,然后枚举6个面,在每个面上三分套三分查找表面距离最远的点即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | #include<cstdio> #include<cmath> #include<algorithm> using namespace std; double ans,r; const double eps=1e-8; double LL,WW,HH; struct P{ double x,y,z; P(){} P( double _x, double _y, double _z){x=_x,y=_y,z=_z;} }a[8]; void turn( int i, int j, double x, double y, double z, double x0, double y0, double L, double W, double H){ if (z==0){ double R=x*x+y*y; if (R<r)r=R; } else { if (i>=0&&i<2)turn(i+1,j,x0+L+z,y,x0+L-x,x0+L,y0,H,W,L); if (j>=0&&j<2)turn(i,j+1,x,y0+W+z,y0+W-y,x0,y0+W,L,H,W); if (i<=0&&i>-2)turn(i-1,j,x0-z,y,x-x0,x0-H,y0,H,W,L); if (j<=0&&j>-2)turn(i,j-1,x,y0-z,y-y0,x0,y0-H,L,H,W); } } double cal( double x1, double y1, double z1, double x2, double y2, double z2){ double L=LL,W=WW,H=HH; if ( fabs (z1)>eps&& fabs (z1-H)>eps) if ( fabs (y1)<eps|| fabs (y1-W)<eps) swap(y1,z1),swap(y2,z2),swap(W,H); else swap(x1,z1),swap(x2,z2),swap(L,H); if ( fabs (z1-H)<eps)z1=0,z2=H-z2; r=1e20; turn(0,0,x2-x1,y2-y1,z2,-x1,-y1,L,W,H); return r; } double solvexy( double x, double y, double z, double A, double B, double C){ double l=0,r=C,m1,m2,s1,s2; double ret=0; while (l+eps<r){ double len=(r-l)/3.0; m1=l+len; m2=r-len; s1=cal(x,y,z,A,B,m1); s2=cal(x,y,z,A,B,m2); ret=max(ret,max(s1,s2)); if (s1>s2)r=m2; else l=m1; } return ret; } double solvexz( double x, double y, double z, double A, double B, double C){ double l=0,r=B,m1,m2,s1,s2; double ret=0; while (l+eps<r){ double len=(r-l)/3.0; m1=l+len; m2=r-len; s1=cal(x,y,z,A,m1,C); s2=cal(x,y,z,A,m2,C); ret=max(ret,max(s1,s2)); if (s1>s2)r=m2; else l=m1; } return ret; } double solvex( double x, double y, double z, double A, double B, double C){ double l=0,r=B,m1,m2,s1,s2; double ret=0; while (l+eps<r){ double len=(r-l)/3.0; m1=l+len; m2=r-len; s1=solvexy(x,y,z,A,m1,C); s2=solvexy(x,y,z,A,m2,C); ret=max(ret,max(s1,s2)); if (s1>s2)r=m2; else l=m1; } return ret; } double solvey( double x, double y, double z, double A, double B, double C){ double l=0,r=A,m1,m2,s1,s2; double ret=0; while (l+eps<r){ double len=(r-l)/3.0; m1=l+len; m2=r-len; s1=solvexy(x,y,z,m1,B,C); s2=solvexy(x,y,z,m2,B,C); ret=max(ret,max(s1,s2)); if (s1>s2)r=m2; else l=m1; } return ret; } double solvez( double x, double y, double z, double A, double B, double C){ double l=0,r=A,m1,m2,s1,s2; double ret=0; while (l+eps<r){ double len=(r-l)/3.0; m1=l+len; m2=r-len; s1=solvexz(x,y,z,m1,B,C); s2=solvexz(x,y,z,m2,B,C); ret=max(ret,max(s1,s2)); if (s1>s2)r=m2; else l=m1; } return ret; } int main(){ int i,j; double L,H,W; scanf ( "%lf%lf%lf" ,&L,&W,&H); a[0]=P(0,0,0); a[1]=P(0,0,H); a[2]=P(0,W,0); a[3]=P(0,W,H); a[4]=P(L,0,0); a[5]=P(L,0,H); a[6]=P(L,W,0); a[7]=P(L,W,H); LL=L; WW=W; HH=H; for (i=0;i<8;i++) for (j=0;j<8;j++) ans=max(ans,cal(a[i].x,a[i].y,a[i].z,a[j].x,a[j].y,a[j].z)); for (i=0;i<8;i++){ ans=max(ans,solvex(a[i].x,a[i].y,a[i].z,0,W,H)); ans=max(ans,solvex(a[i].x,a[i].y,a[i].z,L,W,H)); ans=max(ans,solvey(a[i].x,a[i].y,a[i].z,L,0,H)); ans=max(ans,solvey(a[i].x,a[i].y,a[i].z,L,W,H)); ans=max(ans,solvez(a[i].x,a[i].y,a[i].z,L,W,0)); ans=max(ans,solvez(a[i].x,a[i].y,a[i].z,L,W,H)); } double ret= sqrt (ans); printf ( "%.13f" ,ret); return 0; } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 《HelloGitHub》第 106 期
· 数据库服务器 SQL Server 版本升级公告
· C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)
2015-10-08 BZOJ3165 : [Heoi2013]Segment
2015-10-08 BZOJ2725 : [Violet 6]故乡的梦