Codeforces Beta Round #88

做CF被血虐。。。下次要扳回来!


AB题就pass吧,现在直接切入正题(A掉AB题我都花了1个小时啊,混蛋。。)

C

给你一个竞赛图,要你找一个三元环。

solution

竞赛图的意思就是 Aij≠Aji ,一直都忘记用这个性质,悲催。。

下面讲一个我翻别人代码翻到的最优美的方法:

维护一个序列B,这个序列对于任意 i<j 满足 A[B[i],B[j]]=1。

一开始序列为空,然后把1到N一次插入这个序列,比如现在插入i号元素:

1.找到一个最小的L,满足A[i,B[L]]=1;

2.找到一个最大的R,满足A[i,B[R]]=0;

3.如果L=R+1,就吧i插入到L,R之间,如果L≠R,那么我们就找到了一个环,因为L<R,所以A[B[L],B[R]]=A[B[R],i]=A[i,B[L]]=1。

#include <vector>
#include <cstdio>
using namespace std;

char a[5010][5010];

int main() {
  int n,i;
  scanf("%d",&n);
  for (i=0;i<n;i++) scanf("%s",a[i]);
  vector<int> b(1,0);
  for (i=1;i<n;i++) {
    int l=0,r=i-1;
    while (l<i && a[i][b[l]]=='0') l++;
    while (r>=0 && a[i][b[r]]=='1') r--;
    if (l==r+1) b.insert(b.begin()+l,i);
           else {printf("%d %d %d\n",b[l]+1,b[r]+1,i+1);break;};
  };
  if (i==n) printf("-1\n");
  return 0;
}




D

题意有点复杂。。。

类似于线段树,只不过不需要吧节点建出来。

每个节点的数一定是一个等差数列,所以只要在递归的过程中记下第一项就可以了。

#include <cstdio>
#include <algorithm>
using namespace std;

long long n,m,mo,l,r,u,v;

long long cal(long long a,long long b,long long c) {
  a-=b;
  if (a<0) a=-1; else a=a>>c;
  long long B=b+a*(1ll<<c);
  long long s;
  if ((++a)&1) s=((B+b)/2%mo)*(a%mo);
          else s=((B+b)%mo)*(a/2%mo);
  return s%mo;
}

long long cal(long long ll,long long rr,long long c,int d) {
  if (u>n) return 0;
  long long ans=0;
  if (l>ll || r<rr) {
    long long m=(ll+rr)>>1;
    if (l<=m) ans+=cal(ll,m,c,d+1);
    if (r>m) ans+=cal(m+1,rr,c+(1ll<<d),d+1);
    if (ans>=mo) ans-=mo;
  } else {
    ans=cal(v,c,d)-cal(u-1,c,d);
    if (ans<0) ans+=mo;
  };
  return ans;
}

int main() {
  scanf("%I64d%I64d%I64d",&n,&m,&mo);
  while (m--) {
    scanf("%I64d%I64d%I64d%I64d",&l,&r,&u,&v);
    v=min(v,n);
    long long ans=cal(1,n,1,0);
    printf("%I64d\n",ans%mo);
  };
  return 0;
}

E

给你一颗树+一条边,每条边有01两种状态,要求支持两种操作:

1.将从U到V的最短路上的边的01状态取反;

2.询问只考虑状态为1的边有多少个联通快;


如果没有那多出的一条边的话,也就是要求询问树中有多少条状态为0的边S,答案就是S+1;

加了一条边也差不多,答案是S,不过要特殊考虑树中的那一个环,如果这个环上的边状态全为1的话还要把答案额外加一。

因此我们直接删掉一条边做动态树就可以了。

第一次用C++写动态树,写了2个多小时,oh NO。。

另外疑问就是为什么CF不能用memset?老是编译错误?


#include <cstdio>
#include <algorithm>
#define N 100005
#define update(i) x[i]=x[l[i]]+x[r[i]]+a[i],y[i]=y[l[i]]+y[r[i]]+1-a[i];
using namespace std;

