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");
}

  

posted @ 2022-10-16 17:26  Claris  阅读(157)  评论(0编辑  收藏  举报