• Potyczki Algorytmiczne 2011


    Trial Round:

    Tulips

    按题意模拟。

    #include<cstdio>
    const int N=15000;
    int n,ans=N,x,v[N+1];
    int main(){
      scanf("%d",&n);
      while(n--){
        scanf("%d",&x);
        if(!v[x])ans--,v[x]=1;
      }
      printf("%d",ans);
    }
    

      

    Round 1:

    Rooks [B]

    对于每个没有车的行,随便找一个没有车的列配对。

    #include<cstdio>
    const int N=1005;
    int n,i,j,a[N],b[N];char tmp[N];
    int main(){
      scanf("%d",&n);
      for(i=1;i<=n;i++){
        scanf("%s",tmp+1);
        for(j=1;j<=n;j++)if(tmp[j]=='W')b[a[i]=j]=1;
      }
      for(i=1;i<=n;i++)if(!a[i])for(j=1;j<=n;j++)if(!b[j]){b[a[i]=j]=1;break;}
      for(i=1;i<=n;i++){
        for(j=1;j<=n;j++)tmp[j]='.';
        tmp[a[i]]='W';
        puts(tmp+1);
      }
    }
    

      

    Round 2:

    Unlucky [A]

    如果$w$不是$k$的倍数,那么先考虑从起点带$w\bmod k$升水出发怎么处理,然后再依次考虑每次从起点带$k$升水出发怎么处理。

    假设目前已经推进到的右端点是$all$,算上这次一共要从起点出发$t$次,那么从$all$往右推进的这段路将被经过总计$2t-1$次。最优策略是将这次出发带走的水平均分成$2t-1$份,从而得到本次推进的距离,注意距离要和到终点的距离取$\min$。

    求出推进距离后,乘以经过的次数,就是这段路消耗的水量。

    时间复杂度$O(\frac{w}{k})$。

    #include<cstdio>
    typedef double db;
    int s,w,k,o;db sum,all;
    inline void gao(db k){
      k/=o;
      if(k+all>=s)k=s-all;
      sum+=k*o;
      all+=k;
      o-=2;
    }
    int main(){
      scanf("%d%d%d",&s,&w,&k);
      o=(w/k)*2-1;
      if(w%k)o+=2,gao(w%k);
      while(o>0)gao(k);
      printf("%.3f",w-sum);
    }
    

      

    Climbing [B]

    从左往右贪心,若一段区间能拼上则拼上,通过维护关于最左边数的不等式组的解集来判断。

    时间复杂度$O(n)$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int n,i,ans,A,B;ll L,R,C,D;
    inline bool check(ll K){
      if(C==1){
        L=max(L,-D);
        R=min(R,K-D);
      }else{
        L=max(L,D-K);
        R=min(R,D);
      }
      return L<=R;
    }
    int main(){
      scanf("%d",&n);
      for(i=1;i<=n;i++){
        scanf("%d%d",&A,&B);
        A+=B;
        if(i>1&&check(A)){
          ans++;
          D=A-D;
          C=-C;
        }else{
          L=0,R=A;
          C=-1,D=A;
        }
      }
      printf("%d",ans);
    }
    

      

    Round 3:

    Pedestrian Crossing [B]

    在模$K$意义下做,每个白色段会禁掉起点位置的一个区间,注意这里区间端点不会被禁,所以引入$0.5$坐标。最后检查是否$[0,K-0.5]$都被禁了即可。

    时间复杂度$O(n\log n)$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    typedef pair<ll,ll>P;
    const int N=500005;
    int Case,n,m,i;
    ll S,K,l,r,sum,len[N];
    P e[N<<1];
    inline void ban(ll l,ll r){
      if(l>r)return;
      e[++m]=P(l,r);
    }
    inline bool gao(ll l,ll r){
      if(l+K-S<r)return 0;
      l%=K,r%=K;
      if(l>r)r+=K;
      l+=K-S,r+=K;
      if(l/K==r/K)ban(l%K*2+1,r%K*2-1);
      else{
        ban(l%K*2+1,K*2-1);
        ban(0,r%K*2-1);
      }
      return 1;
    }
    bool check(){
      scanf("%lld%lld%d",&S,&K,&n);
      for(i=1;i<=n;i++)scanf("%lld",&len[i]),len[i]+=len[i-1];
      m=0;
      for(i=1;i<=n;i+=2)if(!gao(len[i-1],len[i]))return 0;
      sort(e+1,e+m+1);
      l=0,r=-5,sum=0;
      for(i=1;i<=m;i++){
        if(e[i].first>r+1){
          if(l<=r)sum+=r-l+1;
          l=e[i].first;
        }
        r=max(r,e[i].second);
      }
      if(l<=r)sum+=r-l+1;
      return sum<K*2;
    }
    int main(){
      scanf("%d",&Case);
      while(Case--)puts(check()?"TAK":"NIE");
    }
    

      

    Round 4:

    Fuel [B]

    找到一条树直径,令直径上的点代价为$1$,剩下的点代价为$2$,注意起点代价为$0$,然后贪心选点即可。

    时间复杂度$O(n)$。

    #include<cstdio>
    const int N=500005;
    int n,m,i,x,y,g[N],v[N<<1],nxt[N<<1],ed,A,B,ans;
    inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
    void dfs(int x,int y,int d){
      if(d>B)A=x,B=d;
      for(int i=g[x];i;i=nxt[i])if(v[i]!=y)dfs(v[i],x,d+1);
    }
    int main(){
      scanf("%d%d",&n,&m);
      for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
      dfs(1,0,1);
      dfs(A,0,1);
      A=n-B;
      ans=1,B--;
      while(m&&B)m--,B--,ans++;
      while(m>1&&A)m-=2,A--,ans++;
      printf("%d",ans);
    }
    

      

    Round 5:

    Declining Sequences [B]

    求出$f[i][j]$表示有多少以$j$为起点长度为$i$的递减序列。

    对于一个有解的询问,从第$1$层一直找到第$m$层,每层需要找到权值小于某数,且下标大于某数的部分里从左往右第$k$个方案所在的下标。

    按照权值扫描线后,线段树上维护区间$f$值的和,然后在线段树上二分即可。

    时间复杂度$O(nm\log n)$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=100005,K=13,M=262155;
    const ll inf=1000000000000000010LL;
    int n,m,q,i,j,k,o,a[N],b[N],ans[N][K];ll f[K][N],bit[N],que[N],all;
    int gn[N],gq[N],v[N<<1],nxt[N<<1],ed;
    int pos[N],A,B;ll val[M],C;
    inline void up(ll&a,ll b){a=a+b<inf?a+b:inf;}
    inline void ins(int x,ll p){for(;x<=n;x+=x&-x)up(bit[x],p);}
    inline ll ask(int x){ll t=0;for(;x;x-=x&-x)up(t,bit[x]);return t;}
    inline void add(int&x,int y){v[++ed]=y;nxt[ed]=x;x=ed;}
    void build(int x,int a,int b){
      val[x]=0;
      if(a==b){pos[a]=x;return;}
      int mid=(a+b)>>1;
      build(x<<1,a,mid),build(x<<1|1,mid+1,b);
    }
    inline void modify(int x,ll p){
      val[x=pos[x]]=p;
      for(x>>=1;x;x>>=1)val[x]=val[x<<1],up(val[x],val[x<<1|1]);
    }
    void query(int x,int a,int b){
      if(B)return;
      if(A<=a&&val[x]<C){C-=val[x];return;}
      if(a==b){B=a;return;}
      int mid=(a+b)>>1;
      if(A<=mid)query(x<<1,a,mid);
      query(x<<1|1,mid+1,b);
    }
    int main(){
      scanf("%d%d%d",&n,&m,&q);
      for(i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
      sort(b+1,b+n+1);
      for(i=1;i<=n;i++)a[i]=lower_bound(b+1,b+n+1,a[i])-b;
      for(i=1;i<=n;i++)f[m][i]=1;
      for(i=m-1;i;i--){
        for(j=1;j<=n;j++)bit[j]=0;
        for(j=n;j;j--){
          f[i][j]=ask(a[j]-1);
          ins(a[j],f[i+1][j]);
        }
      }
      for(i=1;i<=n;i++)up(all,f[1][i]);
      a[0]=n+1;
      for(i=1;i<=q;i++){
        scanf("%lld",&que[i]);
        if(que[i]>all)ans[i][0]=-1;
      }
      for(i=1;i<=n;i++)add(gn[a[i]],i);
      for(i=1;i<=m;i++){
        ed=n;
        for(j=0;j<=n;j++)gq[j]=0;
        for(j=1;j<=q;j++)if(~ans[j][0])add(gq[a[ans[j][i-1]]-1],j);
        build(1,1,n);
        for(j=0;j<=n;j++){
          for(k=gn[j];k;k=nxt[k]){
            o=v[k];
            modify(o,f[i][o]);
          }
          for(k=gq[j];k;k=nxt[k]){
            o=v[k];
            A=ans[o][i-1]+1;
            B=0;
            C=que[o];
            query(1,1,n);
            ans[o][i]=B;
            que[o]=C;
          }
        }
      }
      for(i=1;i<=q;i++){
        if(~ans[i][0])for(j=1;j<=m;j++)printf("%d%c",ans[i][j],j<m?' ':'\n');
        else puts("-1");
      }
    }
    

      

    Double Factorial [B]

    $1!\times 2!\times3!\times\dots\times n!$里质因子$5$的个数即

    \[\left(\lfloor\frac{1}{5^1}\rfloor+\lfloor\frac{2}{5^1}\rfloor+\dots+\lfloor\frac{n}{5^1}\rfloor\right)+\left(\lfloor\frac{1}{5^2}\rfloor+\lfloor\frac{2}{5^2}\rfloor+\dots+\lfloor\frac{n}{5^2}\rfloor\right)+\dots+\left(\lfloor\frac{1}{5^k}\rfloor+\lfloor\frac{2}{5^k}\rfloor+\dots+\lfloor\frac{n}{5^k}\rfloor\right)\]

    枚举$k$后推公式即可。

    n=int(input())
    ans=0
    k=5
    while k<=n:
      m=n//k
      ans+=(m-1)*m*k//2+m*(n-m*k+1)
      k*=5
    print(ans)
    

      

    Trails [A]

    留坑。

    Vacation [A]

    当$k=2$时就是$a$和$b$的距离。

    当$k=3$时,答案上界为$24n$,对于每个点向三个排列对应位置左右$15$个位置连负边可以减小答案。建立二分图,使用Dijkstra找增广路求最小费用流。

    优化1:

    最短路不超过$24$,可以用桶代替堆,使得每次Dijkstra时间复杂度为$O(n+m)$,其中$m=nc$。

    优化2:

    Dijkstra求出源点到每个点的最短路后,在最短路图上BFS得到每个点的层数。

    沿着最短层进行多路增广,不断BFS多路增广直到无法增广。

    那么每次Dijkstra求出的最小花费严格递增,且BFS出来的最小层数严格递增。

    沿用Dinic的分析可得每次多路增广次数不超过$O(\sqrt{n})$,所以一共$O(c\sqrt{n})$次多路增广。

    总时间复杂度$O(c\sqrt{n}(n+m))$=$O(c^2n^{1.5})$。

    #include<cstdio>
    const int N=5005,inf=10000000,K=30;
    int n,k,i,j,ans,a[N],b[N],c[N],last[N];
    int s,t,cnt=1;
    int g[N<<1],d[N<<1],f[N<<1],vis[N<<1],h[N<<1];
    int pool[K][N<<1],top[K],q[N<<1];
    int cur[N<<1];
    void read(int a[]){
      int i,x;
      for(i=1;i<=n;i++)scanf("%d",&x),a[x]=i;
    }
    inline int abs(int x){return x>0?x:-x;}
    inline int min(int a,int b){return a<b?a:b;}
    struct E{
      int v,f,c,nxt;
      E(){}
      E(int _v,int _f,int _c,int _nxt){v=_v,f=_f,c=_c,nxt=_nxt;}
    }e[N*(15*3+2)*2];
    inline void add(int u,int v,int f,int c){
      e[++cnt]=E(v,f,c,g[u]);
      g[u]=cnt;
      e[++cnt]=E(u,0,-c,g[v]);
      g[v]=cnt;
    }
    inline void addedge(int x,int y){
      if(y<1||y>n)return;
      if(last[y]==x)return;
      last[y]=x;
      int w=min(abs(y-a[x]),8)+min(abs(y-b[x]),8)+min(abs(y-c[x]),8);
      add(x,y+n,1,w-24);
    }
    inline void ext(int x,int y){
      if(y>=K)return;
      d[x]=y;
      pool[y][++top[y]]=x;
    }
    inline bool dijkstra(){
      int i,o;
      for(i=1;i<=t;i++)d[i]=inf,vis[i]=0;
      for(i=0;i<K;i++)top[i]=0;
      ext(s,0);
      for(o=0;o<K;o++)while(top[o]){
        int u=pool[o][top[o]--];
        if(vis[u])continue;
        vis[u]=1;
        for(i=g[u];i;i=e[i].nxt)if(e[i].f){
          int v=e[i].v,w=e[i].c+h[u]-h[v];
          if(d[v]>d[u]+w&&!vis[v])ext(v,d[u]+w);
        }
      }
      return d[t]!=inf;
    }
    inline bool bfs(){
      int head=1,tail=1,i,u,v,z;
      for(i=1;i<=t;i++)vis[i]=0;
      vis[s]=1;
      f[s]=0;
      q[1]=s;
      while(head<=tail){
        u=q[head++];
        z=f[u]+1;
        for(i=g[u];i;i=e[i].nxt)if(e[i].f){
          int v=e[i].v,w=e[i].c+h[u]-h[v];
          if(d[v]==d[u]+w&&!vis[v]){
            vis[v]=1;
            f[v]=z;
            q[++tail]=v;
          }
        }
      }
      return vis[t];
    }
    bool dfs(int u){
      if(u==t)return 1;
      vis[u]=1;
      for(int&i=cur[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(!vis[v]&&e[i].f&&d[v]==d[u]+e[i].c+h[u]-h[v]&&f[v]==f[u]+1){
          int tmp=dfs(v);
          if(tmp){
            ans+=e[i].c;
            e[i].f--,e[i^1].f++;
            vis[u]=0;
            i=e[i].nxt;
            return 1;
          }
        }
      }
      return vis[u]=0;
    }
    int main(){
      scanf("%d%d",&n,&k);
      read(a);
      read(b);
      if(k==2){
        for(i=1;i<=n;i++)ans+=min(abs(a[i]-b[i]),8);
        return printf("%d",ans),0;
      }
      read(c);
      s=n*2+1;
      t=s+1;
      for(i=1;i<=n;i++)add(s,i,1,0),add(i+n,t,1,0);
      for(i=1;i<=n;i++){
        for(j=a[i]-7;j<=a[i]+7;j++)addedge(i,j);
        for(j=b[i]-7;j<=b[i]+7;j++)addedge(i,j);
        for(j=c[i]-7;j<=c[i]+7;j++)addedge(i,j);
      }
      for(i=1;i<=n;i++)for(j=g[i];j;j=e[j].nxt)h[e[j].v]=min(h[e[j].v],e[j].c);
      for(i=1;i<=n;i++)h[t]=min(h[t],h[i+n]);
      ans=n*24;
      while(dijkstra()){
        if(h[t]+d[t]>=0)break;
        while(bfs()){
          for(i=1;i<=t;i++)cur[i]=g[i],vis[i]=0;
          while(dfs(s));
        }
        for(i=1;i<=t;i++)h[i]+=d[i];
      }
      printf("%d",ans);
    }
    

      

    Round 6:

    Automorphisms [B]

    找重心作为根,如果两个重心就拆了中间加个点作为根。

    对于每个点,将儿子按hash值分组,对于每组,将答案乘以儿子数的阶乘即可。

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    #include<map>
    using namespace std;
    typedef vector<int>V;
    const int N=500005,P=1000000007;
    int n,m,cnt,i,x,y,A,B,ans,g[N],v[N<<1],nxt[N<<1],ed,fac[N],sz[N],f[N],q[N];
    V tmp;map<V,int>T;
    inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
    void dfs(int x,int y){
      sz[x]=1;
      for(int i=g[x];i;i=nxt[i])if(v[i]!=y)dfs(v[i],x),sz[x]+=sz[v[i]];
      if(sz[x]>n-sz[x]&&!A)A=x;
      if(sz[x]==n-sz[x])A=x,B=y;
    }
    void cal(int x,int y){
      for(int i=g[x];i;i=nxt[i]){
        int u=v[i];
        if(u==y)continue;
        cal(u,x);
      }
      cnt=0;
      for(int i=g[x];i;i=nxt[i]){
        int u=v[i];
        if(u==y)continue;
        q[cnt++]=f[u];
      }
      sort(q,q+cnt);
      for(int i=0,j;i<cnt;i=j){
        for(j=i;j<cnt&&q[i]==q[j];j++);
        ans=1LL*ans*fac[j-i]%P;
      }
      tmp.resize(cnt);
      for(int i=0;i<cnt;i++)tmp[i]=q[i];
      int&o=T[tmp];
      if(!o)o=++m;
      f[x]=o;
    }
    int main(){
      scanf("%d",&n);
      for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
      for(fac[0]=i=1;i<=n;i++)fac[i]=1LL*fac[i-1]*i%P;
      dfs(1,0);
      ans=1;
      cal(A,B);
      if(B){
        cal(B,A);
        if(f[A]==f[B])ans=ans*2%P;
      }
      printf("%d",ans);
    }
    

      

    Kangaroos [A]

    两个区间$[x_1,y_1][x_2,y_2]$相交等价于$x_1\leq y_2$且$y_1\geq x_2$。

    考虑离线,把所有区间看成二维的点建立K-D Tree。

    然后从$1$到$n$依次加入每个区间,每次加入一个区间时,把所有与它相交的询问的值$+1$,把所有与它不相交的询问的值设为$0$。

    在K-D Tree上打标记,最后每个询问的答案为其值的历史最大值,时间复杂度$O(n\sqrt{m})$。

    #include<cstdio>
    #include<algorithm>
    const int N=200010,inf=-1,BUF=6000000;
    int n,m,i,id[N],root,cmp_d,X,Y;struct P{int x,y;}a[N];char Buf[BUF],*buf=Buf;
    inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}
    struct node{
      int D[2],l,r,Max[2],Min[2];
      int m,d,e,hm,hd,he;
    }t[N];
    inline bool cmp(const node&a,const node&b){return a.D[cmp_d]<b.D[cmp_d];}
    inline void Max(int&a,int b){if(a<b)a=b;}
    inline void Min(int&a,int b){if(a>b)a=b;}
    inline void up(int x){
      id[t[x].e]=x,t[x].e=t[x].he=inf;
      if(t[x].l){
        Max(t[x].Max[0],t[t[x].l].Max[0]);
        Min(t[x].Min[0],t[t[x].l].Min[0]);
        Max(t[x].Max[1],t[t[x].l].Max[1]);
        Min(t[x].Min[1],t[t[x].l].Min[1]);
      }
      if(t[x].r){
        Max(t[x].Max[0],t[t[x].r].Max[0]);
        Min(t[x].Min[0],t[t[x].r].Min[0]);
        Max(t[x].Max[1],t[t[x].r].Max[1]);
        Min(t[x].Min[1],t[t[x].r].Min[1]);
      }
    }
    int build(int l,int r,int D){
      int mid=(l+r)>>1;
      cmp_d=D,std::nth_element(t+l+1,t+mid+1,t+r+1,cmp);
      t[mid].Max[0]=t[mid].Min[0]=t[mid].D[0];
      t[mid].Max[1]=t[mid].Min[1]=t[mid].D[1];
      if(l!=mid)t[mid].l=build(l,mid-1,!D);
      if(r!=mid)t[mid].r=build(mid+1,r,!D);
      return up(mid),mid;
    }
    inline void hdoa(node&x,int v){
      Max(x.hm,x.m+v);
      if(x.e>inf)Max(x.he,x.e+v);else Max(x.hd,x.d+v);
    }
    inline void hdoc(node&x,int v){Max(x.hm,v);Max(x.he,v);}
    inline void doa(node&x,int v){
      Max(x.hm,x.m+=v);
      if(x.e>inf)Max(x.he,x.e+=v);else Max(x.hd,x.d+=v);
    }
    inline void doc(node&x,int v){Max(x.hm,x.m=v);Max(x.he,x.e=v);x.d=0;}
    inline void pb(node&x){
      if(x.hd){
        if(x.l)hdoa(t[x.l],x.hd);
        if(x.r)hdoa(t[x.r],x.hd);x.hd=0;
      }
      if(x.he>inf){
        if(x.l)hdoc(t[x.l],x.he);
        if(x.r)hdoc(t[x.r],x.he);
        x.he=inf;
      }
      if(x.d){
        if(x.l)doa(t[x.l],x.d);
        if(x.r)doa(t[x.r],x.d);
        x.d=0;
      }else if(x.e>inf){
        if(x.l)doc(t[x.l],x.e);
        if(x.r)doc(t[x.r],x.e);
        x.e=inf;
      }
    }
    void change(node&x){
      if(x.Min[0]>X||x.Max[1]<Y){doc(x,0);return;}
      if(x.Max[0]<=X&&x.Min[1]>=Y){doa(x,1);return;}
      pb(x);
      if(x.D[0]<=X&&x.D[1]>=Y)Max(x.hm,++x.m);else x.m=0;
      if(x.l)change(t[x.l]);
      if(x.r)change(t[x.r]);
    }
    void dfs(node&x){
      pb(x);
      if(x.l)dfs(t[x.l]);
      if(x.r)dfs(t[x.r]);
    }
    int main(){
      fread(Buf,1,BUF,stdin),read(n),read(m);
      for(i=1;i<=n;i++)read(a[i].x),read(a[i].y);
      for(i=1;i<=m;i++)read(t[i].D[0]),read(t[i].D[1]),t[i].e=i;
      root=build(1,m,0);
      for(i=1;i<=n;i++)X=a[i].y,Y=a[i].x,change(t[root]);
      dfs(t[root]);
      for(i=1;i<=m;i++)printf("%d\n",t[id[i]].hm);
    }
    

      

    Laser Pool [A]

    与横线以及竖线的交点个数很容易求,那么只要求出横线竖线交点与运动轨迹的交点数即可。

    运动轨迹可以划分成若干条贯穿边界的斜线,对于第一条和最后一条,可以用bitset暴力统计。

    对于中间的部分,斜线都是完整的,可以FFT预处理。

    时间复杂度$O(n\log n+\frac{nq}{32})$。

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    typedef unsigned int U;
    const int N=100010,M=10010,L=262150;
    int n,m,cb,ce,i,j,ab[N*2],arb[N*2],rab[N*2],rarb[N*2],g[65540];
    char a[N],b[N];
    struct Que{int x,y,vx,vy,t,ans;}e[M];
    inline int popcount(U x){return g[x>>16]+g[x&65535];}
    struct BIT{
      U v[N/32+5];
      void clr(){for(int i=0;i<=cb;i++)v[i]=0;}
      U get(int x){return v[x>>5]>>(x&31)&1;}
      void set(int x,U y){if((v[x>>5]>>(x&31)&1)^y)v[x>>5]^=1U<<(x&31);}
      void shl(int x,int y){
        int A=y>>5,B=y&31,C=(32-B)&31,D=x>>5,E=(D<<5)+31;
        for(int i=x;i<=E;i++)set(i,get(i+y));
        for(int i=D+1;i<=cb;i++){
          v[i]=v[i+A]>>B;
          if(C)v[i]|=v[i+A+1]<<C;
        }
      }
      void copy(int x,int y,const BIT&p){for(int i=x;i<=y;i++)v[i]=p.v[i];}
      void And(int x,int y,const BIT&p){for(int i=x;i<=y;i++)v[i]&=p.v[i];}
      int count(int x,int y){
        int A=x>>5,B=y>>5,C,ret=0;
        if(A==B){
          for(int i=x;i<=y;i++)if(v[A]>>(i&31)&1)ret++;
          return ret;
        }
        for(int i=A+1;i<B;i++)ret+=popcount(v[i]);
        C=(A<<5)+31;
        for(int i=x;i<=C;i++)if(v[A]>>(i&31)&1)ret++;
        C=B<<5;
        for(int i=C;i<=y;i++)if(v[B]>>(i&31)&1)ret++;
        return ret;
      }
    }bA,bB,brA,brB,tA,tB;
    inline void read(int&a){
      char c;bool f=0;a=0;
      while(!((((c=getchar())>='0')&&(c<='9'))||(c=='-')));
      if(c!='-')a=c-'0';else f=1;
      while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';
      if(f)a=-a;
    }
    namespace FFT{
    int k,j,pos[L];
    const double pi=acos(-1.0);
    struct comp{
      double r,i;comp(double _r=0,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);}
    }a[L],ra[L],b[L],rb[L],c[L];
    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;
        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 work(){
      for(k=1;k<=n||k<=m;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=1;i<=n;i++)a[i].r=ra[n-i+1].r=::a[i];
      for(i=1;i<=m;i++)b[i].r=rb[m-i+1].r=::b[i];
      FFT(a,k,1),FFT(ra,k,1),FFT(b,k,1),FFT(rb,k,1);
      for(i=0;i<k;i++)c[i]=a[i]*b[i];
      FFT(c,k,-1);
      for(i=1;i<=n+m;i++)ab[i]=(int)(c[i].r+0.5);
      for(i=0;i<k;i++)c[i]=a[i]*rb[i];
      FFT(c,k,-1);
      for(i=1;i<=n+m;i++)arb[i]=(int)(c[i].r+0.5);
      for(i=0;i<k;i++)c[i]=ra[i]*b[i];
      FFT(c,k,-1);
      for(i=1;i<=n+m;i++)rab[i]=(int)(c[i].r+0.5);
      for(i=0;i<k;i++)c[i]=ra[i]*rb[i];
      FFT(c,k,-1);
      for(i=1;i<=n+m;i++)rarb[i]=(int)(c[i].r+0.5);
    }
    }
    namespace Solve{
    bool a[N],b[N];
    int sa[N],sb[N];
    int cnt,idx[4][N],idy[4][N];
    int tot,st[N*8],en[N*8],v[N*8],pos[N*8],cur,q[N*8],sw[N*8];long long sl[N*8];
    struct E{int sx,sy,ex,ey,len,w,d,nxt;}f[N*8];
    inline bool check(int x,int y){return b[x]&&a[y];}
    inline int abs(int x){return x>0?x:-x;}
    inline int&getid(int x,int y,int d){
      if(y==1||y==n)return idx[d][x];
      return idy[d][y];
    }
    inline void makerev(int x){
      f[x].sx=f[x-1].ex;
      f[x].sy=f[x-1].ey;
      f[x].ex=f[x-1].sx;
      f[x].ey=f[x-1].sy;
      f[x].w=f[x-1].w;
      f[x].d=(f[x-1].d+2)&3;
    }
    inline int getnxt(int x,int y,int d){
      if(d==0){
        if(x<m&&y==n)return getid(x,y,(d+1)&3);
        if(y==n)return getid(x,y,(d+2)&3);
        return getid(x,y,(d+3)&3);
      }
      if(d==2){
        if(x>1&&y==1)return getid(x,y,(d+1)&3);
        if(y==1)return getid(x,y,(d+2)&3);
        return getid(x,y,(d+3)&3);
      }
      if(d==1){
        if(x<m&&y==1)return getid(x,y,(d+3)&3);
        if(y==1)return getid(x,y,(d+2)&3);
        return getid(x,y,(d+1)&3);
      }
      if(x>1&&y==n)return getid(x,y,(d+3)&3);
      if(y==n)return getid(x,y,(d+2)&3);
      return getid(x,y,(d+1)&3);
    }
    inline int cal(int x,int t,int n,int*s){
      if(x+t<=n)return s[x+t]-s[x-1];
      t-=n-x;
      int ret=s[n-1]-s[x-1];
      ret+=t/(n+n-2)*(s[n]+s[n-1]-s[1]);
      t%=n+n-2;
      if(t<n)return ret+s[n]-s[n-t-1];
      return ret+s[n]-s[1]+s[t-n+2];
    }
    inline int search(int L,int r,int x){
      int l=L,t=L-1,mid;
      while(l<=r)if(sl[mid=(l+r)>>1]-sl[L-1]<=x)l=(t=mid)+1;else r=mid-1;
      return t;
    }
    inline void ask(int x,int y,int t,int p){
      int&ans=e[p].ans;
      ans=cal(x,t,m,sb)+cal(y,t,n,sa);
      int ex=m,ey=y-x+m;
      if(ey>n)ey=n,ex=x-y+n;
      int d=ex-x;
      if(t<=d){
        tA.copy(y>>5,cb,bA);
        tB.copy(x>>5,cb,bB);
        tB.shl(0,x);
        tA.shl(0,y);
        tA.And(0,t>>5,tB);
        ans-=tA.count(0,t);
        return;
      }
      if(d){
        tA.copy(y>>5,cb,bA);
        tB.copy(x>>5,cb,bB);
        tB.shl(0,x);
        tA.shl(0,y);
        tA.And(0,(d-1)>>5,tB);
        ans-=tA.count(0,d-1);
      }
      t-=d;
      int o=getnxt(ex,ey,0);
      int l=st[v[o]],r=en[v[o]];
      o=pos[o];
      int u=search(o,r,t);
      ans-=sw[u]-sw[o-1];
      t-=sl[u]-sl[o-1];
      o=u+1;
      if(o>r){
        ans-=t/(sl[r]-sl[l-1])*(sw[r]-sw[l-1]);
        t%=sl[r]-sl[l-1];
        u=search(l,r,t);
        ans-=sw[u]-sw[l-1];
        t-=sl[u]-sl[l-1];
        o=u+1;
      }
      o=q[o];
      x=f[o].sx,y=f[o].sy;
      if(f[o].d==0){
        tA.copy(y>>5,cb,bA);
        tB.copy(x>>5,cb,bB);
      }
      if(f[o].d==1){
        y=n-y+1;
        tA.copy(y>>5,cb,brA);
        tB.copy(x>>5,cb,bB);
      }
      if(f[o].d==2){
        x=m-x+1;
        y=n-y+1;
        tA.copy(y>>5,cb,brA);
        tB.copy(x>>5,cb,brB);
      }
      if(f[o].d==3){
        x=m-x+1;
        tA.copy(y>>5,cb,bA);
        tB.copy(x>>5,cb,brB);
      }
      tB.shl(0,x);
      tA.shl(0,y);
      tA.And(0,t>>5,tB);
      ans-=tA.count(0,t);
    }
    void work(int*A,int*B){
      for(i=1;i<=n;i++)sa[i]=sa[i-1]+a[i],bA.set(i,a[i]),brA.set(n-i+1,a[i]);
      for(i=1;i<=m;i++)sb[i]=sb[i-1]+b[i],bB.set(i,b[i]),brB.set(m-i+1,b[i]);
      cnt=0;
      for(i=1;i<m;i++){
        cnt++;
        f[cnt].sx=i,f[cnt].sy=1;
        f[cnt].ex=m,f[cnt].ey=m-i+1;
        if(f[cnt].ey>n)f[cnt].ey=n,f[cnt].ex=n-1+i;
        f[cnt].w=B[m-i+2];
        f[cnt].d=0;
        makerev(++cnt);
      }
      for(i=2;i<n;i++){
        cnt++;
        f[cnt].sx=1,f[cnt].sy=i;
        f[cnt].ex=m,f[cnt].ey=m-1+i;
        if(f[cnt].ey>n)f[cnt].ey=n,f[cnt].ex=n-i+1;
        f[cnt].w=B[m+i];
        f[cnt].d=0;
        makerev(++cnt);
      }
      for(i=2;i<=m;i++){
        cnt++;
        f[cnt].sx=i,f[cnt].sy=1;
        f[cnt].ex=1,f[cnt].ey=i;
        if(f[cnt].ey>n)f[cnt].ey=n,f[cnt].ex=i+1-n;
        f[cnt].w=A[i+1];
        f[cnt].d=3;
        makerev(++cnt);
      }
      for(i=2;i<n;i++){
        cnt++;
        f[cnt].sx=m,f[cnt].sy=i;
        f[cnt].ex=1,f[cnt].ey=m+i-1;
        if(f[cnt].ey>n)f[cnt].ey=n,f[cnt].ex=m+i-n;
        f[cnt].w=A[i+m];
        f[cnt].d=3;
        makerev(++cnt);
      }
      for(i=1;i<=cnt;i++){
        f[i].len=abs(f[i].sx-f[i].ex);
        f[i].w-=check(f[i].ex,f[i].ey);
        getid(f[i].sx,f[i].sy,f[i].d)=i;
      }
      for(i=1;i<=cnt;i++)f[i].nxt=getnxt(f[i].ex,f[i].ey,f[i].d);
      cur=tot=0;
      for(i=1;i<=cnt;i++)v[i]=0;
      for(i=1;i<=cnt;i++)if(!v[i]){
        st[++tot]=cur+1;
        for(j=i;!v[j];j=f[j].nxt)v[q[++cur]=j]=tot;
        en[tot]=cur;
      }
      for(i=1;i<=cnt;i++)sl[i]=sl[i-1]+f[q[i]].len,sw[i]=sw[i-1]+f[q[i]].w,pos[q[i]]=i;
    }
    }
    int main(){
      for(i=1;i<65536;i++)g[i]=g[i>>1]+(i&1);
      read(n),read(m);
      scanf("%s%s",a+1,b+1);
      for(i=1;i<=n;i++)a[i]-='0';
      for(i=1;i<=m;i++)b[i]-='0';
      read(ce);
      for(i=1;i<=ce;i++)read(e[i].x),read(e[i].y),read(e[i].vx),read(e[i].vy),read(e[i].t);
      FFT::work();
      cb=(n>m?n:m)>>5;
      for(i=1;i<=n;i++)Solve::a[i]=a[i];
      for(i=1;i<=m;i++)Solve::b[i]=b[i];
      Solve::work(ab,arb);
      for(i=1;i<=ce;i++)if(e[i].vx==1&&e[i].vy==1)Solve::ask(e[i].x,e[i].y,e[i].t,i);
      for(i=1;i<=n;i++)Solve::a[i]=a[n-i+1];
      for(i=1;i<=m;i++)Solve::b[i]=b[i];
      Solve::work(rab,rarb);
      for(i=1;i<=ce;i++)if(e[i].vx==1&&e[i].vy==-1)Solve::ask(e[i].x,n-e[i].y+1,e[i].t,i);
      for(i=1;i<=n;i++)Solve::a[i]=a[i];
      for(i=1;i<=m;i++)Solve::b[i]=b[m-i+1];
      Solve::work(arb,ab);
      for(i=1;i<=ce;i++)if(e[i].vx==-1&&e[i].vy==1)Solve::ask(m-e[i].x+1,e[i].y,e[i].t,i);
      for(i=1;i<=n;i++)Solve::a[i]=a[n-i+1];
      for(i=1;i<=m;i++)Solve::b[i]=b[m-i+1];
      Solve::work(rarb,rab);
      for(i=1;i<=ce;i++)if(e[i].vx==-1&&e[i].vy==-1)Solve::ask(m-e[i].x+1,n-e[i].y+1,e[i].t,i);
      for(i=1;i<=ce;i++)printf("%d\n",e[i].ans);
    }
    

      

    The Shortest Period [B]

    枚举答案长度$L$,设$A$和$B$分别为第一个循环节和反串的第一个循环节:

    1. 坏点不在$A$,那么可以暴力匹配检验。

    2. 坏点不在$B$,那么把串翻转后不在$A$中,转化为$1$。

    3. 坏点在$A$和$B$的交里面,那么只要长度为$N-L+1$的前后缀相同,那么就存在长度为$L$的循环节。

    通过扩展KMP和Hash快速判断即可,时间复杂度$O(dn\log n)$。

    #include<cstdio>
    const int N=200010,P=233;
    int T,n,i,j,t,k,p,l,ans,g[N];char a[N];unsigned int pow[N],f[N];
    inline void swap(char&a,char&b){char c=a;a=b;b=c;}
    inline unsigned int hash(int l,int r){return f[r]-f[l-1]*pow[r-l+1];}
    inline int min(int a,int b){return a<b?a:b;}
    void solve(){
      for(i=1;i<=n;i++)f[i]=f[i-1]*P+a[i];
      for(g[i=0]=n;i<n-1&&a[i+1]==a[i+2];i++);
      for(g[t=1]=i,k=2;k<n;k++){
        p=t+g[t]-1,l=g[k-t];
        if(k+l>p){
          j=(p-k+1)>0?(p-k+1):0;
          while(k+j<n&&a[k+j+1]==a[j+1])j++;
          g[k]=j,t=k;
        }else g[k]=l;
      }
      for(i=n;i;i--)g[i]=g[i-1];
      for(i=1;i<ans;i++){
        j=g[i+1];
        if(j==n-i||g[i+2]>=n-i+1)ans=i;else{
          j+=i+2,k=(j-2)/i*i+1,t=k+i;
          if(t>n)t=n;
          if(hash(j,t)!=hash(j-k,t-k)||g[t+1]<n-t)continue;
          for(t++;t<=n;t+=i)if(g[t]<min(i,n-t+1))break;
          if(t>n)ans=i;
        }
      }
    }
    int main(){
      for(pow[0]=i=1;i<N;i++)pow[i]=pow[i-1]*P;
      scanf("%d",&T);
      while(T--){
        scanf("%d%s",&n,a+1);
        ans=n-1;
        solve();
        for(i=1;i<n-i+1;i++)swap(a[i],a[n-i+1]);
        solve();
        printf("%d\n",ans);
      }
    }
    

      

    Trial Finals:

    Wyznaczanie planu sieci drogowej 2

    前$n-2$次每次取出一个度数$=1$的点和一个度数$>1$的点连边;最后一次取出两个度数$=1$的点连边。

    #include<cstdio>
    #include<cstdlib>
    const int N=2000005;
    int n,i,x,y,ca,cb,a[N],b[N],f[N],d[N],deg[N];
    void NIE(){
      puts("BRAK");
      std::exit(0);
    }
    int main(){
      scanf("%d",&n);
      for(i=1;i<=n;i++){
        scanf("%d",&deg[i]);
        if(deg[i]==1)a[++ca]=i;else b[++cb]=i;
      }
      for(i=1;i<n-1;i++){
        if(!ca)NIE();
        if(!cb)NIE();
        x=a[ca--];
        y=b[cb--];
        f[x]=y;
        d[x]++;
        d[y]++;
        if(d[y]+1<deg[y])b[++cb]=y;else a[++ca]=y;
      }
      if(!ca)NIE();
      x=a[ca--];
      if(!ca)NIE();
      y=a[ca--];
      f[x]=y;
      d[x]++;
      d[y]++;
      for(i=1;i<=n;i++)if(d[i]!=deg[i])NIE();
      for(i=1;i<=n;i++)if(f[i])printf("%d %d\n",i,f[i]);
    }
    

      

    Finals:

    Computational Biology

    对于每个长度为$m$的子串,由其向循环移一位的字符串hash连边,找最大环即可。

    时间复杂度$O(qn\log n)$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef unsigned long long ull;
    typedef pair<ull,ull>P;
    const int N=500005,S=233;
    int n,q,m,ce,i,j,f[N],vis[N],ans,now;
    char a[N];
    ull p[N],tmp,base;
    P e[N];
    int main(){
      scanf("%d%d%s",&n,&q,a+1);
      for(p[0]=i=1;i<=n;i++)p[i]=p[i-1]*S;
      while(q--){
        scanf("%d",&m);
        tmp=ce=0,base=p[m];
        for(i=1;i<=n;i++){
          tmp=tmp*S+a[i];
          if(i>m)tmp-=base*a[i-m];
          e[++ce]=P(tmp,tmp*S-(base-1)*a[i-m+1]);
        }
        sort(e+1,e+ce+1);
        ans=0;
        for(i=1;i<=ce;i++)f[i]=0;
        for(i=1;i<=ce;i=j){
          for(j=i;j<=ce&&e[i]==e[j];j++);
          f[i]=j-i;
        }
        for(i=1;i<=ce;i++)if(f[i]){
          tmp=e[i].second;
          now=0;
          while(1){
            j=lower_bound(e+1,e+ce+1,P(tmp,0))-e;
            if(j<1||j>ce||e[j].first!=tmp||!f[j])break;
            now+=f[j];
            f[j]=0;
            if(j==i){
              if(now>ans)ans=now;
              break;
            }
            tmp=e[j].second;
          }
        }
        printf("%d\n",ans);
      }
    }
    

      

    Byteland Worldbeat Publishers

    不妨设$n=m$,考虑一个完美匹配:

    • 对于每条匹配边$(左u,右v,w)$,连边$左 u\rightarrow 右 v$,边权$w$。
    • 对于每条非匹配边$(左u,右v,w)$,连边$右 v\rightarrow 左 u$,边权$-w$。

    那么每个完美匹配权值和相同当且仅当每个环的边权和都是$0$。

    注意到所有环都可以由$左u\rightarrow 右 v\rightarrow 左 u+1\rightarrow 右 v+1\rightarrow 左 u$拼成,于是对于所有$(u,v)$检查这样的环边权和是否是$0$即可。

    将信息排序后双指针即可完成检查。

    时间复杂度$O(k\log k+n+m)$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=100005,M=300005;
    int Case,n,m,i,j,k,o,nxt,A,B,C,D,st[N],en[N];
    struct E{int x,l,r,v;}e[M];
    inline bool cmp(const E&a,const E&b){return a.x==b.x?a.l<b.l:a.x<b.x;}
    inline void up(int x){
      if(x<o||x>nxt)return;
      nxt=x;
    }
    bool check(){
      scanf("%d%d",&n,&m);
      if(n<m)n=m;
      scanf("%d",&m);
      for(i=1;i<=m;i++)scanf("%d%d%d%d",&e[i].x,&e[i].l,&e[i].r,&e[i].v);
      sort(e+1,e+m+1,cmp);
      for(i=1;i<=n;i++)st[i]=m+1,en[i]=0;
      for(i=1;i<=m;i++)en[e[i].x]=i;
      for(i=m;i;i--)st[e[i].x]=i;
      for(i=1;i<n;i++){
        j=st[i],k=st[i+1],o=1;
        while(o<n){
          while(j<=en[i]&&e[j].r<o)j++;
          while(k<=en[i+1]&&e[k].r<o)k++;
          A=0;
          if(j<=en[i]&&e[j].l<=o)A=e[j].v;
          B=0;
          if(k<=en[i+1]&&e[k].l<=o)B=e[k].v;
          o++;
          while(j<=en[i]&&e[j].r<o)j++;
          while(k<=en[i+1]&&e[k].r<o)k++;
          C=0;
          if(j<=en[i]&&e[j].l<=o)C=e[j].v;
          D=0;
          if(k<=en[i+1]&&e[k].l<=o)D=e[k].v;
          if(A+D!=B+C)return 0;
          nxt=n;
          up(n-1);
          if(j<=en[i]){
            up(e[j].l-2);
            up(e[j].l-1);
            up(e[j].r-1);
            up(e[j].r);
          }
          if(k<=en[i+1]){
            up(e[k].l-2);
            up(e[k].l-1);
            up(e[k].r-1);
            up(e[k].r);
          }
          o=nxt;
        }
      }
      return 1;
    }
    int main(){
      scanf("%d",&Case);
      while(Case--)puts(check()?"TAK":"NIE");
    }
    

      

    Exam

    即求出与每个矩形有交的编号最大的矩形$f[i]$,若$f[i]\leq i$则矩形$i$处于顶层。

    枚举矩形$i$和矩形$j$的形状,那么询问范围是二维滑窗,对着$x$扫描线,对着$y$维护线段树即可。

    时间复杂度$O(n\log n)$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=100010,M=262150;
    int n,m,A,B,i,j,k,tmp,q[N],id[N],pos[N],v[M],f[N],ans,fin[N];
    struct E{int x,y,r,t;}e[N];
    inline bool cmpe(const E&a,const E&b){return a.x<b.x;}
    inline bool cmp(int x,int y){return e[x].y<e[y].y;}
    void build(int x,int a,int b){
      v[x]=0;
      if(a==b){pos[a]=x;return;}
      int mid=(a+b)>>1;
      build(x<<1,a,mid),build(x<<1|1,mid+1,b);
    }
    inline void change(int x,int p){
      v[x=pos[x]]=p;
      for(x>>=1;x;x>>=1)v[x]=max(v[x<<1],v[x<<1|1]);
    }
    inline void up(int&a,int b){a<b?(a=b):0;}
    void ask(int x,int a,int b,int c,int d){
      if(e[q[a]].y>=d||e[q[b]].y<=c||!v[x])return;
      if(e[q[a]].y>c&&e[q[b]].y<d){up(tmp,v[x]);return;}
      if(a==b)return;
      int mid=(a+b)>>1;
      ask(x<<1,a,mid,c,d);
      ask(x<<1|1,mid+1,b,c,d);
    }
    void gao(int me,int him,int xl,int xr,int yl,int yr){
      for(m=0,i=1;i<=n;i++)if(e[i].r==him)q[++m]=i;
      if(!m)return;
      sort(q+1,q+m+1,cmp);
      for(i=1;i<=m;i++)id[q[i]]=i;
      build(1,1,m);
      for(i=j=k=1;i<=n;i++)if(e[i].r==me){
        while(j<=n&&e[j].x<e[i].x+xr){
          if(e[j].r==him)change(id[j],e[j].t);
          j++;
        }
        while(k<=n&&e[k].x<=e[i].x+xl){
          if(e[k].r==him)change(id[k],0);
          k++;
        }
        tmp=0;
        ask(1,1,m,e[i].y+yl,e[i].y+yr);
        up(f[e[i].t],tmp);
      }
    }
    int main(){
      scanf("%d%d%d",&n,&A,&B);
      for(i=1;i<=n;i++)scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].r),e[i].t=i;
      sort(e+1,e+n+1,cmpe);
      gao(0,0,-B,B,-A,A);
      gao(1,1,-A,A,-B,B);
      gao(0,1,-A,B,-B,A);
      gao(1,0,-B,A,-A,B);
      for(i=1;i<=n;i++)if(f[i]<=i)fin[++ans]=i;
      printf("%d\n",ans);
      for(i=1;i<=ans;i++)printf("%d ",fin[i]);
    }
    

      

    Computational Geometry

    $n$为奇数则无解,否则$n$的方案可以由$n-2$的方案右边拼上$1\times 2$或$2\times 1$的矩形得到。

    #include<cstdio>
    int n,i,o,x;
    int main(){
      scanf("%d",&n);
      if(n&1)return puts("NIE"),0;
      puts("0 0");
      puts("0 2");
      puts("2 2");
      for(i=4,x=2;i<n;i+=2,o^=1){
        if(!o){
          printf("%d 1\n",x);
          printf("%d 1\n",x+=2);
        }else{
          printf("%d 2\n",x);
          printf("%d 2\n",x+=1);
        }
      }
      printf("%d 0",x);
    }
    

      

    Coprime Numbers

    设$g_i$表示数字$i$倍数的出现次数,$f_i$表示有多少对数字的最大公约数是$i$的倍数,则$f_i=C(g_i,2)-f_{2i}-f_{3i}-\dots$。

    时间复杂度$O(a\log a)$。

    #include<cstdio>
    const int M=3000005;
    int n,m,i,j,x,c[M];long long f[M];
    int main(){
      scanf("%d",&n);
      while(n--){
        scanf("%d",&x);
        c[x]++;
        if(x>m)m=x;
      }
      for(i=m;i;i--){
        for(j=i;j<=m;j+=i)f[i]+=c[j];
        f[i]=1LL*f[i]*(f[i]-1);
        for(j=i+i;j<=m;j+=i)f[i]-=f[j];
      }
      printf("%lld",f[1]/2);
    }
    

      

    Prime prime power

    对于$a^b$,如果$b=2$,那么在$[\sqrt{n},\sqrt{n}+k\log k]$内必定能找到$k$个质数作为$a$。

    筛出$n^{\frac{1}{4}}$内的所有质数,暴力枚举所有落在该区间内的倍数,将其筛掉,即可判断每个数是否是质数。

    然后以最大的质数的平方作为上界,枚举更大的$a$和$b$,这里方案数指数级下降,故暴力即可。

    最后排序输出第$k$小的值即可。

    时间复杂度$O(n^{\frac{1}{3}}+k\log^2k)$。

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int N=1050010;
    ll n,lim,t,o,b[66],q[N*2];int cnt,k,m,i,j,a[66],p[N],tot;bool v[N],vis[N*2];
    inline ll pow(ll a,int b){
      ll t=1;
      while(b--){
        if(a>lim/t)return lim+1;
        t*=a;
      }
      return t;
    }
    inline ll powfast(ll a,int b){ll t=1;for(;b;b>>=1,a*=a)if(b&1)t*=a;return t;}
    void sieve(ll n){
      int i,j;
      for(tot=0,i=2;i<=n;i++){
        if(!v[i])p[tot++]=i;
        for(j=0;j<tot&&i*p[j]<=n;j++){
          v[i*p[j]]=1;
          if(i%p[j]==0)break;
        }
      }
    }
    inline void check(int x){for(ll i=max(t/x*x,2LL*x);i<=lim;i+=x)if(i>=t)vis[i-t]=1;}
    int main(){
      scanf("%lld%d",&n,&k);
      t=max((ll)sqrt(n)-10,2LL);
      while(t*t<=n)t++;
      sieve((ll)sqrt(lim=t+max(k,10)*21)+5);
      for(i=0;i<tot;i++)check(p[i]);
      for(o=t;o<=lim&&cnt<k;o++)if(!vis[o-t])q[++cnt]=o*o;
      lim=q[cnt];
      for(i=3;;a[++m]=i++)if((1LL<<i)>lim)break;
      for(b[1]=2;(b[1]+1)*(b[1]+1)*(b[1]+1)<=lim;b[1]++);
      for(i=2;i<=m;i++)for(b[i]=b[i-1];pow(b[i],a[i])>lim;b[i]--);
      sieve(max(b[1],1LL*a[m]));
      for(i=1;i<=m;i++)if(!v[a[i]]){
        for(j=0;j<tot&&powfast(p[j],a[i])<=n;j++);
        for(;j<tot&&p[j]<=b[i];j++)q[++cnt]=powfast(p[j],a[i]);
      }
      sort(q+1,q+cnt+1);
      printf("%lld",q[k]);
    }
    

      

    Hard Choice

    在每条边两个点中间加上一个虚拟点代表这条边权,就可以化边权为点权。

    把没删掉的边用LCT维护一棵生成树,树边都是桥。

    对于一条非树边,把树上对应路径上所有边的权值都修改为$0$,表示都不是桥。

    然后倒着处理询问,对于每次删掉的边,把两点路径上边权都修改为$0$。

    询问等价于查询两点间边权和,若两点连通且路径上不存在桥,则有解。

    #include<cstdio>
    #include<map>
    const int N=200010,BUF=5000000;
    char Buf[BUF],*buf=Buf;
    int a[N],n,m,i,x,fa[N],edge[N][2],ask[N][4],q;
    struct LCT{int f,son[2],sum,data;bool rev,tag;}T[N];
    int father(int x){return fa[x]==x?x:fa[x]=father(fa[x]);}
    std::map<int,bool>del[N>>1];
    inline void swap(int&a,int&b){int c=a;a=b;b=c;}
    inline bool isroot(int x){return !T[x].f||T[T[x].f].son[0]!=x&&T[T[x].f].son[1]!=x;}
    inline void rev1(int x){if(!x)return;swap(T[x].son[0],T[x].son[1]),T[x].rev^=1;}
    inline void makezero1(int x){if(!x)return;T[x].sum=T[x].data=0;T[x].tag=1;}
    inline void pb(int x){
      if(T[x].rev)rev1(T[x].son[0]),rev1(T[x].son[1]),T[x].rev=0;
      if(T[x].tag)makezero1(T[x].son[0]),makezero1(T[x].son[1]),T[x].tag=0;
    }
    inline void up(int x){T[x].sum=T[x].data|T[T[x].son[0]].sum|T[T[x].son[1]].sum;}
    inline void rotate(int x){
      int y=T[x].f,w=T[y].son[1]==x;
      T[y].son[w]=T[x].son[w^1];
      if(T[x].son[w^1])T[T[x].son[w^1]].f=y;
      if(T[y].f){
        int z=T[y].f;
        if(T[z].son[0]==y)T[z].son[0]=x;else if(T[z].son[1]==y)T[z].son[1]=x;
      }
      T[x].f=T[y].f;T[x].son[w^1]=y;T[y].f=x;up(y);
    }
    inline void splay(int x){
      int s=1,i=x,y;a[1]=i;
      while(!isroot(i))a[++s]=i=T[i].f;
      while(s)pb(a[s--]);
      while(!isroot(x)){
        y=T[x].f;
        if(!isroot(y)){if((T[T[y].f].son[0]==y)^(T[y].son[0]==x))rotate(x);else rotate(y);}
        rotate(x);
      }
      up(x);
    }
    inline void access(int x){for(int y=0;x;y=x,x=T[x].f)splay(x),T[x].son[1]=y,up(x);}
    inline void makeroot(int x){access(x);splay(x);rev1(x);}
    inline void link(int x,int y){makeroot(x);T[x].f=y;access(x);}
    inline void makezero(int x,int y){
      if(father(x)!=father(y)){
        fa[father(x)]=father(y);
        n++;
        T[n].sum=T[n].data=1;
        link(x,n);link(n,y);
        return;
      }
      makeroot(x);
      access(y);
      splay(x);
      makezero1(x);
    }
    inline int getsum(int x,int y){
      if(father(x)!=father(y))return 1;
      makeroot(x);
      access(y);
      splay(x);
      return T[x].sum;
    }
    inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}
    int main(){
      fread(Buf,1,BUF,stdin);read(n);read(m);read(q);
      for(i=1;i<=n;i++)fa[i]=i;
      for(i=1;i<=m;i++){
        read(edge[i][0]);read(edge[i][1]);
        if(edge[i][0]>edge[i][1])swap(edge[i][0],edge[i][1]);
      }
      for(i=1;i<=q;i++){
        while(*buf!='Z'&&*buf!='P')buf++;
        ask[i][0]=x=*buf=='P',buf++;
        read(ask[i][1]);read(ask[i][2]);
        if(ask[i][1]>ask[i][2])swap(ask[i][1],ask[i][2]);
        if(!x)del[ask[i][1]][ask[i][2]]=1;
      }
      for(i=1;i<=m;i++)if(!del[edge[i][0]][edge[i][1]])if(father(edge[i][0])!=father(edge[i][1])){
        fa[father(edge[i][0])]=father(edge[i][1]);
        n++;
        T[n].sum=T[n].data=1;
        link(edge[i][0],n);link(n,edge[i][1]);
        del[edge[i][0]][edge[i][1]]=1;
      }
      for(i=1;i<=m;i++)if(!del[edge[i][0]][edge[i][1]])makezero(edge[i][0],edge[i][1]);
      for(i=q;i;i--)if(!ask[i][0])makezero(ask[i][1],ask[i][2]);else ask[i][3]=getsum(ask[i][1],ask[i][2]);
      for(i=1;i<=q;i++)if(ask[i][0])puts(ask[i][3]?"NIE":"TAK");
    }
    

      

  • 相关阅读:
    知识点
    nodejs总结之redis模块
    nodejs总结之日志模块log4js
    各种类型的串口说明
    linux常用命令
    JAVA总结之编码
    JAVA总结之异常
    JAVA总结之方法重载
    JAVA总结之关键字static和final
    JAVA总结之数组篇
  • 原文地址:https://www.cnblogs.com/clrs97/p/16796622.html
Copyright © 2020-2023  润新知