ZOJ Monthly, January 2018
A. Candy Game
显然最优策略是一个一个吃,故比较哪种糖果的个数比较多即可。
1 2 3 4 5 6 7 8 9 10 11 12 | #include<cstdio> int T,n,i,x,sum; int main(){ scanf ( "%d" ,&T); while (T--){ scanf ( "%d" ,&n); sum=0; for (i=1;i<=n;i++) scanf ( "%d" ,&x),sum+=x; for (i=1;i<=n;i++) scanf ( "%d" ,&x),sum-=x; puts (sum>0? "BaoBao" : "DreamGrid" ); } } |
B. PreSuffix
对所有串建立AC自动机,那么若前缀i是前缀j的后缀,说明i是Fail树上j的祖先。
所以对于询问(x,y),答案就是两点在Fail树上的LCA在原Trie中子树内的字符串总数。
时间复杂度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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=100010,M=500010; char s[M]; int tot,son[M][26],fail[M],q[M],val[M],G[M],NXT[M],size[M],heavy[M],top[M],d[M]; int n,i,j,m,x,y,pos[N]; inline void ins( int p){ int l= strlen (s),x=0; for ( int i=0,w;i<l;i++){ if (!son[x][w=s[i]- 'a' ])son[x][w]=++tot; x=son[x][w]; val[x]++; } pos[p]=x; } void make(){ int h=1,t=0,i,j,x;fail[0]=-1; for (i=0;i<26;i++) if (son[0][i])q[++t]=son[0][i]; while (h<=t) for (x=q[h++],i=0;i<26;i++) if (son[x][i])fail[son[x][i]]=son[fail[x]][i],q[++t]=son[x][i]; else son[x][i]=son[fail[x]][i]; } void dfs( int x){ size[x]=1; for ( int i=G[x];i;i=NXT[i]){ d[i]=d[x]+1; dfs(i); size[x]+=size[i]; if (heavy[x]<0)heavy[x]=i; else if (size[i]>size[heavy[x]])heavy[x]=i; } } void dfs2( int x, int y){ top[x]=y; if (~heavy[x])dfs2(heavy[x],y); for ( int i=G[x];i;i=NXT[i]) if (i!=heavy[x])dfs2(i,i); } inline int lca( int x, int y){ for (;top[x]!=top[y];x=fail[top[x]]) if (d[top[x]]<d[top[y]])swap(x,y); return d[x]<d[y]?x:y; } inline void ask( int x, int y){ x=pos[x]; y=pos[y]; int z=lca(x,y); if (!val[z]) puts ( "N" ); else printf ( "%d\n" ,val[z]); } int main(){ while (~ scanf ( "%d" ,&n)){ for (i=1;i<=n;i++) scanf ( "%s" ,s),ins(i); make(); for (i=1;i<=tot;i++)NXT[i]=G[fail[i]],G[fail[i]]=i; for (i=0;i<=tot;i++)heavy[i]=-1; dfs(0); dfs2(0,0); scanf ( "%d" ,&m); while (m--) scanf ( "%d%d" ,&x,&y),ask(x,y); for (i=0;i<=tot;i++){ for (fail[i]=G[i]=j=0;j<26;j++)son[i][j]=0; val[i]=size[i]=top[i]=d[i]=0; heavy[i]=-1; } tot=0; } return 0; } |
C. An Unsure Catch
考虑k=0的情形:
对于每个连通块,断开一条环边变成树,求出树上每个点的深度d。
设环长为len,则该连通块中最优解为d\bmod len中出现次数最多的那一个。
考虑k>0的情形,有两种最优策略:
1. 利用一步操作将某个环长修改为1,再用k-1步操作把其它k-1个连通块合并上来。
故此时答案为连通块点数最大的k个的点数之和。
2. 不刻意制造长度为1的环,直接用k步操作把k-1个连通块合并到某个点上。
枚举每个连通块作为最终合并点,假设其环长为K,则要选k-1个其它连通块,将它们各断开一条边,然后将环长修改为K,使得d\bmod K中出现次数最大值最大。
注意到K的取值只有O(\sqrt{n})种,枚举每个K,对于每个连通块计算最优解,然后计数排序统计前k大值的和即可。
对于每个连通块,假设环长为len,那么按顺序依次断开每条环边后,对断开点子树内所有d的影响是整体减去len,共O(n)次修改和O(n)次查询。
注意到每次修改时,只会将某个数加1或减1,故最大值也只会变化1,记录每种值有多少个即可判断最大值是否需要变化,时间复杂度O(1)。
总时间复杂度O(n\sqrt{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 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 | #include<cstdio> #include<algorithm> using namespace std; typedef pair< int , int >P; const int N=100010,inf=100000000; int Case,n,i,a[N],g[N],nxt[N]; bool vis[N],visit[N],on[N]; int d[N]; int f[N]; int id[N],m,ce; int fin[N]; int vs[N]; bool need[N]; int len[N],head[N],cur; P pool[N]; int nowmx,cf[N],have[N]; inline void add( int x, int y){nxt[y]=g[x];g[x]=y;} void dfs( int x){ if (vis[x]) return ; vis[x]=1; id[++m]=x; for ( int i=g[x];i;i=nxt[i])dfs(i); dfs(a[x]); } int cal( int x){ if (d[x]) return d[x]; return d[x]=cal(a[x])+1; } inline void solve(){ int x=id[1]; while (!visit[x])visit[x]=1,x=a[x]; int circle=0; while (!on[x]){ circle++; d[x]=inf-circle; on[x]=1,x=a[x]; } ce++; vs[ce]=m; need[circle]=1; len[ce]=circle; head[ce]=x; for ( int i=1;i<=m;i++)pool[++cur]=P(ce,cal(id[i])); } inline void up( int &a, int b){a<b?(a=b):0;} inline void addf( int x){ cf[++f[x]]++; up(nowmx,f[x]); } inline void delf( int x){ cf[f[x]--]--; if (!cf[nowmx])nowmx--; } int MO,L; void update( int x){ delf(d[x]%MO); addf((d[x]-L)%MO); for ( int i=g[x];i;i=nxt[i]) if (!on[i])update(i); } inline void mustlen( int K){ MO=K; int i,j,k=0,x; int hismax=0; for (i=1;i<=n+1;i++)have[i]=0; for (i=1;i<=n;i=j){ for (j=i;j<=n&&pool[i].first==pool[j].first;j++); int l=i,r=j-1; k++; nowmx=0; L=len[k]; int best=0; for (x=l;x<=r;x++)addf(pool[x].second%K); best=nowmx; int S=x=head[k]; while (1){ update(x); up(best,nowmx); x=a[x]; if (x==S) break ; } for (x=l;x<=r;x++)delf((pool[x].second-L)%K); have[best]++; if (L==K&&best>hismax)hismax=best; } have[hismax]--; up(fin[k=0],hismax); for (i=n;i;i--) for (j=have[i];j;j--)up(fin[++k],hismax+=i); for (k++;k<=n;k++)up(fin[k],hismax); } int main(){ scanf ( "%d" ,&Case); cf[0]=1e9; while (Case--){ scanf ( "%d" ,&n); for (i=0;i<=n;i++)g[i]=vis[i]=visit[i]=on[i]=d[i]=f[i]=id[i]=need[i]=0; m=ce=cur=0; for (i=1;i<=n;i++) scanf ( "%d" ,&a[i]),add(a[i],i); for (i=1;i<=n;i++) if (!vis[i])m=0,dfs(i),solve(); sort(vs+1,vs+ce+1);reverse(vs+1,vs+ce+1); for (i=1;i<=ce;i++)vs[i]+=vs[i-1]; sort(pool+1,pool+cur+1); for (i=0;i<=n;i++)fin[i]=vs[min(ce,i)]; for (i=1;i<=n;i++) if (need[i])mustlen(i); int k=0; for (i=0;i<=n;i++){ fin[i]=min(fin[i],n); k=i; if (fin[i]>=n) break ; } printf ( "%d\n" ,k); for (i=0;i<=k;i++) printf ( "%d%c" ,fin[i],i<k? ' ' : '\n' ); } } |
D. Seat Assignment
lcm(1,2,...,10)=2520,对于模lcm的每个余数k分析其是否是1到10的倍数,可以发现一共48种本质不同的情况。
那么计算出每种情况的容量之后,就转化成了左边10个点,右边48个点的最大流。
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 | #include<cstdio> const int N=10000,inf=~0U>>2; int lcm,m,i,j,Case,lim[N],q[N],mask[N],vis[N],cnt; int S,T,h[N],gap[N],maxflow; struct E{ int t,f;E*nxt,*pair;}*g[N],*d[N],pool[100000],*cur=pool; inline int min( int a, int b){ return a<b?a:b;} inline void add( int s, int t, int f){ E*p=cur++;p->t=t;p->f=f;p->nxt=g[s];g[s]=p; p=cur++;p->t=s;p->f=0;p->nxt=g[t];g[t]=p; g[s]->pair=g[t];g[t]->pair=g[s]; } int sap( int v, int flow){ if (v==T) return flow; int rec=0; for (E*p=d[v];p;p=p->nxt) if (h[v]==h[p->t]+1&&p->f){ int ret=sap(p->t,min(flow-rec,p->f)); p->f-=ret;p->pair->f+=ret;d[v]=p; if ((rec+=ret)==flow) return flow; } if (!(--gap[h[v]]))h[S]=T; gap[++h[v]]++;d[v]=g[v]; return rec; } int main(){ lcm=2520; for (i=0;i<lcm;i++){ //k%lcm=i int S=0; for (j=1;j<=10;j++) if (i%j==0)S|=1<<j; if (!vis[S]){ vis[S]=++cnt; q[cnt]=S; } mask[i]=vis[S]; } scanf ( "%d" ,&Case); while (Case--){ scanf ( "%d" ,&m); for (i=1;i<=cnt;i++)lim[i]=0; int sum=0; for (i=0;i<lcm;i++){ int now=m-i; if (now<0)now=0; now=now/lcm; if (i&&i<=m)now++; lim[mask[i]]+=now; } S=cnt+11; T=S+1; for (cur=pool,i=1;i<=T;i++)g[i]=d[i]=NULL,h[i]=gap[i]=0; for (i=1;i<=cnt;i++)add(S,i,lim[i]); for (i=1;i<=10;i++){ int x; scanf ( "%d" ,&x); add(cnt+i,T,x); if (x) for (j=1;j<=cnt;j++) if (q[j]>>i&1)add(j,cnt+i,x); } for (gap[maxflow=0]=T,i=1;i<=T;i++)d[i]=g[i]; while (h[S]<T)maxflow+=sap(S,inf); printf ( "%d\n" ,maxflow); } } /* 2^10 %1=0 %2= 1..m %lcm lcm=8*9*5*7 */ |
E. Yet Another Data Structure Problem
线段树维护序列乘积mul,标记(a,b)表示mul=a\times mul^b。
时间复杂度O(n\log n\log P)。
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 | #include<cstdio> const int N=100010,M=262150,P=1000000007; int Case,n,m,i,a[N],ans; int v[M],ta[M],tb[M],len[M]; inline int po( int a, int b){ int t=1; for (;b;b>>=1,a=1LL*a*a%P) if (b&1)t=1LL*t*a%P; return t; } inline void tag1( int x, int a, int b){ v[x]=1LL*po(v[x],b)*po(a,len[x])%P; ta[x]=1LL*po(ta[x],b)*a%P; tb[x]=1LL*tb[x]*b%(P-1); } inline void pb( int x){ if (ta[x]==1&&tb[x]==1) return ; tag1(x<<1,ta[x],tb[x]); tag1(x<<1|1,ta[x],tb[x]); ta[x]=tb[x]=1; } inline void up( int x){v[x]=1LL*v[x<<1]*v[x<<1|1]%P;} void build( int x, int a, int b){ ta[x]=tb[x]=1; len[x]=b-a+1; if (a==b){v[x]=::a[a]; return ;} int mid=(a+b)>>1; build(x<<1,a,mid),build(x<<1|1,mid+1,b); up(x); } void change( int x, int a, int b, int c, int d, int A, int B){ if (c<=a&&b<=d){tag1(x,A,B); return ;} pb(x); int mid=(a+b)>>1; if (c<=mid)change(x<<1,a,mid,c,d,A,B); if (d>mid)change(x<<1|1,mid+1,b,c,d,A,B); up(x); } void ask( int x, int a, int b, int c, int d){ if (c<=a&&b<=d){ans=1LL*ans*v[x]%P; return ;} pb(x); int mid=(a+b)>>1; if (c<=mid)ask(x<<1,a,mid,c,d); if (d>mid)ask(x<<1|1,mid+1,b,c,d); } int main(){ scanf ( "%d" ,&Case); while (Case--){ scanf ( "%d%d" ,&n,&m); for (i=1;i<=n;i++) scanf ( "%d" ,&a[i]); build(1,1,n); while (m--){ int op,l,r,k; scanf ( "%d%d%d" ,&op,&l,&r); if (op==1){ scanf ( "%d" ,&k); change(1,1,n,l,r,k,1); } if (op==2){ scanf ( "%d" ,&k); change(1,1,n,l,r,1,k); } if (op==3){ ans=1; ask(1,1,n,l,r); printf ( "%d\n" ,ans); } } } } |
UPD:
P=1000000007的原根g为5,如果将每个数都取指标的话,则变为模P-1意义下的区间加、区间乘、区间求和,可以线段树O(n\log n)维护。
对于每个询问,求出区间指标之和sum后,该询问的答案ans=g^{sum}\bmod P。
注意到只需要预处理1到1000的指标,故更改BSGS算法中的步长为300000,可以降低每次求指标的时间复杂度。
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 | #include<cstdio> #include<algorithm> #include<tr1/unordered_map> using namespace std; using namespace std::tr1; const int N=100010,M=262150,P=1000000007,Q=P-1; int Case,n,m,i,a[N],ans; int v[M],ta[M],tb[M],len[M]; inline int po( int a, int b){ int t=1; for (;b;b>>=1,a=1LL*a*a%P) if (b&1)t=1LL*t*a%P; return t; } namespace NT{ unordered_map< int , int >T; typedef pair< int , int >PI; const int K=300000,G=5; int ind[1005],base,i; inline int ask( int x){ if (x==1) return 0; int t=po(x,P-2); for ( int i=K;;i+=K){ t=1LL*t*base%P; unordered_map< int , int >::iterator it=T.find(t); if (it!=T.end()) return i-it->second; } } void init(){ for (base=1,i=0;i<K;i++){ T.insert(PI(base,i)); base=1LL*base*G%P; } for (i=1;i<=1000;i++)ind[i]=ask(i); } } inline void tag1( int x, int a, int b){ v[x]=(1LL*v[x]*b+1LL*a*len[x])%Q; tb[x]=1LL*tb[x]*b%Q; ta[x]=(1LL*ta[x]*b+a)%Q; } inline void pb( int x){ if (ta[x]==0&&tb[x]==1) return ; tag1(x<<1,ta[x],tb[x]); tag1(x<<1|1,ta[x],tb[x]); ta[x]=0,tb[x]=1; } inline void up( int x){v[x]=(v[x<<1]+v[x<<1|1])%Q;} void build( int x, int a, int b){ ta[x]=0,tb[x]=1; len[x]=b-a+1; if (a==b){v[x]=::a[a]; return ;} int mid=(a+b)>>1; build(x<<1,a,mid),build(x<<1|1,mid+1,b); up(x); } void change( int x, int a, int b, int c, int d, int A, int B){ if (c<=a&&b<=d){tag1(x,A,B); return ;} pb(x); int mid=(a+b)>>1; if (c<=mid)change(x<<1,a,mid,c,d,A,B); if (d>mid)change(x<<1|1,mid+1,b,c,d,A,B); up(x); } void ask( int x, int a, int b, int c, int d){ if (c<=a&&b<=d){ans=(ans+v[x])%Q; return ;} pb(x); int mid=(a+b)>>1; if (c<=mid)ask(x<<1,a,mid,c,d); if (d>mid)ask(x<<1|1,mid+1,b,c,d); } int main(){ NT::init(); scanf ( "%d" ,&Case); while (Case--){ scanf ( "%d%d" ,&n,&m); for (i=1;i<=n;i++) scanf ( "%d" ,&a[i]),a[i]=NT::ind[a[i]]; build(1,1,n); while (m--){ int op,l,r,k; scanf ( "%d%d%d" ,&op,&l,&r); if (op==1){ scanf ( "%d" ,&k); change(1,1,n,l,r,NT::ind[k],1); } if (op==2){ scanf ( "%d" ,&k); change(1,1,n,l,r,0,k); } if (op==3){ ans=0; ask(1,1,n,l,r); printf ( "%d\n" ,po(NT::G,ans)); } } } } |
F. The Limit
如果分子分母不同时为0,那么极限显然,否则根据洛必达法则分别求导计算。
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 | #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; typedef long long ll; const int N=15; char s[100000]; ll X; struct Poly{ ll v[N]; Poly(){ memset (v,0, sizeof v);} void read(){ memset (v,0, sizeof v); scanf ( "%s" ,s); int len= strlen (s); int i,j; for (i=0;i<len;){ if (s[i]== 'x' ){ if (s[i+1]== '^' ){ v[s[i+2]- '0' ]++; i+=3; continue ; } v[1]++; i++; continue ; } if (s[i]== '+' ||s[i]== '-' ){ int flag=s[i]== '+' ?1:-1; if (s[i+1]== 'x' ){ if (s[i+2]== '^' ){ v[s[i+3]- '0' ]+=flag; i+=4; continue ; } v[1]+=flag; i+=2; continue ; } else { //is number ll num=flag*(s[i+1]- '0' ); if (s[i+2]== 'x' ){ if (s[i+3]== '^' ){ v[s[i+4]- '0' ]+=num; i+=5; continue ; } v[1]+=num; i+=3; continue ; } else { v[0]+=num; i+=2; continue ; } } } //is number ll num=s[i]- '0' ; if (s[i+1]== 'x' ){ if (s[i+2]== '^' ){ v[s[i+3]- '0' ]+=num; i+=4; continue ; } v[1]+=num; i+=2; continue ; } else { v[0]+=num; i++; continue ; } } } ll f(ll x){ ll ret=0,t=1; for ( int i=0;i<N;i++){ ret+=v[i]*t; t*=x; } return ret; } void write(){ for ( int i=0;i<N;i++) if (v[i]) printf ( "%lldx^%d " ,v[i],i); puts ( "." ); } void dao(){ for ( int i=1;i<N;i++)v[i-1]=v[i]*i; v[N-1]=0; } }A,B; ll gcd(ll a,ll b){ return b?gcd(b,a%b):a;} void solve(){ A.read(); B.read(); scanf ( "%lld" ,&X); while (1){ ll U=A.f(X),D=B.f(X); if (!U&&D){ puts ( "0" ); return ; } if (U&&!D){ puts ( "INF" ); return ; } if (U&&D){ ll d=gcd( abs (U), abs (D)); U/=d,D/=d; if (D<0)U*=-1,D*=-1; if (D!=1) printf ( "%lld/%lld\n" ,U,D); else printf ( "%lld\n" ,U); return ; } A.dao(); B.dao(); } } int main(){ int Case; scanf ( "%d" ,&Case); while (Case--)solve(); } |
G. It's High Noon
假设人位于(x,y),若攻击部分背对原点,那么显然最优情况下(x,y)=(0,0)。将所有点极角排序然后双指针枚举即可。注意特判点在原点的情况。
若攻击部分面向原点,那么显然最优情况下x^2+y^2=R^2,且直线与圆相切。抠除圆内部的所有点之后,对于剩下的点求出切线角度,那么角度在某个区间内的直线都能取到这个点,扫描线统计即可。
时间复杂度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 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 | #include<cstdio> #include<cmath> #include<algorithm> using namespace std; const int N=500000; const double pi= acos (-1.0),PI= acos (-1.0)*2.0,eps=1e-9; int Case,n,K,i,ans; struct P{ int x,y; }a[N],b[N]; inline int pos( int x, int y){ return x?x>0:y>0;} inline int cross( const P&a, const P&b){ return a.x*b.y-a.y*b.x;} inline bool cmp( const P&a, const P&b){ if (pos(a.x,a.y)!=pos(b.x,b.y)) return pos(a.x,a.y)<pos(b.x,b.y); return cross(a,b)<0; } struct E{ double x; int y; E(){} E( double _x, int _y){x=_x,y=_y;} }e[N]; inline bool cmpe( const E&a, const E&b){ return a.x<b.x;} void solve_center(){ int atcenter=0; int m=0; int i,j; for (i=1;i<=n;i++) if (!a[i].x&&!a[i].y)atcenter++; else { b[++m]=a[i]; } ans=max(ans,atcenter); sort(b+1,b+m+1,cmp); for (i=1;i<=m;i++)b[i+m]=b[i]; for (i=j=1;i<=m;i++){ if (j<i)j=i; while (j+1<i+m&&cross(b[i],b[j+1])<=0)j++; ans=max(ans,j-i+1+atcenter); } } inline int sgn( double x){ if (x>eps) return 1; if (x<-eps) return -1; return 0; } inline void fix( double &x){ while (sgn(x)<0)x+=PI; while (sgn(x-PI)>=0)x-=PI; x=max(x,( double )0.0); } const double eps2=eps*5; void solve_ka(){ int all=0; int i,j,k; int m=0; for (i=1;i<=n;i++){ int dis=a[i].x*a[i].x+a[i].y*a[i].y; if (dis>K){ double o= atan2 (-a[i].y,-a[i].x); double w= acos ( sqrt (max(1.0-1.0*K/dis,0.0))); w= fabs (w); double A=o-w,B=o+w+pi; double l=A,r=B; l-=eps2,r+=eps2; fix(l); fix(r); if (!sgn(l-r))r=l+eps; //printf("%.10f %.10f\n",l,r); if (l>r)all++; //[l..r] 1 else 0 e[++m]=E(l,1); e[++m]=E(r,-1); } else all++; } ans=max(ans,all); sort(e+1,e+m+1,cmpe); for (i=1;i<=m;i++){ all+=e[i].y; ans=max(ans,all); } } int main(){ scanf ( "%d" ,&Case); while (Case--){ scanf ( "%d%d" ,&n,&K);K*=K; for (i=1;i<=n;i++) scanf ( "%d%d" ,&a[i].x,&a[i].y); ans=0; solve_center(); solve_ka(); printf ( "%d\n" ,ans); } } |
H. Traveling Plan
对于每个点x求出d_x表示离它最近的补给站到它的距离。
对于每条边(x,y,w),将边权重置为d_x+d_y+w。
那么对于询问(x,y),答案就是最小生成树上两点间边权的最大值。
时间复杂度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 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 | #include<cstdio> #include<algorithm> #include<vector> #include<queue> using namespace std; typedef long long ll; typedef pair<ll, int >P; const int N=100010,M=200010,K=20; const ll inf=1LL<<60; int n,m,q,x,y,i,j,f[N],g[M<<1],v[M<<1],nxt[M<<1],ed;ll w[M<<1]; int d[N],fa[N][K]; ll fw[N][K]; ll dis[N]; bool vis[N]; priority_queue<P,vector<P>,greater<P> >Q; struct E{ int x,y;ll z;}e[M]; inline bool cmp( const E&a, const E&b){ return a.z<b.z;} int F( int x){ return f[x]==x?x:f[x]=F(f[x]);} inline void add( int x, int y,ll z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;} void dfs( int x, int y){ vis[x]=1; for ( int i=1;i<K;i++){ fa[x][i]=fa[fa[x][i-1]][i-1]; fw[x][i]=max(fw[x][i-1],fw[fa[x][i-1]][i-1]); } for ( int i=g[x];i;i=nxt[i]) if (v[i]!=y){ fa[v[i]][0]=x; fw[v[i]][0]=w[i]; d[v[i]]=d[x]+1; dfs(v[i],x); } } inline ll ask( int x, int y){ ll ret=0; if (d[x]<d[y])swap(x,y); for ( int i=K-1;~i;i--) if (d[fa[x][i]]>=d[y]){ ret=max(ret,fw[x][i]); x=fa[x][i]; } if (x==y) return ret; for ( int i=K-1;~i;i--) if (fa[x][i]!=fa[y][i]){ ret=max(ret,max(fw[x][i],fw[y][i])); x=fa[x][i]; y=fa[y][i]; } return max(ret,max(fw[x][0],fw[y][0])); } int main(){ scanf ( "%d%d" ,&n,&m); for (i=1;i<=n;i++){ scanf ( "%d" ,&x); if (x==1){ Q.push(P(0,i)); } else { dis[i]=inf; } } for (i=1;i<=m;i++){ scanf ( "%d%d%lld" ,&e[i].x,&e[i].y,&e[i].z); add(e[i].x,e[i].y,e[i].z); add(e[i].y,e[i].x,e[i].z); } while (!Q.empty()){ P t=Q.top();Q.pop(); if (dis[t.second]<t.first) continue ; for (i=g[t.second];i;i=nxt[i]) if (dis[v[i]]>t.first+w[i])Q.push(P(dis[v[i]]=t.first+w[i],v[i])); } for (i=1;i<=m;i++)e[i].z+=dis[e[i].x]+dis[e[i].y]; sort(e+1,e+m+1,cmp); for (i=1;i<=n;i++)g[i]=0; ed=0; for (i=1;i<=n;i++)f[i]=i; for (i=1;i<=m;i++) if (F(e[i].x)!=F(e[i].y)){ f[f[e[i].x]]=f[e[i].y]; add(e[i].x,e[i].y,e[i].z); add(e[i].y,e[i].x,e[i].z); } for (i=1;i<=n;i++) if (!vis[i])dfs(i,0); scanf ( "%d" ,&q); while (q--) scanf ( "%d%d" ,&x,&y), printf ( "%lld\n" ,ask(x,y)); } |
I. Wooden Bridge
留坑。
J. Distance
枚举O(n^2)对区间右端点,左端点的取值满足单调性,双指针即可。
时间复杂度O(n^2)。
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 | #include<cstdio> typedef long long ll; const int N=1010; int Case,n,p,i,j,k,ans,g[N*3];ll a[N],b[N],f[N][N],w[N*3],V; inline ll cal(ll x){ if (x<0)x=-x; ll t=1; for ( int i=0;i<p;i++)t*=x; return t; } int main(){ scanf ( "%d" ,&Case); while (Case--){ scanf ( "%d%lld%d" ,&n,&V,&p); for (i=1;i<=n;i++) scanf ( "%lld" ,&a[i]); for (i=1;i<=n;i++) scanf ( "%lld" ,&b[i]); ans=0; for (i=1;i<=n;i++) for (j=1;j<=n;j++)f[i][j]=cal(a[i]-b[j]); for (i=0;i<=n+n+5;i++)g[i]=w[i]=0; for (i=1;i<=n;i++) for (j=1;j<=n;j++){ k=i-j+n; g[k]++; w[k]+=f[i][j]; int G=g[k];ll W=w[k]; while (G>0&&W>V){ G--; W-=f[i-G][j-G]; } g[k]=G; w[k]=W; ans+=G; } printf ( "%d\n" ,ans); } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步