cqoi2016

cqoi2016

不同的最小割
做法同zjoi2011最小割,题解略。

#include
#include
#include
#include
#include
#include
#include
#define inf 1050000000 
#define N 855
#define M 17005
using namespace std;
int n,m,S,T,tot,po[N],lx[N],rx[N],ans[N][N];
int k=1,la[N],ff[M],q[N],dep[N],flag[N],out[N*N];
struct node{int a,b,c;}map[M];

void add(int a,int b,int c)
{
  map[++k]=(node){a,b,c};ff[k]=la[a];la[a]=k;
  map[++k]=(node){b,a,c};ff[k]=la[b];la[b]=k;
}

int dfs(int x,int flow)
{
  if(x==T)return flow;
  int res=0,tmp;
  for(int a=la[x];a&&flow>0;a=ff[a])
    if(dep[map[a].b]==dep[x]+1&&map[a].c)
    {
      tmp=dfs(map[a].b,min(flow,map[a].c));
      map[a].c-=tmp;map[a^1].c+=tmp;res+=tmp;flow-=tmp;
    }
  if(res==0)dep[x]=-1;
  return res;
}

bool bfs()
{
  memset(dep,0,sizeof(dep));
  int l=1,r=2;q[1]=S;dep[S]=1;
  while(lint x=q[l];l++;
    for(int a=la[x];a;a=ff[a])
      if(!dep[map[a].b]&&map[a].c)
        q[r]=map[a].b,dep[q[r]]=dep[x]+1,r++;
  }
  return dep[T];
}

void dfs(int x)
{
  flag[x]=1;
  for(int a=la[x];a;a=ff[a])
    if(map[a].c&&!flag[map[a].b])dfs(map[a].b);
}

void solve(int l,int r)
{
  if(l==r)return;
  int res=0;S=po[l];T=po[r];
  for(int i=2;i<=k;i+=2)
    map[i].c=map[i^1].c=(map[i].c+map[i^1].c>>1);
  while(bfs())res+=dfs(S,inf);
  memset(flag,0,sizeof(flag));dfs(S);
  for(int i=1;i<=n;i++)if(flag[i])
    for(int j=1;j<=n;j++)if(!flag[j])
      ans[i][j]=ans[j][i]=min(ans[i][j],res);
  int l1=0,l2=0;
  for(int i=l;i<=r;i++)
  {
    if(flag[po[i]])lx[++l1]=po[i];
    else rx[++l2]=po[i];
  }
  for(int i=l,j=1;j<=l1;i++,j++)po[i]=lx[j];
  for(int i=l+l1,j=1;j<=l2;i++,j++)po[i]=rx[j];
  solve(l,l+l1-1);solve(l+l1,r);
}

int main()
{
  int a,b,c,x,res;
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++)po[i]=i;
  for(int i=1;i<=m;i++)
    scanf("%d%d%d",&a,&b,&c),add(a,b,c);
  memset(ans,127,sizeof(ans));
  solve(1,n);
  for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)out[++tot]=ans[i][j];
  sort(out+1,out+tot+1);
  printf("%d\n",unique(out+1,out+tot+1)-out-1); 
  return 0;
}

K远点对
裸的kd树维护最远点对。

#include 
#include 
#include 
#include 
#include 
#include 
#include
#include 
#define inf 2100000000
#define ll long long
#define N 100010
using namespace std;
int n,rt,k,D,cnt;
struct info{
  int c[2];
  bool operator<(const info &x)const{
    return c[D]1]1]);
  }
}s[N];
struct node{int lc,rc,minx,maxx,miny,maxy,x,y;}t[N];
priority_queueq;

