XIV Open Cup named after E.V. Pankratiev. GP of Europe

A. The Motorway

等价于找到最小和最大的$L$满足存在$S$使得$S+(i-1)Lleq a_ileq S+i imes L$

$Sleqmin((1-i)L+a_i)$

$Sgeqmax(-i imes 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

不断剥叶子得出每个点的层次,那么前$lfloorfrac{k}{2} floor$层必选,若$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
",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
",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^{lfloorlog_2i floor}$,数位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(nlog 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;
}

  

原文地址:https://www.cnblogs.com/clrs97/p/8843761.html