zoukankan      html  css  js  c++  java
  • Potyczki Algorythmiczne 2019

    Runda próbna:

    A + B

    设$f[i]$表示两数相加得到前$i$位的方案数,由$f[i-1]$和$f[i-2]$转移得到。

    #include<cstdio>
    #include<cstring>
    typedef long long ll;
    const int N=50;
    int n,i,j,k,a[N],b[N][N];ll f[N];char s[N];
    int main(){
      for(i=0;i<10;i++)for(j=0;j<10;j++){
        k=i+j;
        if(k<10)a[k]++;
        else b[k/10][k%10]++;
      }
      scanf("%s",s+1);
      n=strlen(s+1);
      for(i=1;i<=n;i++)s[i]-='0';
      f[0]=1;
      for(i=0;i<=n;i++){
        f[i+1]+=f[i]*a[s[i+1]];
        f[i+2]+=f[i]*b[s[i+1]][s[i+2]];
      }
      printf("%lld",f[n]);
    }
    

      

    Runda 1:

    Wina [B]

    求出为了拿走每个数至少需要拿走几个数即可。

    #include<cstdio>
    const int N=2010;
    int n,k,i,j,a[N][N],f[N][N],ans=~0U>>1;
    int main(){
      scanf("%d%d",&n,&k);
      for(i=1;i<=n;i++)for(j=1;j<=i;j++)scanf("%d",&a[i][j]),f[i][j]=1;
      for(i=1;i<=n;i++)for(j=1;j<=i;j++){
        f[i][j]+=f[i-1][j-1]+f[i-1][j];
        if(i>=2)f[i][j]-=f[i-2][j-1];
        if(f[i][j]<=k&&a[i][j]<ans)ans=a[i][j];
      }
      printf("%d",ans);
    }
    

      

    Muzyka pop [A]

    数位DP,设$f[i][j][l][r]$表示已经考虑了二进制下最高$i$位,是否卡住上界为$j$,要在里面找数字分配给$a[l..r]$的最大得分,枚举分界线转移。

    时间复杂度$O(n^3log m)$。

    #include<cstdio>
    typedef long long ll;
    const int N=67,M=205;
    const ll inf=3000000000000000000LL;
    int n,len,i,b[N];ll m,a[M],s[M],f[N][2][M][M];bool v[N][2][M][M];
    inline void up(ll&a,ll b){a<b?(a=b):0;}
    ll dfs(int x,int y,int l,int r){
      if(l>r)return 0;
      if(!x)return l==r?0:-inf;
      if(v[x][y][l][r])return f[x][y][l][r];
      v[x][y][l][r]=1;
      ll tmp=-inf;
      int ny=y;
      if(y==1&&b[x]==1)ny=0;
      up(tmp,dfs(x-1,ny,l,r));
      if(y==0||b[x]==1)for(int i=l;i<=r;i++)up(tmp,dfs(x-1,ny,l,i-1)+dfs(x-1,y,i,r)+s[r]-s[i-1]);
      return f[x][y][l][r]=tmp;
    }
    int main(){
      scanf("%d%lld",&n,&m);
      if(!m)return puts("0"),0;
      while(m)b[++len]=m&1,m>>=1;
      for(i=1;i<=n;i++)scanf("%lld",&a[i]),s[i]=s[i-1]+a[i];
      printf("%lld",dfs(len,1,1,n));
    }
    

      

    Runda 2:

    Herbata [B]

    对于每种温度$i$,统计初始状态和最终状态下有多少升温度为$i$的水。按照温度从高到低模拟,如果任意时刻能量差非负且最后能量守恒,则合法。

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

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=100010;
    int Case,n,i,j,tmp,l,a,b;ll e;
    struct E{int x,y;E(){}E(int _x,int _y){x=_x,y=_y;}}f[N],g[N];
    inline bool cmp(const E&a,const E&b){return a.x<b.x;}
    inline bool solve(){
      scanf("%d",&n);
      for(i=1;i<=n;i++){
        scanf("%d%d%d",&l,&a,&b);
        f[i]=E(a,l);
        g[i]=E(b,l);
      }
      sort(f+1,f+n+1,cmp);
      sort(g+1,g+n+1,cmp);
      for(i=j=1,e=0;i<=n;i++)while(f[i].y){
        while(!g[j].y)j++;
        tmp=min(f[i].y,g[j].y);
        f[i].y-=tmp;
        g[j].y-=tmp;
        e+=1LL*tmp*(g[j].x-f[i].x);
        if(e<0)return 0;
      }
      return !e;
    }
    int main(){
      scanf("%d",&Case);
      while(Case--)puts(solve()?"TAK":"NIE");
    }
    

      

    Desant [A]

    动态规划,设$f[i][S]$表示考虑了前$i$个数,这些数的选择情况为$S$的最小逆序对数以及对应的方案数。

    状态优化:假设$[i+1,n]$这些数从小到大排序后是___X___X____X_____X___的形式,那么X__1__1__1_1X和X1111_______X是没有区别的。利用位运算和基数排序等方法可以将转移做到$O(1)$。

    对于一个$i$来说,其状态数在最坏情况下等价于将$n$划分为若干个数的和,使得这些数的乘积最大,因此总状态数不超过$O(n3^{frac{n}{3}})$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=45,M=7000000,K=(1<<20)-1;
    int n,i,j,l,r,a[N],cf,cg,ans[N];ll cnt[N],S,nxt,need;char tab[65537];
    int c[K+8],q[M],p[M];
    struct E{
      ll S,v;
      E(){}
      E(ll _S,ll _v){S=_S,v=_v;}
    }f[M],g[M];
    inline bool cmp(const E&a,const E&b){return a.S<b.S;}
    inline void up(int x,int a,ll b){
      if(ans[x]<a)return;
      if(ans[x]==a)cnt[x]+=b;
      else ans[x]=a,cnt[x]=b;
    }
    inline void merge(){
      int i,j;
      cf=0;
      for(i=0;i<=K;i++)c[i]=0;
      for(i=1;i<=cg;i++)c[g[i].S&K]++;
      for(i=1;i<=K;i++)c[i]+=c[i-1];
      for(i=1;i<=cg;i++)q[c[g[i].S&K]--]=i;
      for(i=0;i<=K;i++)c[i]=0;
      for(i=1;i<=cg;i++)c[g[i].S>>20]++;
      for(i=1;i<=K;i++)c[i]+=c[i-1];
      for(i=cg;i;i--)p[c[g[q[i]].S>>20]--]=q[i];
      for(i=1;i<=cg;i=j){
        int a=M;ll b=0;
        for(j=i;j<=cg&&g[p[i]].S==g[p[j]].S;j++){
          if((g[p[j]].v&1023)<a)a=g[p[j]].v&1023,b=g[p[j]].v>>10;
          else if((g[p[j]].v&1023)==a)b+=g[p[j]].v>>10;
        }
        f[++cf]=E(g[p[i]].S,b<<10|a);
      }
      cg=0;
    }
    inline int popcount(ll S){return tab[S&65535]+tab[S>>16&65535]+tab[S>>32];}
    inline ll fix(ll S){
      return S^(S&need)^(((1LL<<popcount(S&need))-1)<<l);
    }
    int main(){
      for(i=0;i<65536;i++)tab[i]=__builtin_popcount(i);
      scanf("%d",&n);
      for(i=1;i<=n;i++)scanf("%d",&a[i]),a[i]--;
      f[cf=1]=E(0,1<<10);
      for(i=1;i<=n;i++){
        S=~0ULL>>1;
        for(j=0;j<=a[i];j++)S^=1LL<<j;
        nxt=0;
        for(j=i+1;j<=n;j++)nxt|=1LL<<a[j];
        for(l=a[i];~l;l--)if(nxt>>l&1)break;
        for(r=a[i];r<n;r++)if(nxt>>r&1)break;
        l++,r--;
        need=0;
        for(j=l;j<=r;j++)need|=1LL<<j;
        for(j=1;j<=cf;j++){
          g[++cg]=E(fix(f[j].S),f[j].v);
          g[++cg]=E(fix(f[j].S|(1LL<<a[i])),f[j].v+popcount(S&f[j].S));
        }
        merge();
      }
      for(i=0;i<=n;i++)ans[i]=M;
      for(i=1;i<=cf;i++)up(popcount(f[i].S),f[i].v&1023,f[i].v>>10);
      for(i=1;i<=n;i++)printf("%d %lld
    ",ans[i],cnt[i]);
    }
    

      

    Runda 3:

    Terytoria [B]

    两维独立,考虑一维的情况。

    所有区间的交集等于总的减去所有区间的补的并集。将坐标离散化后,枚举一个必然不属于补的并集的位置,那么所有区间的方案都定了,线段树维护并集的大小即可。

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

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=500010,M=2111111;
    int n,m,_,X,Y,i,j,x,y,e[N][4],a[N],b[N],c[N<<1],ans;
    int gl[N<<1],gr[N<<1],v[N<<1],nxt[N<<1],ed;
    int cov[N<<1],mi[M],val[M],tag[M];
    inline void add(int&x,int y){v[++ed]=y;nxt[ed]=x;x=ed;}
    inline void tag1(int x,int p){mi[x]+=p;tag[x]+=p;}
    inline void up(int x){
      if(mi[x<<1]<mi[x<<1|1]){
        mi[x]=mi[x<<1];
        val[x]=val[x<<1];
      }else if(mi[x<<1]>mi[x<<1|1]){
        mi[x]=mi[x<<1|1];
        val[x]=val[x<<1|1];
      }else{
        mi[x]=mi[x<<1];
        val[x]=val[x<<1]+val[x<<1|1];
      }
    }
    void build(int x,int a,int b){
      tag[x]=0;
      if(a==b){
        mi[x]=cov[a];
        val[x]=c[a+1]-c[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 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+1,b,c,d,p);
      up(x);
    }
    int solve(int all){
      c[1]=0;
      c[m=2]=all;
      for(i=1;i<=n;i++)c[++m]=a[i],c[++m]=b[i];
      sort(c+1,c+m+1);
      for(_=0,i=1;i<=m;i++)if(i==1||c[i]>c[_])c[++_]=c[i];
      m=_;
      for(i=1;i<=m;i++)gl[i]=gr[i]=cov[i]=0;
      ed=0;
      for(i=1;i<=n;i++){
        x=a[i],y=b[i];
        if(x>y)swap(x,y);
        x=lower_bound(c+1,c+m+1,x)-c;
        y=lower_bound(c+1,c+m+1,y)-c;
        a[i]=x;
        b[i]=y;
        add(gl[x],i);
        add(gr[y],i);
        cov[x]++;
        cov[y]--;
      }
      for(i=1;i<=m;i++)cov[i]+=cov[i-1];
      ans=0;
      build(1,1,m-1);
      for(i=1;i<m;i++){
        for(j=gl[i];j;j=nxt[j]){
          change(1,1,m-1,a[v[j]],b[v[j]]-1,-2);
          change(1,1,m-1,1,m-1,1);
        }
        for(j=gr[i];j;j=nxt[j]){
          change(1,1,m-1,a[v[j]],b[v[j]]-1,2);
          change(1,1,m-1,1,m-1,-1);
        }
        if(!mi[1])ans=max(ans,val[1]);
      }
      return ans;
    }
    int main(){
      scanf("%d%d%d",&n,&X,&Y);
      for(i=1;i<=n;i++)scanf("%d%d%d%d",&e[i][0],&e[i][1],&e[i][2],&e[i][3]);
      for(i=1;i<=n;i++)a[i]=e[i][0],b[i]=e[i][2];
      int ansx=solve(X);
      for(i=1;i<=n;i++)a[i]=e[i][1],b[i]=e[i][3];
      int ansy=solve(Y);
      printf("%lld",1LL*ansx*ansy);
    }
    

      

    Iloczyny Fibonacciego [A]

    令$f_2=1,f_3=2,f_i=f_{i-1}+f_{i-2}(igeq 4),s_i=f_i+s_{i-4}$。

    则对于$xgeq ygeq 2$:

    • 如果$y$是偶数,那么$f_xf_y=s_{x+y-2}+f_{x-y+2}-s_{x-y+2}$。
    • 如果$y$是奇数且$x eq y$,那么$f_xf_y=s_{x+y-2}+f_{x-y+4}-s_{x-y+4}+f_{x-y+1}$。
    • 如果$y$是奇数且$x=y$,那么$f_xf_y=s_{x+y-2}+f_{x-y+4}-s_{x-y+4}+f_{2}$。

    首先处理掉$x=y$的贡献,容易发现剩下的贡献只和$y$的奇偶性以及$x-y$和$x+y$有关,构造多项式卷积利用NTT求出贡献即可。

    那么剩下的问题就是将$sum a_if_i$的$a$转化成合法的斐波那契表示。

    分治,设$solve(l,r)$表示$sum_{i=l}^r a_if_i$的斐波那契表示,如果将前导0和后缀0都去掉的话,那么它的长度不超过$O(r-l+log n)$。

    • 若$l<r$,取$mid=lfloorfrac{l+r}{2} floor$,则$solve(l,r)=solve(l,mid)+solve(mid+1,r)$,而斐波那契进制的加法可以在线性时间内完成。
    • 若$l=r$,则可以利用$(f_{x-2}+f_{x})f_l=f(l-x)+f(l+x)$($x$是偶数)将$a_l$拆成$O(log n)$个互不相邻的$2$,然后用斐波那契进制的加法将其修正为合法的斐波那契表示;需要注意的是如果$l$太小以至$l-x$越界的话,这时$a_lf_l$的数值一定不大,可以暴力分解为斐波那契表示。

    时间复杂度$T(n)=2T(frac{n}{2})+O(n+log n)=O(nlog n)$。

    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<string>
    using namespace std;
    typedef long long ll;
    typedef pair<int,string>E;
    const int N=2111111;
    int Case,lenx,leny,n,i,x,k,a[N],b[N],A[N],B[N],C[N];ll f[N],s[N];E fin;
    typedef unsigned int uint32;
    typedef long long int64;
    typedef unsigned long long uint64;
    typedef uint32 word;
    typedef uint64 dword;
    typedef int sword;
    const int word_bits=sizeof(word)*8;
    word mod,Modinv,r2;
    struct UnsafeMod{
      word x;
      UnsafeMod(): x(0) {}
      UnsafeMod(word _x): x(init(_x)) {}
      UnsafeMod& operator += (const UnsafeMod& rhs) {
        (x += rhs.x) >= mod && (x -= mod);
        return *this;
      }
      UnsafeMod& operator -= (const UnsafeMod& rhs) {
        sword(x -= rhs.x) < 0 && (x += mod);
        return *this;
      }
      UnsafeMod& operator *= (const UnsafeMod& rhs) {
        x = reduce(dword(x) * rhs.x);
        return *this;
      }
      UnsafeMod operator + (const UnsafeMod &rhs) const {
        return UnsafeMod(*this) += rhs;
      }
      UnsafeMod operator - (const UnsafeMod &rhs) const {
        return UnsafeMod(*this) -= rhs;
      }
      UnsafeMod operator * (const UnsafeMod &rhs) const {
        return UnsafeMod(*this) *= rhs;
      }
      UnsafeMod pow(uint64 e) const {
        UnsafeMod ret(1);
        for (UnsafeMod base = *this; e; e >>= 1, base *= base) {
          if (e & 1) ret *= base;
        }
        return ret;
      }
      word get() const {
        return reduce(x);
      }
      static word modulus() {
        return mod;
      }
      static word init(word w) {
        return reduce(dword(w) * r2);
      }
      static void set_mod(word m) {
        mod = m;
        Modinv = mul_inv(mod);
        r2 = -dword(mod) % mod;
      }
      static word reduce(dword x) {
        word y = word(x >> word_bits) - word((dword(word(x) * Modinv) * mod) >> word_bits);
        return sword(y) < 0 ? y + mod : y;
      }
      static word mul_inv(word n, int e = 6, word x = 1) {
        return !e ? x : mul_inv(n, e - 1, x * (2 - x * n));
      }
    }pool[N];
    namespace NTT{
    const int N=1048576*2,K=20,P=998244353,G=3;
    UnsafeMod A[N+10],B[N+10],C[N+10];
    UnsafeMod g[K+1],ng[K+10],gw[N+10],ngw[N+10];
    int pos[N+10],inv[N+10];
    inline void doNTT(UnsafeMod*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;
        UnsafeMod*_w=t==1?gw:ngw;
        _w+=m;
        for(int i=0;i<n;i+=m2){
          UnsafeMod*w=_w;
          for(int j=i;j<m+i;j++,w++){
            UnsafeMod t=*w*a[j+m];
            a[j+m]=a[j]-t;
            a[j]+=t;
          }
        }
      }
      if(t==-1){
        UnsafeMod j=inv[n];
        for(int i=0;i<n;i++)a[i]*=j;
      }
    };
    void trans(int k,int*a,UnsafeMod*A){
      int i;
      for(i=0;i<k;i++)A[i]=a[i];
      doNTT(A,k,1);
    }
    void mul(int k,int*a,UnsafeMod*B,int*c){
      int i;
      for(i=0;i<k;i++)A[i]=a[i];
      doNTT(A,k,1);
      for(i=0;i<k;i++)C[i]=A[i]*B[i];
      doNTT(C,k,-1);
      for(i=0;i<k;i++)c[i]=C[i].get();
    }
    void pre(){
      int i,j;
      UnsafeMod::set_mod(P);
      for(g[K]=((UnsafeMod)G).pow((P-1)/N),ng[K]=g[K].pow(P-2),i=K-1;~i;i--)g[i]=g[i+1]*g[i+1],ng[i]=ng[i+1]*ng[i+1];
      for(i=0;i<=K;i++){
        gw[1<<i]=ngw[1<<i]=1;
        for(j=1;j<1<<i;j++){
          gw[(1<<i)+j]=gw[(1<<i)+j-1]*g[i];
          ngw[(1<<i)+j]=ngw[(1<<i)+j-1]*ng[i];
        }
      }
      for(inv[1]=1,i=2;i<=N;i++)inv[i]=1LL*(P-inv[P%i])*(P/i)%P;
    }
    void init(int k){
      int i,j;
      j=__builtin_ctz(k)-1;
      for(i=0;i<k;i++)pos[i]=pos[i>>1]>>1|((i&1)<<j);
    }
    }
    namespace FIB{
    const int M=100;
    int n=90,m,a[M];ll b[M],f[M];
    int q[N],len,L;
    void init(){
      for(f[0]=f[1]=1,i=2;i<=n;i++)f[i]=f[i-1]+f[i-2];
      for(i=2;i<=n;i+=2){
        a[++m]=i;
        b[m]=f[i-2]+f[i];
      }
      for(f[1]=f[2]=1,i=3;i<=n;i++)f[i]=f[i-1]+f[i-2];
    }
    inline void add(int x,int y){
      x-=L;
      while(len<=x)q[len++]=0;
      q[x]+=y;
    }
    inline void up(int x){for(;q[x]&&q[x+1];x+=2)q[x]--,q[x+1]--,q[x+2]++;}
    inline E fix(){
      int i;
      for(i=0;i<5;i++)q[len++]=0;
      int t=min(L-1,5);
      if(L-t==2)t++;
      len+=t;
      L-=t;
      for(i=len-1;i>=t;i--)q[i]=q[i-t];
      for(;~i;i--)q[i]=0;
      for(i=len-1;i;i--){
        if(q[i]==3)q[i]=0,q[i+2]++,up(i+2),q[i-2]++,up(i-3);
        else if(q[i]==2){
          if(q[i-1])q[i]=0,q[i-1]--,q[i+2]++,up(i+2);
          else q[i]=0,q[i+1]++,up(i+1),q[i-2]++,up(i-3);
        }else up(i-1);
      }
      if(L==1)q[1]+=q[0],q[0]=0;
      while(!q[len-1])len--;
      for(t=0;!q[t];t++,L++);
      E ret(L,string(len-t,0));
      for(i=t;i<len;i++)ret.second[i-t]=q[i];
      return ret;
    }
    inline E single(int A,ll B){
      if(!B)return E(0,"");
      if(B==1)return E(A,string(1,1));
      int x=m,i;
      while(x&&b[x]>B)x--;
      if(A-a[x]<2){
        B*=f[A];
        L=2;
        len=0;
        for(x=n;x>=2;x--)if(B>=f[x])add(x,1),B-=f[x];
        E ret(L,string(len,0));
        for(i=0;i<len;i++)ret.second[i]=q[i];
        return ret;
      }
      L=A-a[x];
      len=0;
      for(;x;x--)while(B>=b[x]){
        add(A-a[x],1);
        add(A+a[x],1);
        B-=b[x];
      }
      add(A,B);
      return fix();
    }
    inline E merge(const E&A,const E&B){
      if(!A.first)return B;
      if(!B.first)return A;
      L=min(A.first,B.first);
      len=0;
      for(int i=0;i<A.second.size();i++)add(A.first+i,A.second[i]);
      for(int i=0;i<B.second.size();i++)add(B.first+i,B.second[i]);
      return fix();
    }
    }
    void read(int&len,int*a){
      scanf("%d",&len);
      len++;
      a[1]=0;
      for(int i=2;i<=len;i++)scanf("%d",&a[i]);
    }
    E solve(int l,int r){
      if(l==r)return FIB::single(l,f[l]);
      int mid=(l+r)>>1;
      return FIB::merge(solve(l,mid),solve(mid+1,r));
    }
    int main(){
      NTT::pre();
      FIB::init();
      scanf("%d",&Case);
      while(Case--){
        read(lenx,a);
        read(leny,b);
        for(k=1;k<=lenx+leny;k<<=1);
        NTT::init(k);
        for(i=0;i<k;i++)A[i]=B[i]=f[i]=s[i]=0;
        for(i=2;i<=lenx;i++)A[i]=a[i];
        for(i=2;i<=leny;i++)B[i]=b[i];
        NTT::trans(k,A,pool);
        NTT::mul(k,B,pool,C);
        for(i=2;i<k;i++)s[i-2]+=C[i];
        for(i=3;i<=lenx&&i<=leny;i+=2)if(a[i]&&b[i])f[2]++;
        for(i=0;i<k;i++)A[i]=B[i]=0;
        for(i=2;i<=lenx;i+=2)A[i]=a[i];
        for(i=2;i<=leny;i++)B[leny-i]=b[i];
        NTT::trans(k,B,pool);
        NTT::mul(k,A,pool,C);
        for(i=0;i<k;i++)if(C[i]&&i<leny){
          x=leny-i;
          f[x+2]+=C[i];
          s[x+2]-=C[i];
        }
        for(i=0;i<k;i++)A[i]=0;
        for(i=3;i<=lenx;i+=2)A[i]=a[i];
        NTT::mul(k,A,pool,C);
        for(i=0;i<k;i++)if(C[i]&&i<leny){
          x=leny-i;
          f[x+4]+=C[i];
          s[x+4]-=C[i];
          f[x+1]+=C[i];
        }
        for(i=0;i<k;i++)A[i]=B[i]=0;
        for(i=2;i<=lenx;i++)A[i]=a[i];
        for(i=2;i<=leny;i+=2)B[leny-i]=b[i];
        NTT::trans(k,A,pool);
        NTT::mul(k,B,pool,C);
        for(i=0;i<k;i++)if(C[i]&&i>leny){
          x=i-leny;
          f[x+2]+=C[i];
          s[x+2]-=C[i];
        }
        for(i=0;i<k;i++)B[i]=0;
        for(i=3;i<=leny;i+=2)B[leny-i]=b[i];
        NTT::mul(k,B,pool,C);
        for(i=0;i<k;i++)if(C[i]&&i>leny){
          x=i-leny;
          f[x+4]+=C[i];
          s[x+4]-=C[i];
          f[x+1]+=C[i];
        }
        for(i=k-1;i>=4;i--)s[i-4]+=s[i];
        for(i=0;i<k;i++)f[i]+=s[i];
        n=k-1;
        while(!f[n])n--;
        fin=solve(2,n);
        printf("%d",fin.first+fin.second.size()-2);
        for(i=2;i<fin.first;i++)printf(" 0");
        for(i=0;i<fin.second.size();i++)printf(" %d",(int)fin.second[i]);
        puts("");
      }
    }
    

      

    Runda 4:

    Szprotki i szczupaki [B]

    显然最优策略是每次吃能吃的最大的数。

    离散化后建立权值线段树,对于每个询问暴力模拟,每次在线段树上二分找出一段要吃掉的后缀,使得吃掉它们之后可以解锁下一个数或者达到最终目标。容易发现每次解锁都会使得我方的重量翻倍,因此最多只有$O(log w)$次迭代。

    因为每个询问独立,因此需要在每次清空一个区间后将其保存下来,方便询问结束后还原。

    时间复杂度$O(qlog nlog w)$。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int N=400010,M=1111111;
    const ll inf=1LL<<60;
    int n,m,q,i,x,ce,have[N];
    ll a[N],e[N],op[N][3];
    int POS,vis[M];ll cnt[M],sum[M];
    int C;ll NXT,SUM,CNT;
    int pool[M],cp;
    ll sc[M],ss[M];
    inline void up(int x){
      cnt[x]=cnt[x<<1]+cnt[x<<1|1];
      sum[x]=sum[x<<1]+sum[x<<1|1];
    }
    void build(int x,int a,int b){
      if(a==b){
        cnt[x]=have[a];
        sum[x]=cnt[x]*e[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,ll A,ll B){
      sum[x]+=A;
      cnt[x]+=B;
      if(a==b)return;
      int mid=(a+b)>>1;
      if(c<=mid)change(x<<1,a,mid,c,A,B);else change(x<<1|1,mid+1,b,c,A,B);
    }
    void getnxt(int x,int a,int b){
      if(NXT)return;
      if(!cnt[x])return;
      if(a==b){
        NXT=e[a];
        return;
      }
      int mid=(a+b)>>1;
      if(C<=mid)getnxt(x<<1,a,mid);
      getnxt(x<<1|1,mid+1,b);
    }
    void eat(int x,int a,int b){
      if(vis[x]<POS){
        vis[x]=POS;
        pool[++cp]=x;
        sc[x]=cnt[x];
        ss[x]=sum[x];
      }
      if(SUM>NXT)return;
      if(!cnt[x])return;
      if(b<=C){
        if(SUM+sum[x]<=NXT){
          SUM+=sum[x];
          CNT+=cnt[x];
          cnt[x]=sum[x]=0;
          return;
        }
        if(a==b){
          ll tmp=(NXT-SUM)/e[a];
          while(tmp*e[a]+SUM<=NXT)tmp++;
          ll val=tmp*e[a];
          cnt[x]-=tmp,sum[x]-=val;
          SUM+=val;
          CNT+=tmp;
          return;
        }
      }
      int mid=(a+b)>>1;
      if(C>mid)eat(x<<1|1,mid+1,b);
      eat(x<<1,a,mid);
      up(x);
    }
    inline int lower(ll x){
      int l=1,r=m,t,mid;
      while(l<=r)if(e[mid=(l+r)>>1]>=x)r=(t=mid)-1;else l=mid+1;
      return t;
    }
    inline int query(ll A,ll B){
      int ans=0;
      cp=0;
      while(A<B){
        C=lower(A);
        NXT=0;
        getnxt(1,1,m);
        NXT=min(NXT,B-1);
        C--;
        SUM=A;
        CNT=0;
        eat(1,1,m);
        if(SUM<=NXT)return -1;
        A=SUM;
        ans+=CNT;
      }
      return ans;
    }
    int main(){
      scanf("%d",&n);
      e[ce=1]=0;
      for(i=1;i<=n;i++)scanf("%lld",&a[i]),e[++ce]=a[i];
      scanf("%d",&q);
      for(i=1;i<=q;i++){
        scanf("%lld%lld",&op[i][0],&op[i][1]);
        if(op[i][0]==1)scanf("%lld",&op[i][2]);
        if(op[i][0]==2)e[++ce]=op[i][1];
      }
      sort(e+1,e+ce+1);
      for(i=1;i<=ce;i++)if(i==1||e[i]>e[m])e[++m]=e[i];
      e[++m]=inf;
      have[m]=1;
      for(i=1;i<=n;i++)have[lower_bound(e+1,e+m+1,a[i])-e]++;
      build(1,1,m);
      for(i=1;i<=q;i++){
        POS++;
        if(op[i][0]==1){
          printf("%d
    ",query(op[i][1],op[i][2]));
          while(cp){
            x=pool[cp--];
            cnt[x]=sc[x];
            sum[x]=ss[x];
          }
        }
        if(op[i][0]==2)change(1,1,m,lower_bound(e+1,e+m+1,op[i][1])-e,op[i][1],1);
        if(op[i][0]==3)change(1,1,m,lower_bound(e+1,e+m+1,op[i][1])-e,-op[i][1],-1);
      }
    }
    

      

    Wyspa [A]

    首先将不能被任何一个湖边点到达的海边点删除。因为是平面图,所以每个湖边点能到达的海边点都是环上一个区间,也就是序列上不超过两个区间,可以缩SCC后在$O(nlog n)$的时间内递推求出这个范围。

    那么问题转化为给定环上一些区间,求选点的方案数使得每个区间内至少选了一个点。

    如果区间$A$包含$B$,那么满足$B$一定可以满足$A$,可以将$A$删除。同时我们可以离散化区间的左右端点使得点数和区间数同阶。通过旋转一个区间到$[1,len]$,我们可以发现从左往右选的第一个点一定在$[1,len]$范围内,枚举第一个选的点,那么跨过$1$和$n$的区间都可以视作序列上的正常区间,达到破环成链的目的。

    动态规划,设$f_i$表示考虑了前$i$个点,$i$点必选的方案数,则$f_i=sum f_j$,其中$[j+1,i-1]$不能含有完整的区间,令$g_i$表示右端点不超过$i$的区间中左端点的最大值,则$jgeq g_{i-1}$,可以利用前缀和$O(1)$转移。

    如果将最短的区间旋转到$[1,len]$的话,则时间复杂度为$O(min(len) imes cnt)$,其中$cnt$为区间数,因为平面图的限制这个乘积是$O(n)$级别的。

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=500010,M=2000010,P=1000000007;
    int n,m,ca,cb,cnt,i,j,k,x,y,scc,base=1;
    bool vis[N];
    int ed,g[N],v[M],nxt[M],G[N],V[M],NXT[M];
    int id[N],at[N],q[N],t;
    int pool[N],st[N],en[N],cp,last[N];
    int ce;
    struct Info{int a,b,c,d;}f[N];
    struct E{int l,r;E(){}E(int _l,int _r){l=_l,r=_r;}}e[N<<1];
    inline bool cmp(const E&a,const E&b){return a.l<b.l;}
    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';}
    inline void readch(char&a){while(!(((a=getchar())=='-')||(a=='>')));}
    inline void umin(int&a,int b){a>b?(a=b):0;}
    inline void umax(int&a,int b){a<b?(a=b):0;}
    inline void add(int x,int y){
      v[++ed]=y;nxt[ed]=g[x];g[x]=ed;
      V[ed]=x;NXT[ed]=G[y];G[y]=ed;
    }
    void dfs(int x){
      if(vis[x])return;
      vis[x]=1;
      for(int i=g[x];i;i=nxt[i])dfs(v[i]);
    }
    void dfs1(int x){
      if(vis[x])return;
      vis[x]=1;
      for(int i=g[x];i;i=nxt[i])dfs1(v[i]);
      q[++t]=x;
    }
    void dfs2(int x){
      if(!vis[x])return;
      vis[x]=0;
      at[x]=scc;
      pool[++cp]=x;
      for(int i=G[x];i;i=NXT[i])dfs2(V[i]);
    }
    inline void ext(int o,int x,int y){
      if(!x)return;
      if(!f[o].a)f[o].a=x,f[o].b=y;
      else if(!f[o].c)f[o].c=x,f[o].d=y;
      else puts("GG");
    }
    namespace COUNTING{
    typedef unsigned int uint32;
    typedef long long int64;
    typedef unsigned long long uint64;
    typedef uint32 word;
    typedef uint64 dword;
    typedef int sword;
    const int word_bits=sizeof(word)*8;
    word mod,Modinv,r2;
    struct UnsafeMod{
      word x;
      UnsafeMod(): x(0) {}
      UnsafeMod(word _x): x(init(_x)) {}
      UnsafeMod& operator += (const UnsafeMod& rhs) {
        (x += rhs.x) >= mod && (x -= mod);
        return *this;
      }
      UnsafeMod& operator -= (const UnsafeMod& rhs) {
        sword(x -= rhs.x) < 0 && (x += mod);
        return *this;
      }
      UnsafeMod& operator *= (const UnsafeMod& rhs) {
        x = reduce(dword(x) * rhs.x);
        return *this;
      }
      UnsafeMod operator + (const UnsafeMod &rhs) const {
        return UnsafeMod(*this) += rhs;
      }
      UnsafeMod operator - (const UnsafeMod &rhs) const {
        return UnsafeMod(*this) -= rhs;
      }
      UnsafeMod operator * (const UnsafeMod &rhs) const {
        return UnsafeMod(*this) *= rhs;
      }
      UnsafeMod pow(uint64 e) const {
        UnsafeMod ret(1);
        for (UnsafeMod base = *this; e; e >>= 1, base *= base) {
          if (e & 1) ret *= base;
        }
        return ret;
      }
      word get() const {
        return reduce(x);
      }
      static word modulus() {
        return mod;
      }
      static word init(word w) {
        return reduce(dword(w) * r2);
      }
      static void set_mod(word m) {
        mod = m;
        Modinv = mul_inv(mod);
        r2 = -dword(mod) % mod;
      }
      static word reduce(dword x) {
        word y = word(x >> word_bits) - word((dword(word(x) * Modinv) * mod) >> word_bits);
        return sword(y) < 0 ? y + mod : y;
      }
      static word mul_inv(word n, int e = 6, word x = 1) {
        return !e ? x : mul_inv(n, e - 1, x * (2 - x * n));
      }
    }dp[N],v[N],ans;
    int n,m,_,ca,i,j,k,x,y,e[N][2],is[N],a[N];
    int p[N],val[N];
    int minlen,S,now,en;
    int g[N],q[N][2],h,t;
    int f[N<<1],lim[N];
    inline void init(int A,int B,int C,int D){
      if(!C){
        e[++m][0]=A;
        e[m][1]=B;
        umax(f[B],A);
        umax(f[B+n],A+n);
      }else{
        e[++m][0]=C;
        e[m][1]=B;
        umax(f[B+n],C);
      }
    }
    inline void add(int l,int r){
      if(l<=r){
        if(f[r]>l)return;
        is[l-1]=is[r]=1;
      }else{
        if(f[r+n]>l)return;
        is[l-1]=is[r]=is[n]=1;
      }
      e[++_][0]=l;
      e[_][1]=r;
    }
    inline void ins(int x,int y){
      if(h<=t&&q[t][1]>=y)return;
      q[++t][0]=x;
      q[t][1]=y;
    }
    inline int askmin(int x){
      while(h<=t&&q[h][1]<=x)h++;
      if(h>t)return n;
      return q[h][0];
    }
    inline void cal(int lim,int n){
      for(int i=1;i<=n;i++){
        dp[i]=dp[i-1];
        if(f[i-1])dp[i]-=dp[f[i-1]-1];
        if(i<=lim)dp[i]+=1;
        dp[i]=dp[i]*v[i]+dp[i-1];
      }
    }
    int solve(){
      UnsafeMod::set_mod(P);
      for(i=1;i<=n+n;i++)umax(f[i],f[i-1]);
      for(i=1;i<=m;i++)add(e[i][0],e[i][1]);
      m=_;
      for(p[0]=i=1;i<=n;i++)p[i]=p[i-1]*2%P;
      for(i=0;i<=n;i++)p[i]--;
      is[n]=1;
      for(i=1;i<=n;i++)if(is[i])a[is[i]=++ca]=i;
      for(i=n;i;i--)if(!is[i])is[i]=is[i+1];
      n=ca;
      for(i=1;i<=n;i++)val[i]=p[a[i]-a[i-1]];
      minlen=n,S=1;
      for(i=1;i<=m;i++){
        x=is[e[i][0]],y=is[e[i][1]];
        if(x<=y)now=y-x+1;else now=y-x+1+n;
        if(now<=minlen)minlen=now,S=x;
      }
      for(i=1,j=S;i<=n;i++){
        v[i]=val[j];
        j++;
        if(j>n)j=1;
      }
      en=n;
      for(i=0;i<=n+1;i++)f[i]=0;
      for(i=1;i<=m;i++){
        x=is[e[i][0]],y=is[e[i][1]];
        if(y<x)y+=n;
        y=y-x;
        while(1){
          if(S<=x&&x<S+n)break;
          x+=n;
        }
        x-=S-1;
        if(y==n-1)x=1,y=n;else y+=x;
        if(y>n)y-=n;
        if(x<=y){
          umax(f[y],x);
          umin(en,y);
        }else{
          umax(g[y+1],x);
        }
      }
      for(i=1;i<=n;i++)umax(f[i],f[i-1]);
      h=1,t=0;
      for(i=1;i<=n;i++){
        now=en;
        if(g[i])ins(i-1,g[i]);
        umin(now,askmin(i));
        lim[i]=now;
      }
      for(i=max(f[n],1);i<=n;i=j){
        for(j=i;j<=n&&lim[i]==lim[j];j++);
        cal(lim[i],j-1);
        ans+=dp[j-1]-dp[i-1];
      }
      return ans.get();
    }
    }
    int main(){
      read(n);read(m);read(ca);read(cb);
      while(m--){
        char ch;
        read(x);
        readch(ch);
        readch(ch);
        read(y);
        add(x,y);
        if(ch=='-')add(y,x);
      }
      for(i=1;i<=ca;i++)dfs(i);
      for(i=ca+1;i<=ca+cb;i++)if(vis[i])id[i]=++cnt;else base=base*2%P;
      for(i=1;i<=n;i++)vis[i]=0;
      for(i=1;i<=n;i++)if(!vis[i])dfs1(i);
      for(i=t;i;i--)if(vis[q[i]]){
        scc++;
        st[scc]=cp+1;
        dfs2(q[i]);
        en[scc]=cp;
      }
      for(i=scc;i;i--){
        ce=0;
        for(j=st[i];j<=en[i];j++){
          x=pool[j];
          if(id[x])e[++ce]=E(id[x],id[x]);
          for(k=g[x];k;k=nxt[k]){
            y=at[v[k]];
            if(y==i)continue;
            if(last[y]==i)continue;
            last[y]=i;
            if(f[y].a)e[++ce]=E(f[y].a,f[y].b);
            if(f[y].c)e[++ce]=E(f[y].c,f[y].d);
          }
        }
        if(!ce)continue;
        sort(e+1,e+ce+1,cmp);
        x=0,y=-1;
        for(j=1;j<=ce;j++){
          if(e[j].l>y+1){
            ext(i,x,y);
            x=e[j].l;
          }
          umax(y,e[j].r);
        }
        ext(i,x,y);
      }
      COUNTING::n=cnt;
      for(i=1;i<=ca;i++){
        x=at[i];
        if(f[x].a){
          COUNTING::init(f[x].a,f[x].b,f[x].c,f[x].d);
          f[x].a=0;
        }
      }
      base=1LL*base*COUNTING::solve()%P;
      printf("%d",base);
    }
    

      

    Runda 5:

    Trzy kule [B]

    答案为必然满足一个条件的方案数$-$必然满足两个条件的方案数$+$必然满足三个条件的方案数。

    一个和两个的情况比较简单,可以枚举相同的位数利用组合数在$O(n)$时间内得到答案。

    对于三个的情况,不妨令第一个串为全0,那么对于每一位,一共有4种情况:000,001,010,011。

    令这四种情况的数量分别为$A,B,C,D$,这些位在要求的串中为1的数量分别为$a,b,c,d$,则有:

    • $a+b+c+dleq r_1$
    • $a+b+C-c+D-dleq r_2$
    • $a+B-b+c+D-dleq r_3$

    • $c+dleq r_1-a-b$
    • $c+dgeq a+b+C-r_2+D$
    • $c-dleq r_3-a-B+b-D$

    枚举$a$和$b$后,则对应的$c$和$d$是关于$c+d$和$c-d$的二维数点,二维前缀和即可。

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

    #include<cstdio>
    const int N=10005,P=1000000007;
    typedef unsigned int uint32;
    typedef long long int64;
    typedef unsigned long long uint64;
    typedef uint32 word;
    typedef uint64 dword;
    typedef int sword;
    const int word_bits=sizeof(word)*8;
    word mod,Modinv,r2;
    struct UnsafeMod{
      word x;
      UnsafeMod(): x(0) {}
      UnsafeMod(word _x): x(init(_x)) {}
      UnsafeMod& operator += (const UnsafeMod& rhs) {
        (x += rhs.x) >= mod && (x -= mod);
        return *this;
      }
      UnsafeMod& operator -= (const UnsafeMod& rhs) {
        sword(x -= rhs.x) < 0 && (x += mod);
        return *this;
      }
      UnsafeMod& operator *= (const UnsafeMod& rhs) {
        x = reduce(dword(x) * rhs.x);
        return *this;
      }
      UnsafeMod operator + (const UnsafeMod &rhs) const {
        return UnsafeMod(*this) += rhs;
      }
      UnsafeMod operator - (const UnsafeMod &rhs) const {
        return UnsafeMod(*this) -= rhs;
      }
      UnsafeMod operator * (const UnsafeMod &rhs) const {
        return UnsafeMod(*this) *= rhs;
      }
      UnsafeMod pow(uint64 e) const {
        UnsafeMod ret(1);
        for (UnsafeMod base = *this; e; e >>= 1, base *= base) {
          if (e & 1) ret *= base;
        }
        return ret;
      }
      word get() const {
        return reduce(x);
      }
      static word modulus() {
        return mod;
      }
      static word init(word w) {
        return reduce(dword(w) * r2);
      }
      static void set_mod(word m) {
        mod = m;
        Modinv = mul_inv(mod);
        r2 = -dword(mod) % mod;
      }
      static word reduce(dword x) {
        word y = word(x >> word_bits) - word((dword(word(x) * Modinv) * mod) >> word_bits);
        return sword(y) < 0 ? y + mod : y;
      }
      static word mul_inv(word n, int e = 6, word x = 1) {
        return !e ? x : mul_inv(n, e - 1, x * (2 - x * n));
      }
    }ans,f[N],g[N],s[N][N];
    int n,i,lim[3],fac[N],inv[N];char a[3][N];
    inline UnsafeMod getC(int n,int m){
      if(n<m)return 0;
      UnsafeMod ret=fac[n];
      ret*=inv[m];
      return ret*inv[n-m];
    }
    void pre(int n,UnsafeMod*f){for(int i=0;i<N;i++)f[i]=getC(n,i);}
    UnsafeMod one(int x){
      UnsafeMod ret=0;
      for(int i=0;i<=lim[x];i++)ret+=getC(n,i);
      return ret;
    }
    UnsafeMod two(int x,int y){
      UnsafeMod ret=0;
      int cnt=0,i,j;
      for(i=0;i<n;i++)if(a[x][i]==a[y][i])cnt++;
      pre(cnt,f);
      pre(n-cnt,g);
      for(i=0;i<=cnt;i++)for(j=0;j<=n-cnt;j++){
        if(i+j>lim[x])break;
        if(i+n-cnt-j>lim[y])continue;
        ret+=f[i]*g[j];
      }
      return ret;
    }
    UnsafeMod three(){
      UnsafeMod ret=0;
      int i,j,A=0,B=0,C=0,D=0,ra=lim[0],rb=lim[1],rc=lim[2];
      for(i=0;i<n;i++){
        int x=a[0][i]-'0',y=a[1][i]-'0',z=a[2][i]-'0';
        y^=x,z^=x;
        if(y==0&&z==0)A++;
        if(y==0&&z==1)B++;
        if(y==1&&z==0)C++;
        if(y==1&&z==1)D++;
      }
      pre(C,f),pre(D,g);
      for(i=0;i<=C;i++)for(j=0;j<=D;j++)s[i+j][i-j+D]+=f[i]*g[j];
      int m=C+D;
      for(i=0;i<=m;i++)for(j=0;j<=m;j++){
        if(i)s[i][j]+=s[i-1][j];
        if(j)s[i][j]+=s[i][j-1];
        if(i&&j)s[i][j]-=s[i-1][j-1];
      }
      pre(A,f),pre(B,g);
      for(i=0;i<=A;i++)for(j=0;j<=B&&i+j<=ra;j++){
        int l=i+j+C+D-rb;
        int r=ra-i-j;
        if(l<0)l=0;
        if(r>m)r=m;
        if(l>r)continue;
        int k=rc-i+j-B;
        if(k<0)continue;
        if(k>m)k=m;
        UnsafeMod tmp=s[r][k];
        if(l)tmp-=s[l-1][k];
        ret+=tmp*f[i]*g[j];
      }
      return ret;
    }
    int main(){
      UnsafeMod::set_mod(P);
      scanf("%d",&n);
      for(i=0;i<3;i++)scanf("%d%s",&lim[i],a[i]);
      for(fac[0]=i=1;i<=n;i++)fac[i]=1LL*fac[i-1]*i%P;
      for(inv[0]=inv[1]=1,i=2;i<=n;i++)inv[i]=1LL*(P-inv[P%i])*(P/i)%P;
      for(i=2;i<=n;i++)inv[i]=1LL*inv[i-1]*inv[i]%P;
      ans=one(0)+one(1)+one(2)-two(0,1)-two(0,2)-two(1,2)+three();
      printf("%u",ans.get());
    }
    

      

    Osady i warownie 2 [B]

    考虑两种最极端的从左上角到右下角的路径:尽量优先往右走,不能走时再往下走的路径和尽量优先往下走,不能走时再往右走的路径。如果两条路径的边界接触到了对方,那么不存在合法的路径,否则存在。如下图黄线和蓝线所示:

    线段树维护这两条路径,对于每个障碍$(r,c)$:

    • 如果$(r,c)$是起点或者终点,那么显然。
    • 否则如果它严格在黄线之上或者严格在蓝线之下,那么可以忽略。
    • 否则如果它同时接触到了黄线和蓝线,那么它会将黄线和蓝线连通,使得起点和终点不连通。
    • 否则如果它既没有接触到黄线,也没有接触到蓝线,那么将其放在池子里,暂时不会影响结果。
    • 否则如果它接触到了蓝线或者黄线,那么它的加入会导致蓝线或者黄线的扩张,在不断迭代扩张的过程中从池子里找出和线接触的障碍,用其继续扩张线。在这里可以对于每一行和每一列用std::set存储有哪些待定障碍。

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

    #include<cstdio>
    #include<algorithm>
    #include<set>
    using namespace std;
    const int N=100010,M=262150;
    int n,m,_,x,y,z,last,pos[N],mi[M],ma[M];
    int q[5555555][2],h,t;
    set<int>row[N],col[N];
    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';}
    inline void umin(int&a,int b){a>b?(a=b):0;}
    inline void umax(int&a,int b){a<b?(a=b):0;}
    void build(int x,int a,int b){
      if(a==b){
        pos[a]=x;
        mi[x]=n+1;
        ma[x]=0;
        if(a==0)mi[x]=0;
        if(a==m+1)ma[x]=n+1;
        return;
      }
      int mid=(a+b)>>1;
      build(x<<1,a,mid),build(x<<1|1,mid+1,b);
      mi[x]=min(mi[x<<1],mi[x<<1|1]);
      ma[x]=max(ma[x<<1],ma[x<<1|1]);
    }
    int askmin(int x,int a,int b,int c,int d){
      if(c<=a&&b<=d)return mi[x];
      int mid=(a+b)>>1,t=N;
      if(c<=mid)t=askmin(x<<1,a,mid,c,d);
      if(d>mid)umin(t,askmin(x<<1|1,mid+1,b,c,d));
      return t;
    }
    int askmax(int x,int a,int b,int c,int d){
      if(c<=a&&b<=d)return ma[x];
      int mid=(a+b)>>1,t=0;
      if(c<=mid)t=askmax(x<<1,a,mid,c,d);
      if(d>mid)umax(t,askmax(x<<1|1,mid+1,b,c,d));
      return t;
    }
    inline void changemin(int x,int p){for(x=pos[x];x;x>>=1)umin(mi[x],p);}
    inline void changemax(int x,int p){for(x=pos[x];x;x>>=1)umax(ma[x],p);}
    inline int checkS(int y,int x){
      int t=askmin(1,0,m+1,x,m+1);
      if(t<=y)return -1;
      if(t==y+1||askmin(1,0,m+1,x-1,m+1)<=y+1)return 0;
      return 1;
    }
    inline int checkT(int y,int x){
      int t=askmax(1,0,m+1,0,x);
      if(t>=y)return -1;
      if(t==y-1||askmax(1,0,m+1,0,x+1)>=y-1)return 0;
      return 1;
    }
    inline void extS(int x,int y){
      q[h=t=1][0]=x;
      q[1][1]=y;
      while(h<=t){
        x=q[h][0];
        y=q[h++][1];
        changemin(y,x);
        while(row[x-1].size()){
          set<int>::iterator it=row[x-1].begin();
          if((*it)<=y+1){
            q[++t][0]=x-1;
            q[t][1]=*it;
            row[x-1].erase(*it);
          }else break;
        }
        while(col[y+1].size()){
          set<int>::reverse_iterator it=col[y+1].rbegin();
          if((*it)>=x-1){
            q[++t][0]=*it;
            q[t][1]=y+1;
            col[y+1].erase(*it);
          }else break;
        }
      }
    }
    inline void extT(int x,int y){
      q[h=t=1][0]=x;
      q[1][1]=y;
      while(h<=t){
        x=q[h][0];
        y=q[h++][1];
        changemax(y,x);
        while(row[x+1].size()){
          set<int>::reverse_iterator it=row[x+1].rbegin();
          if((*it)>=y-1){
            q[++t][0]=x+1;
            q[t][1]=*it;
            row[x+1].erase(*it);
          }else break;
        }
        while(col[y-1].size()){
          set<int>::iterator it=col[y-1].begin();
          if((*it)<=x+1){
            q[++t][0]=*it;
            q[t][1]=y-1;
            col[y-1].erase(*it);
          }else break;
        }
      }
    }
    inline bool merge(int x,int y){
      x++,y++;
      if(x==1&&y==1)return 1;
      if(x==n&&y==m)return 1;
      int A=checkS(x,y);
      if(A<0)return 0;
      int B=checkT(x,y);
      if(B<0)return 0;
      if(A==0&&B==0)return 1;
      if(A==1&&B==1){
        row[x].insert(y);
        col[y].insert(x);
      }
      if(A==0)extS(x,y);
      if(B==0)extT(x,y);
      return 0;
    }
    int main(){
      read(n),read(m),read(_);
      build(1,0,m+1);
      while(_--){
        read(x),read(y),read(z);
        x=(x^last)%n;
        y=(y^last)%m;
        if(merge(x,y))puts("TAK"),last^=z;else puts("NIE");
      }
    }
    

      

    Podatki drogowe [A]

    如果边权不大,那么可以二分答案,然后统计有多少条路径的长度不超过$mid$。

    在二分答案之前对树进行点分治,在每个分治过程中将所有点到重心的距离从小到大排序,那么每次二分时只需要双指针统计方案数。

    现在边权很大,可以用可持久线段树来维护高精度数,做到$O(log n)$比较两个高精度数的大小,时间复杂度$O(nlog^3n)$。

    最后一个问题是如何对高精度数进行二分。注意到答案只能是$a_i+a_j(1leq ileq jleq cnt)$的形式,其中$a$是所有分治过程中点到重心的距离的集合的并。假设我们知道$l<ans<r$,那么我们可以通过双指针求出每个$a_i$和哪个范围的$a_j$相加在$(l,r)$之间,并统计处出这样的方案数$tot$。

    • 如果$tot$不大,那么可以将它们全部暴力提取出来,在排序后的数组里二分,从而最小化二分轮数。
    • 否则$tot$比较大,我们可以随机取一对这样的$(i,j)$,这不会导致期望二分轮数变大很多。
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef unsigned int U;
    typedef long long ll;
    typedef unsigned long long ull;
    const int N=25010,CNT=500005,M=CNT*17,P=1000000007,LIM=150000;
    int n,_,i,j,k,x,y,z;ll K;
    int root[N],g[N],v[N<<1],w[N<<1],nxt[N<<1],ok[N<<1],ed,son[N],f[N],now,all;
    int pool[CNT],cp,q[CNT<<1],cq,cnt,st[N<<1],en[N<<1],base[N<<1];
    int pl[CNT],pr[CNT];
    int tot,l[M],r[M],sum[M],val[M],p[N];ll sw[M],weight[N];
    int LA,LB,RA,RB,MA,MB,ANSA,ANSB;
    ll total;
    U SX=335634763,SY=873658265,SZ=192849106,SW=746126501;
    int ce;
    struct E{int l,r;E(){}E(int _l,int _r){l=_l,r=_r;}}e[LIM+5];
    inline ull xorshift128(){
      U t=SX^(SX<<11);
      SX=SY;
      SY=SZ;
      SZ=SW;
      return SW=SW^(SW>>19)^t^(t>>8);
    }
    inline ull myrand(){return (xorshift128()<<32)^xorshift128();}
    int ins(int x,int a,int b,int c){
      int y=++tot;
      val[y]=val[x]+1;
      sum[y]=(sum[x]+p[c])%P;
      sw[y]=sw[x]+weight[c];
      if(a==b)return y;
      int mid=(a+b)>>1;
      if(c<=mid)l[y]=ins(l[x],a,mid,c),r[y]=r[x];
      else l[y]=l[x],r[y]=ins(r[x],mid+1,b,c);
      return y;
    }
    inline int compare(int A,int B,int C,int D){
      if(sw[A]+sw[B]==sw[C]+sw[D])return 0;
      int a=1,b=n,mid;
      while(a<b){
        mid=(a+b)>>1;
        if(sw[r[A]]+sw[r[B]]==sw[r[C]]+sw[r[D]]){
          b=mid;
          A=l[A];
          B=l[B];
          C=l[C];
          D=l[D];
        }else{
          a=mid+1;
          A=r[A];
          B=r[B];
          C=r[C];
          D=r[D];
        }
      }
      return val[A]+val[B]<val[C]+val[D]?-1:1;
    }
    inline bool cmp(int x,int y){return compare(x,0,y,0)<0;}
    inline bool cmpe(const E&a,const E&b){return compare(a.l,a.r,b.l,b.r)<0;}
    inline void add(int x,int y,int z){v[++ed]=y;w[ed]=z;nxt[ed]=g[x];g[x]=ed;ok[ed]=1;}
    void findroot(int x,int y){
      son[x]=1;f[x]=0;
      for(int i=g[x];i;i=nxt[i])if(ok[i]&&v[i]!=y){
        findroot(v[i],x);
        son[x]+=son[v[i]];
        if(son[v[i]]>f[x])f[x]=son[v[i]];
      }
      if(all-son[x]>f[x])f[x]=all-son[x];
      if(f[x]<f[now])now=x;
    }
    void dfs1(int x,int y,int z){
      root[x]=z;
      pool[++cp]=z;
      for(int i=g[x];i;i=nxt[i])if(v[i]!=y&&ok[i])dfs1(v[i],x,ins(z,1,n,w[i]));
    }
    void dfs2(int x,int y){
      q[++cq]=root[x];
      for(int i=g[x];i;i=nxt[i])if(v[i]!=y&&ok[i])dfs2(v[i],x);
    }
    void solve(int x){
      int i;
      dfs1(x,0,0);
      base[++cnt]=1;
      st[cnt]=cq+1;
      dfs2(x,0);
      en[cnt]=cq;
      if(en[cnt]==st[cnt])cnt--,cq--;
      for(i=g[x];i;i=nxt[i])if(ok[i]){
        base[++cnt]=-1;
        st[cnt]=cq+1;
        dfs2(v[i],x);
        en[cnt]=cq;
        if(en[cnt]==st[cnt])cnt--,cq--;
      }
      for(i=g[x];i;i=nxt[i])if(ok[i]){
        ok[i^1]=0;
        f[0]=all=son[v[i]];
        findroot(v[i],now=0);
        solve(now);
      }
    }
    inline void dec(int l,int r){
      int i,j;
      for(i=l,j=r;i<=r;i++){
        while(j>i&&compare(MA,MB,q[i],q[j])<0)j--;
        if(j<=i)break;
        total-=j-i;
      }
    }
    inline void inc(int l,int r){
      int i,j;
      if(total>=K)return;
      for(i=l,j=r;i<=r;i++){
        while(j>i&&compare(MA,MB,q[i],q[j])<0)j--;
        if(j<=i)break;
        total+=j-i;
        if(total>=K)return;
      }
    }
    int main(){
      scanf("%d%lld",&n,&K);
      for(i=1;i<=n;i++)weight[i]=myrand();
      for(p[0]=i=1;i<=n;i++)p[i]=1LL*p[i-1]*n%P;
      for(ed=i=1;i<n;i++)scanf("%d%d%d",&x,&y,&z),add(x,y,z),add(y,x,z);
      f[0]=all=n;findroot(1,now=0);solve(now);
      sort(pool+1,pool+cp+1,cmp);
      for(i=1;i<=cp;i++)if(i==1||cmp(pool[i-1],pool[i]))pool[++_]=pool[i];
      cp=_;
      for(i=1;i<=cnt;i++)sort(q+st[i],q+en[i]+1,cmp);
      LA=LB=pool[1];
      RA=RB=ANSA=ANSB=pool[cp];
      while(1){
        ll have=0;
        for(i=1,j=cp+1,k=cp;i<=cp;i++){
          while(j-1>i&&compare(LA,LB,pool[i],pool[j-1])<0)j--;
          while(k>i&&compare(RA,RB,pool[i],pool[k])<=0)k--;
          j=max(j,i+1);
          pl[i]=j,pr[i]=k;
          if(j>i&&j<=k)have+=k-j+1;
        }
        if(have<=LIM){
          for(i=1;i<=cp;i++){
            j=pl[i],k=pr[i];
            if(j>i&&j<=k)for(x=j;x<=k;x++)e[++ce]=E(pool[i],pool[x]);
          }
          break;
        }
        have=myrand()%have+1;
        for(i=1;i<=cp;i++){
          j=pl[i],k=pr[i];
          if(j>i&&j<=k){
            if(k-j+1>=have){
              MA=pool[i];
              MB=pool[pl[i]+have-1];
              break;
            }
            have-=k-j+1;
          }
        }
        total=0;
        for(i=1;i<=cnt;i++)if(base[i]==-1)dec(st[i],en[i]);
        for(i=1;i<=cnt;i++)if(base[i]==1)inc(st[i],en[i]);
        if(total>=K){
          ANSA=MA;
          ANSB=MB;
          RA=MA;
          RB=MB;
        }else{
          LA=MA;
          LB=MB;
        }
      }
      if(ce>1)sort(e+1,e+ce+1,cmpe);
      int l=1,r=ce;
      while(l<=r){
        int mid=(l+r)>>1;
        MA=e[mid].l;
        MB=e[mid].r;
        total=0;
        for(i=1;i<=cnt;i++)if(base[i]==-1)dec(st[i],en[i]);
        for(i=1;i<=cnt;i++)if(base[i]==1)inc(st[i],en[i]);
        if(total>=K){
          ANSA=MA;
          ANSB=MB;
          r=mid-1;
        }else{
          l=mid+1;
        }
      }
      printf("%d",(sum[ANSA]+sum[ANSB])%P);
    }
    

      

  • 相关阅读:
    c语言基础学习10_文件操作02
    c语言_文件操作_FILE结构体小解释
    初识 Swift编程语言(中文版)
    Jquery滑动门实现
    【一步一步走(1)】远程桌面软件VNC的安装与配置
    并查集 路径压缩(具体解释)
    linux中操作java进程
    HDOJ 3944 DP?
    选择排序与冒泡排序
    UVa145 Gondwanaland Telecom
  • 原文地址:https://www.cnblogs.com/clrs97/p/12075650.html
Copyright © 2011-2022 走看看