class Kevin_Durant_Tree
{
  void update(int x)
  {
    int lc=t[x].lc,rc=t[x].rc;
    t[x].minx=min(t[x].x,min(t[lc].minx,t[rc].minx));
    t[x].miny=min(t[x].y,min(t[lc].miny,t[rc].miny));
    t[x].maxx=max(t[x].x,max(t[lc].maxx,t[rc].maxx));
    t[x].maxy=max(t[x].y,max(t[lc].maxy,t[rc].maxy));
  }
  ll pf(int x){return (ll)x*x;}
  ll get(int x1,int y1,int x2,int y2)
  {
    return pf(x1-x2)+pf(y1-y2); 
  }
  ll cal(int x,int X,int Y)
  {
    if(!x)return 0;
    ll res=0;
    res=max(res,get(t[x].minx,t[x].miny,X,Y));
    res=max(res,get(t[x].minx,t[x].maxy,X,Y));
    res=max(res,get(t[x].maxx,t[x].miny,X,Y));
    res=max(res,get(t[x].maxx,t[x].maxy,X,Y));
    return res;
  }
  public:
  void build(int &x,int l,int r,int inv)
  { 
    if(l>r)return;x=++cnt;
    int mid=l+r>>1;
    D=inv;nth_element(s+l,s+mid,s+r+1);
    t[x].minx=t[x].maxx=t[x].x=s[mid].c[0];
    t[x].miny=t[x].maxy=t[x].y=s[mid].c[1];
    build(t[x].lc,l,mid-1,inv^1);
    build(t[x].rc,mid+1,r,inv^1);
    update(x);  
  }
  void qry(int x,int X,int Y)
  {
    if(!x)return;
    int lc=t[x].lc,rc=t[x].rc;
    ll ls=cal(lc,X,Y),rs=cal(rc,X,Y);
    ll tmp=get(t[x].x,t[x].y,X,Y);
    if(tmp>-q.top())q.pop(),q.push(-tmp);
    if(ls>rs)
    {
      if(ls>-q.top())qry(lc,X,Y);
      if(rs>-q.top())qry(rc,X,Y);
    }
    else
    {
      if(rs>-q.top())qry(rc,X,Y);
      if(ls>-q.top())qry(lc,X,Y); 
    }
  }
  void clear()
  {
    t[0].minx=t[0].miny=inf;
    t[0].maxx=t[0].maxy=0;
  }
}T;

int main()
{
  scanf("%d%d",&n,&k);
  for(int i=1;i<=n;i++)
    scanf("%d%d",&s[i].c[0],&s[i].c[1]);
  T.clear();T.build(rt,1,n,0);
  for(int i=1;i<=2*k;i++)q.push(0);
  for(int i=1;i<=n;i++)
    T.qry(rt,s[i].c[0],s[i].c[1]);
  printf("%lld\n",-q.top());
  return 0;
}

手机号码
裸的数位dp,f[x][A][B][C][D][E][F]分别表示前x位,上一位为A,连续B个相同,是否满足连续超过3个相同,是否有4,是否有8,是否等于上界。
写成记忆化搜索代码会好写很多。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define ll long long
#define N 13
using namespace std;
int flag[N][N][4][2][2][2][2],f[N][N][4][2][2][2][2],p[N];ll l,r,s[N];

int dfs(int x,int A,int B,int C,int D,int E,ll num)
{
  if((num>s[x+1])||(D&&E)||(x<0&&!C))return 0;
  if(x<0)return 1;
  int F=(num==s[x+1]);
  if(flag[x][A][B][C][D][E][F])return f[x][A][B][C][D][E][F];
  flag[x][A][B][C][D][E][F]=1;ll tmp=0;
  for(int i=0;i<=9;i++)
  {
    if(i==A)tmp+=dfs(x-1,i,min(B+1,3),C|(B>=2),D|(i==4),E|(i==8),num*10+i);
    else tmp+=dfs(x-1,i,1,C,D|(i==4),E|(i==8),num*10+i);
  }
  return f[x][A][B][C][D][E][F]=tmp; 
}

ll work(ll x)
{
  ll res=0,t=x;
  memset(f,0,sizeof(f));
  memset(flag,0,sizeof(flag));
  for(int i=0;i<=10;i++)p[i]=t%10,t/=10;
  for(int i=10;i>=0;i--)s[i]=s[i+1]*10+p[i];
  for(int i=1;i<=9;i++)
    res+=dfs(9,i,1,0,i==4,i==8,i);
  return res;
}