int t,T,L,mb,mdb,ans,d[N],z[N],g[N],o[N],fa[N],FA[N],p[N*2],next[N*2];

struct d_tree {
  int g,t,l[N],r[N],z[N],a[N],x[N],y[N],fa[N],root[N];
  void put(int i,int j) {if (i&&j) z[i]=!z[i],a[i]=!a[i],swap(x[i],y[i]);}
  void lazy(int i) {put(l[i],z[i]),put(r[i],z[i]),z[i]=0;}

  void rt(int i,int j) {
    if (l[i]==j) l[i]=r[j],fa[r[j]]=i,r[j]=i;
            else r[i]=l[j],fa[l[j]]=i,l[j]=i;
    root[j]=root[i],root[i]=0;
    if (l[fa[i]]==i) l[fa[i]]=j; else
    if (r[fa[i]]==i) r[fa[i]]=j;
    fa[j]=fa[i],fa[i]=j;
    update(i);update(j);
  }

  void splay(int i,int j) {
    if (root[i]) lazy(i);
    while (!root[i]) {
      lazy(fa[i]);
      lazy(i);
      rt(fa[i],i);
    };
    if (j) {
      root[g=r[i]]=1;
      r[i]=0;
      update(i);
    }
  }

  void access(int i) {
    splay(t=i,1);
    while (fa[i]) {
      splay(t=fa[i],1);
      r[t]=1,rt(t,i);
    };
  }

  void cover(int i) {
    access(i);
    splay(L,0);
    ans+=y[r[L]]-x[r[L]];
    put(r[L],1);
  }

} TREE;

void link(int a,int b) {next[++t]=d[a],d[a]=t,p[t]=b;}
int find(int i) {return (FA[i]==i)?i:FA[i]=find(FA[i]);}

void dfs(int i,int h) {
  z[++t]=i,o[i]=t;
  for (int k=d[i],j=p[k];k;k=next[k],j=p[k]) if ((h^k)!=1) {
    if (o[j]) {
      mb=k;
      for (int I=o[j];I<=t;I++) g[++T]=z[I];
    } else dfs(j,k);
  };
  t--;
}

void dfs2(int i,int h) {
  for (int k=d[i],j=p[k];k;k=next[k],j=p[k]) if ((h^k)!=1 && ((k^mb)>1)) {
    fa[j]=i;dfs2(j,k);
  };
}

int main() {
  int n,m,a,b,mdb=0;
  t=1;
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++) {
    scanf("%d%d",&a,&b);
    link(a,b);link(b,a);
  };
  t=0;
  dfs(1,0);
  dfs2(g[1],0);
  for (int i=0;i<N;i++) o[i]=0;
  for (int i=1;i<=T;i++) o[g[i]]=i;
  for (int i=1;i<=n;i++) FA[i]=(o[i])?i:fa[i];
  for (int i=1;i<=n;i++) find(i);
  for (int i=1;i<=n;i++) {
    TREE.fa[i]=fa[i];
    TREE.y[i]=TREE.root[i]=1;
  };
  g[0]=g[T],L=g[T+1]=g[1];
  while (m--) {
    scanf("%d%d",&a,&b);
    int Fa=FA[a],Fb=FA[b];
    int d1=abs(o[Fa]-o[Fb]);
    int g1,g2;
    if (o[Fa]<o[Fb]) g1=g[o[Fa]+1],g2=g[o[Fa]-1];
                else g1=g[o[Fa]-1],g2=g[o[Fa]+1];
    TREE.cover(a);
    TREE.cover(b);
    if (d1>T-d1 || ((d1==T-d1) && g1>g2)) {
      mdb=!mdb;
      if (mdb) ans++; else ans--;
      TREE.cover(g[T]);
    };
    TREE.access(g[T]);
    int al=TREE.x[g[T]]+mdb==T;
    printf("%d\n",n-ans+al);
  };
  return 0;
}



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值