A. The Motorway
等價於找到最小和最大的$L$滿足存在$S$使得$S+(i-1)L\leq a_i\leq S+i\times L$
即
$S\leq\min((1-i)L+a_i)$
$S\geq\max(-i\times L+a_i)$
求出上下凸殼的交點即可,因為斜率本身有序,故時間複雜度為$O(n)$。
#include<cstdio> const int N=1000010,BUF=12000000; const double inf=1e30,eps=1e-9; char Buf[BUF],*buf=Buf; int n,i,j,b[N],ka[N],kb[N],qa[N],ta,qb[N],tb; double ansl=inf,ansr=-inf,pre,A,B,ca[N],cb[N]; inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;} inline int sgn(double x){ if(x>eps)return 1; if(x<-eps)return -1; return 0; } inline double posa(int x,int y){return 1.0*(b[y]-b[x])/(ka[x]-ka[y]);} inline double posb(int x,int y){return 1.0*(b[y]-b[x])/(kb[x]-kb[y]);} inline double posab(int x,int y){ if(ka[x]==kb[y])return 1e100; return 1.0*(b[y]-b[x])/(ka[x]-kb[y]); } inline void work(int x,int y,double now){ double o=posab(qa[x],qb[y]); if(sgn(o-pre)>=0&&sgn(o-now)<=0){ if(o<ansl)ansl=o; if(o>ansr)ansr=o; } pre=now; } int main(){ fread(Buf,1,BUF,stdin);read(n); for(i=1;i<=n;i++)read(b[i]),ka[i]=1-i,kb[i]=-i; for(i=1;i<=n;qa[++ta]=i++)while(ta>1&&posa(qa[ta-1],qa[ta])>posa(qa[ta],i))ta--; for(i=n;i;qb[++tb]=i--)while(tb>1&&posb(qb[tb-1],qb[tb])>posb(qb[tb],i))tb--; for(i=1;i<ta;i++)ca[i]=posa(qa[i],qa[i+1]); for(i=1;i<tb;i++)cb[i]=posb(qb[i],qb[i+1]); ca[ta]=cb[tb]=inf; i=j=1,pre=-inf; while(i<=ta&&j<=tb){ A=ca[i],B=cb[j]; if(!sgn(A-B))work(i++,j++,A); else if(A<B)work(i++,j,A); else work(i,j++,B); } return printf("%.12f %.12f",ansl,ansr),0; }
B. Bytehattan
在平面圖中,兩個點不連通等價於在對偶圖中兩個點連通,並查集維護。
#include<cstdio> int n,k,i,x,y,a,b,f[2250000],t;char op,s[100]; 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';} int F(int x){return f[x]==x?x:f[x]=F(f[x]);} inline int P(int x,int y){return !x||x==n||!y||y==n?0:(x-1)*(n-1)+y;} int main(){ read(n),read(k); for(i=1;i<n*n;i++)f[i]=i; while(k--){ read(x),read(y);while(!(((op=getchar())=='N')||(op=='E'))); if(t){read(x),read(y);while(!(((op=getchar())=='N')||(op=='E')));}else gets(s); a=op=='N'?P(x-1,y):P(x,y-1);b=P(x,y); if(F(a)==F(b))t=1,puts("NIE");else t=0,f[f[a]]=f[b],puts("TAK"); } return 0; }
C. The Carpenter
兩個三角形之間必然存在一條橫的或者豎的或者斜$45$度的分割線,求出每條分割線兩側的最優解即可。
時間複雜度$O(nm)$。
D. Demonstrations
離散化後用並查集染色維護出每一段被哪幾個區間覆蓋,然後列舉每段更新答案即可。
#include<cstdio> #include<algorithm> #include<map> using namespace std; const int N=1000010; int n,m,i,a[N],e[N][2],f[N],cnt[N],g[N][2],val[N],ans;map<int,int>T[N]; int F(int x){return f[x]==x?x:f[x]=F(f[x]);} inline void col(int l,int r,int x){ l=lower_bound(a+1,a+m+1,l)-a; while(1){ l=F(l); if(a[l]>=r)return; cnt[l]++; if(cnt[l]>2)f[l]++; else g[l][cnt[l]-1]=x; l++; } } int main(){ scanf("%d",&n); for(i=1;i<=n;i++){ scanf("%d%d",&e[i][0],&e[i][1]); a[++m]=e[i][0]; a[++m]=e[i][1]; } sort(a+1,a+m+1); for(i=1;i<=m;i++)f[i]=i; for(i=1;i<=n;i++)col(e[i][0],e[i][1],i); for(i=1;i<m;i++){ int x=g[i][0],y=g[i][1],z=a[i+1]-a[i]; if(cnt[i]==1)val[x]+=z; if(cnt[i]==2)T[x][y]+=z; } for(i=1;i<m;i++){ int x=g[i][0],y=g[i][1],z=a[i+1]-a[i]; if(cnt[i]==2)ans=max(ans,val[x]+val[y]+T[x][y]); } sort(val+1,val+n+1); ans=max(ans,val[n-1]+val[n]); printf("%d",ans); }
E. The Exam
按$n$奇偶性構造出相鄰差值最大的序列然後判斷是否不小於$k$即可,時間複雜度$O(n)$。
#include<cstdio> const int N=1000010,OUT=10000000; int n,m,k,i,j,x,y,w[N],a[N];char Out[OUT],*ou=Out;int Outn[30],Outcnt; inline void write(int x){ if(!x)*ou++=48; else{ for(Outcnt=0;x;x/=10)Outn[++Outcnt]=x%10+48; while(Outcnt)*ou++=Outn[Outcnt--]; } } inline int abs(int x){return x>0?x:-x;} int main(){ scanf("%d%d",&n,&k); for(i=0;i<n;i++)w[i]=i+1; if(n&1){ m=n/2; for(i=j=m;i<n;i++)if(w[i]-w[i-m]<w[j]-w[j-m])j=i; x=j,y=(x+m)%n; for(i=0;i<n;i+=2,x=(x+n-1)%n)a[i]=x; for(i=1;i<n;i+=2,y=(y+n-1)%n)a[i]=y; }else{ for(i=1,j=0;i<n;i+=2,j++)a[i]=j; for(i=0;i<n;i+=2,j++)a[i]=j; } for(i=1;i<n;i++)if(abs(w[a[i]]-w[a[i-1]])<k)return puts("NIE"),0; for(i=0;i<n;i++)write(w[a[i]]),*ou++=' '; fwrite(Out,1,ou-Out,stdout); return 0; }
F. Speed Cameras
不斷剝葉子得出每個點的層次,那麼前$\lfloor\frac{k}{2}\rfloor$層必選,若$k$為奇數則還要多選一個點。
#include<cstdio> const int N=1000010; int n,m,k,i,x,y,deg[N],d[N],g[N],v[N<<1],nxt[N<<1],ed,h,t,q[N],ans,vis[N]; inline void add(int x,int y){deg[x]++;v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} inline void ext(int x,int y){ deg[x]--; if(deg[x]>1)return; if(!d[x])d[q[++t]=x]=y; } int main(){ scanf("%d%d",&n,&m); for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x); for(h=i=1;i<=n;i++)if(deg[i]<=1)ext(i,1); while(h<=t){ x=q[h++]; for(i=g[x];i;i=nxt[i])ext(v[i],d[x]+1); } for(i=1;i<=n;i++)if(d[i]*2<=m)vis[i]=1; for(i=1;i<=n;i++)if(!vis[i]&&(m&1)){vis[i]=1;break;} for(i=1;i<=n;i++)if(vis[i])ans++; printf("%d\n",ans); for(i=1;i<=n;i++)if(vis[i])printf("%d ",i); }
G. Game
若$0$只有$1$個,那麼顯然無解。
若$0$超過$1$個,那麼顯然有解。
若$2,3,5,7$這些質數出現次數為奇數,那麼顯然無解。
那麼將剩下每個數出現次數不斷減$2$直到$\leq 6$都不會影響結果。
設$f[i][j][k]$表示質數$2$的指數為$i$,$3$的指數為$j$,選了$k$個數是否可能,bitset優化DP即可。
#include<cstdio> typedef long long ll; const int lim=6; int Case,i,j,x,y;ll a[10]; unsigned long long f[lim*7+1][lim*4+1]; bool solve(){ if(a[0]==1)return 0; if(a[0]>1)return 1; if(a[5]&1)return 0; a[5]=0; if(a[7]&1)return 0; a[7]=0; ll c2=a[2]+a[4]*2+a[6]+a[8]*3; if(c2&1)return 0; ll c3=a[3]+a[6]+a[9]*2; if(c3&1)return 0; for(i=0;i<=lim*7;i++)for(j=0;j<=lim*4;j++)f[i][j]=0; f[0][0]=1; int s2=0,s3=0,s=0; for(i=0;i<10;i++){ if(a[i]>lim)a[i]-=(a[i]-lim)/2*2; while(a[i]>lim)a[i]-=2; if(!a[i])continue; int A=0,B=0; if(i==2)A=1; if(i==3)B=1; if(i==4)A=2; if(i==6)A=B=1; if(i==8)A=3; if(i==9)B=2; for(j=0;j<a[i];j++){ s2+=A,s3+=B,s++; for(x=lim*7;x>=A;x--)for(y=lim*4;y>=B;y--)f[x][y]|=f[x-A][y-B]<<1; } } return f[s2/2][s3/2]>>(s/2)&1; } int main(){ scanf("%d",&Case); while(Case--){ for(i=0;i<10;i++)scanf("%lld",&a[i]); puts(solve()?"TAK":"NIE"); } }
H. The Hero
將每個點按存在時間段拆點,共$O(n+p)$個點。
進行Dijkstra求最短路,對於每個拆出來的點列舉所有出邊進行鬆弛。
同時對於每個點維護所有拆點集合set,每次用$l$鬆弛距離時將其從set中刪除。
時間複雜度$O(10(n+q)\log n)$。
#include<cstdio> #include<algorithm> #include<queue> #include<vector> #include<set> using namespace std; typedef long long ll; typedef pair<ll,int>P; const int N=100010,M=1000010; const ll inf=1LL<<60; const int BUF=40000000; char Buf[BUF],*buf=Buf; inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;} inline void read(ll&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;} int n,m,cnt,i,j,k,x,y,z,g[N],v[M],w[M],nxt[M],ed,st[N],en[N]; bool vis[N<<1]; struct E{ int x;ll l,r; E(){} E(int _x,ll _l,ll _r){x=_x,l=_l,r=_r;} }e[N],p[N<<1]; set<P>T[N]; ll d[N<<1],ans=inf; priority_queue<P,vector<P>,greater<P> >q; inline bool cmp(const E&a,const E&b){ if(a.x!=b.x)return a.x<b.x; return a.l<b.l; } inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;} inline void ext(int x,ll y){ if(y>p[x].r)return; if(vis[x])return; if(y>=d[x])return; d[x]=y; q.push(P(y,x)); } inline void work(ll R,ll dis,int x,int len){ dis+=len; set<P>::iterator it=T[x].lower_bound(P(dis,0)); if(it==T[x].end())return; if(p[it->second].l<=dis)ext(it->second,dis),it++; while(it!=T[x].end()){ if(p[it->second].l-len>R)return; ext(it->second,p[it->second].l); set<P>::iterator nxt=it; nxt++; T[x].erase(it); it=nxt; } } int main(){ fread(Buf,1,BUF,stdin); read(n),read(m); while(m--)read(x),read(y),read(z),add(x,y,z); read(m); for(i=1;i<=m;i++)read(e[i].x),read(e[i].l),read(e[i].r); 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++); ll pre=0; x=e[i].x; st[x]=cnt+1; for(k=i;k<j;k++){ if(e[k].l>pre+1)p[++cnt]=E(x,pre+1,e[k].l-1); pre=max(pre,e[k].r); } p[++cnt]=E(x,pre+1,inf); en[x]=cnt; } for(i=1;i<=n;i++)if(!st[i]){ p[++cnt]=E(i,1,inf); st[i]=en[i]=cnt; } for(i=1;i<=cnt;i++)d[i]=inf; for(i=1;i<=n;i++)for(j=st[i];j<=en[i];j++)T[i].insert(P(p[j].r,j)); ext(st[1],1); while(!q.empty()){ P t=q.top();q.pop(); if(vis[t.second])continue; vis[t.second]=1; for(i=g[p[t.second].x];i;i=nxt[i])work(p[t.second].r,t.first,v[i],w[i]); } for(i=st[n];i<=en[n];i++)ans=min(ans,d[i]); if(ans>=inf)puts("NIE");else printf("%lld",ans-1); }
I. Genetic Engineering
設$f[i]$表示以$i$為開頭一段的結尾最多能有多少段,$g[i]$表示以$i$為開頭一段的開頭最多能有多少段。
則$f[i]=\max(g[>i])+1,g[i]=f[i後面第k個和i數值相同的位置]$。
若有多個最大值,選數值最小的,再有多個選下標最靠前的,這樣可以滿足字典序最小。
時間複雜度$O(n)$。
#include<cstdio> #include<vector> using namespace std; const int N=1000010; int n,m,i,rk[N],a[N],f[N],g[N],nxt[N],mx,pos; vector<int>v[N]; int main(){ scanf("%d%d",&n,&m); for(i=1;i<=n;i++){ scanf("%d",&a[i]); v[a[i]].push_back(i); rk[i]=v[a[i]].size()-1; } mx=0,pos=n+1; for(i=n;i;i--){ f[i]=mx+1; nxt[i]=pos; if(rk[i]+m-1<v[a[i]].size()){ g[i]=f[v[a[i]][rk[i]+m-1]]; if(g[i]>mx||g[i]==mx&&a[i]<=a[pos])mx=g[i],pos=i; } } printf("%d\n",mx*m); while(mx--){ for(i=1;i<=m;i++)printf("%d ",a[pos]); pos=nxt[v[a[pos]][rk[pos]+m-1]]; } }
J. Robin Hood
$ans=\sum_{i=1}^n i-2^{\lfloor\log_2i\rfloor}$,數位DP求出每種$\log$的$i$的個數即可。
#include<cstdio> const int N=50; int n,i,j,k,nj,x,m,a[N];long long ans,f[N][2][N]; int main(){ scanf("%d",&n); m=40; ans=1LL*n*(n+1)/2; for(i=1;i<=m;i++)a[i]=n&1,n>>=1; f[m][0][0]=1; for(i=m;i>1;i--)for(j=0;j<2;j++)for(k=0;k<=m;k++)for(x=0;x<2;x++){ nj=j; if(!j){ if(x>a[i-1])continue; if(x<a[i-1])nj=1; } f[i-1][nj][x?(k?k:(i-1)):k]+=f[i][j][k]; } for(j=0;j<2;j++)for(k=1;k<=m;k++)ans-=f[1][j][k]<<(k-1); printf("%lld",ans); }
K. Blanket
掃描線+線段樹統計出每個方格被經過的矩形數目的平方和即可,時間複雜度$O(n\log n)$。
#include<cstdio> #include<algorithm> using namespace std; typedef long long ll; const int N=400010,M=1200000; int n,m,i,X,Y,A,B,ca,ce,a[N],w[M],tag[M];ll wv[M],wvv[M];long double ans; struct E{int x,l,r,t;E(){}E(int _x,int _l,int _r,int _t){x=_x,l=_l,r=_r,t=_t;}}e[N]; inline bool cmp(const E&a,const E&b){return a.x<b.x;} 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';} void build(int x,int a,int b){ w[x]=::a[b]-::a[a]; if(a+1>=b)return; int mid=(a+b)>>1; build(x<<1,a,mid),build(x<<1|1,mid,b); } inline void tag1(int x,int p){ wvv[x]+=2LL*p*wv[x]+1LL*p*p*w[x]; wv[x]+=1LL*p*w[x]; tag[x]+=p; } void change(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)change(x<<1,a,mid,c,d,p); if(d>mid)change(x<<1|1,mid,b,c,d,p); wv[x]=wv[x<<1]+wv[x<<1|1]; wvv[x]=wvv[x<<1]+wvv[x<<1|1]; } int main(){ read(n);read(A),read(B); for(i=1;i<=n;i++){ read(X),read(Y); if(!A||!B)continue; ans-=1LL*A*B; a[++ca]=Y; a[++ca]=Y+B; e[++ce]=E(X,Y,Y+B,1); e[++ce]=E(X+A,Y,Y+B,-1); } sort(e+1,e+ce+1,cmp); sort(a+1,a+ca+1); for(i=1;i<=ca;i++)if(i==1||a[i]!=a[i-1])a[++m]=a[i]; build(1,1,m); for(i=1;i<=ce;i++){ if(i>1&&e[i].x>e[i-1].x)ans+=1.0*wvv[1]*(e[i].x-e[i-1].x); change(1,1,m,lower_bound(a+1,a+m+1,e[i].l)-a,lower_bound(a+1,a+m+1,e[i].r)-a,e[i].t); } return printf("%.15f",(double)(ans/n/(n-1))),0; }