int main()
{
  scanf("%lld%lld",&l,&r);
  printf("%lld\n",work(r)-work(l-1));
  return 0;
}

密钥破解
模拟+泼辣肉。

#include
#include
#include
#include
#include
#include
#include
#define ll unsigned long long
using namespace std;
ll e,N,c,n,d,p,q,r,x,y,num;

ll add(ll a,ll b,ll mod)
{return (a+=b)>=mod?a-mod:a;}

ll mul(ll a,ll b,ll n)
{
  ll res=0;a%=n;b%=n;
  while(b)
  {
    if(b&1)res=add(res,a,n);
    a=(a<<1)%n;b=b>>1;
  }
  return res;
}

ll Pow(ll a,ll b,ll mod)
{
  ll res=1;
  while(b)
  {
    if(b&1)res=mul(res,a,mod);
    a=mul(a,a,mod);b>>=1;
  }
  return res;
}

ll gcd(ll a,ll b)
{
  if(!b)return a;
  return gcd(b,a%b);
}

void exgcd(ll a,ll b)
{
  if(b==0){x=1;y=0;return;}
  exgcd(b,a%b);ll t=x;x=y;y=t-a/b*y;
}

ll Abs(ll x){return x>0?x:-x;}

ll Pollard_rho(ll n,ll c)  
{  
  ll i=1,k=2,x,y,d,p;
  x=rand()%(n-1)+1;y=x;
  while(1)
  {
    i++;x=add(mul(x,x,n),c,n);
    if(y==x)return n;
    p=Abs(x-y);d=gcd(p,n);
    if(d!=1&&d!=n)return d;
    if(i==k)y=x,k+=k;
  }
}

int main()
{
  srand(666072);
  scanf("%lld%lld%lld",&e,&N,&c);
  if(N<=1000000)num=072;
  else num=666;
  p=Pollard_rho(N,num);
  q=N/p;r=(p-1)*(q-1);
  exgcd(e,r);d=(x+r)%r;
  n=Pow(c,d,N);
  printf("%lld% lld\n",d,n);
  return 0;
}

路由表
语文题。建chuai树,每次查询等价于维护一段时间递增序列。

#include
#include
#include
#include
#include
#include
#include
#define N 1000010
using namespace std;
int n,cnt,tot,ch[N][2],pos[N],q[N],str[50];
char s[20];

class chuai
{
  public:
  int cal(int top,int l)
  {
    int res=0;
    for(int i=1;i<=top;i++)res+=(q[i]>=l);
    return res;
  }
  void insert(int *s,int len,int ti)
  {
    int x=0;
    for(int i=1;i<=len;i++)
    {
      if(!ch[x][s[i]])ch[x][s[i]]=++cnt;
      x=ch[x][s[i]];
    }
    pos[x]=ti;
  }
  int qry(int *s,int l,int r,int len)
  {
    int x=0,top=0;
    for(int i=1;i<=len;i++)
    {
      x=ch[x][s[i]];
      if(!x)return cal(top,l);
      if(pos[x]&&pos[x]<=r)
      {
        while(q[top]>pos[x]&&top)top--;
        q[++top]=pos[x];
      }
    }
    return cal(top,l);
  }
}T;

int main()
{
  char ch,cyc,wbs,lxe,yp;
  int l,r,num,len,A,B,C,D;
  scanf("%d",&n);
  while(n--)
  {
    scanf(" %c%d %c%d %c%d %c%d",&ch,&A,&wbs,&B,&cyc,&C,&lxe,&D);
    if(ch=='A')
    {
      scanf(" %c%d",&yp,&len);
      num=A;num=num*256+B;
      num=num*256+C;num=num*256+D; 
      for(int i=1,j=31;i<=len;i++,j--)str[i]=((num>>j)&1);
      T.insert(str,len,++tot);
    }
    else
    {
      scanf("%d%d",&l,&r);
      len=32;num=A;num=num*256+B;
      num=num*256+C;num=num*256+D;
      for(int i=1,j=31;i<=len;i++,j--)str[i]=((num>>j)&1);
      printf("%d\n",T.qry(str,l,r,len));
    }
  }
  return 0;
}

