zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    EditPlus等编辑器选中列(块)的方法
    构建Springboot项目的3种方式
    STS各版本下载
    Spring Boot 各版本的Java版本要求
    Maven安装
    Linux find命令:在目录中查找文件(超详解)
    rpm命令怎么指定安装位置
    CentOS6.8安装RabbitMQ
    codeforces459D:Pashmak and Parmida's problem
    codeforces 705B:Spider Man
  • 原文地址:https://www.cnblogs.com/clrs97/p/8843761.html
Copyright © 2011-2022 走看看