伪光滑数
终于有一道有点意思的题目。
考虑只要求最大,建立dp,f[i][j]表示前i小额质数选j个的最大值。
维护一下前缀和即可。
f[i][j]=g[i-1][j-k]*num;
g[i][j]=g[i-1][j]+f[i][j];
要求K大,就把每个状态改成一个堆,再把所有状态套进一个大堆。
跑K次,每次取出一个节点换次大。
乘法等价于在堆上打标机,加法等价于合并两个堆。
拿可持久化可并队维护即可。
感觉这种堆套堆得方法还是很经典的。
cyc大爷的做法,二分答案暴搜,直接A了(%%%)。
lxe大爷的做法,对于每个状态维护每个质数使用的个数,然后对于每个状态只对最后两个数字进行转移。也A了(%%%)。lxe大爷似乎用奇怪的办法拆解了我的这个dp,然后码量少了不少。

#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define N 18000000
#define MAX 127
using namespace std;
int k,D,cnt=1,f[MAX][65],g[MAX][65];
int tot,check[MAX],prime[MAX];ll n;
struct node{int lc,rc,dis;ll num,tag;}t[N];
struct info{
  int x,y;ll num;
  bool operator<(const info &p)
  const{return numq;

class heap
{
  void pushdown(int x)
  {
    if(t[x].tag==1)return;
    int lc=t[x].lc,rc=t[x].rc;
    if(lc)
    {
      t[++cnt]=t[lc];t[x].lc=cnt;
      t[cnt].num*=t[x].tag;
      t[cnt].tag*=t[x].tag;
    }
    if(rc)
    {
      t[++cnt]=t[rc];t[x].rc=cnt;
      t[cnt].num*=t[x].tag;
      t[cnt].tag*=t[x].tag;
    }
    t[x].tag=1;
  }
  public:
  int merge(int a,int b)
  {
    if(!a)return b;if(!b)return a;
    if(t[a].numint x=++cnt;t[x]=t[a];pushdown(x);
    t[x].rc=merge(t[x].rc,b);
    if(t[t[x].lc].dis1;
    return x;
  }
  int mul(int x,ll y)
  {
    t[++cnt]=t[x];t[cnt].tag*=y;t[cnt].num*=y;
    return cnt;
  }
  int add(int a,int b){return merge(a,b);}
  int erase(int x)
  {
    pushdown(x);
    return merge(t[x].lc,t[x].rc);
  }
}T;

int main()
{
  scanf("%lld%d",&n,&k);D=log2(n)+1;
  for(int i=2;i<=MAX;i++)
  {
    if(check[i])continue;prime[++tot]=i;
    for(int j=i;j<=MAX;j+=i)check[j]=1;
  }
  for(int i=0;i<=D;i++)f[0][i]=g[0][i]=1;
  t[1].num=t[1].tag=t[1].dis=1;
  for(int i=1;i<=tot;i++)
  {
    ll ss=1;g[i][0]=g[i-1][0];
    for(int j=1;;j++)
    {
      ss*=prime[i];
      if(ss<=0||ss>n)break;
      ll num=1;
      for(int k=1;k<=j;k++)
      {
        num*=prime[i];
        f[i][j]=T.add(f[i][j],T.mul(g[i-1][j-k],num));
      }
      q.push((info){i,j,t[f[i][j]].num});
      g[i][j]=T.add(g[i-1][j],f[i][j]);
    }
  }
  ll pre=0;
  for(int i=1;iif(x.num==pre)i--;pre=x.num;
    f[x.x][x.y]=T.erase(f[x.x][x.y]);
    q.push((info){x.x,x.y,t[f[x.x][x.y]].num});
  }
  printf("%lld\n",q.top().num);
  return 0;
}

你可能感兴趣的:(题解,套题总结)