【ctsc2012】solution

35 篇文章 0 订阅
24 篇文章 0 订阅

梭哈游戏

题意:给定一系列比较规则,并给出A,B当前手牌,讨论牌抽完后,A能赢B的概率

比较规则比较复杂,可以先判牌形,再将花色大小压成一个数,排序后我们就只要统计,在某副手牌之前的手牌中,有多少个与其无重复手牌,判重工作可以由容斥原理和hash表完成,对于每副手牌需要2^5判断复杂度为o(手牌数*2^5),手牌数最多只有c(51,5)5s时限还算充裕,对于hash,我们考虑用53进制压位,因为搜索过程中不可能出现完全相同的两副手牌,所以最大只有53^4,直接用数组存,连挂链都不需要

#include <cstdio>
#include <cstdlib>
#include <cstring>
#ifdef WIN32
#define fmt64 "%I64d"
#else
#define fmt64 "%lld"
#endif
struct state{int a[6],b[6],e,u1,u2;}st[2600000];
int a[6],b[6],v[15][5],u[2600000],o[5],d[10],pi,qi,debug;
int n,m,tot,xx[15],yy[5],g[8000000],p[10];
long long ans1,ans2,f[32];
void sort(state &u)
{
  int i,j,e;
  for (i=1;i<=4;i++)
    for (j=i+1;j<=5;j++)
      if (u.a[i]<u.a[j] || (u.a[i]==u.a[j] && u.b[i]<u.b[j])) 
	e=u.a[i],u.a[i]=u.a[j],u.a[j]=e,
	  e=u.b[i],u.b[i]=u.b[j],u.b[j]=e;
}
void yasuo(state &x)
{
  int i,k;
  for (i=1,k=6;i<=5;i++,k--) 
    x.u2+=x.a[i]*d[k];
  x.u2+=x.b[1];
}
int tonghuashun(state &x)
{
  int i;
  if (o[x.b[1]]!=5) return 0;
  for (i=2;i<=5;i++)
    if (x.a[i]!=x.a[i-1]-1) return 0;
  x.u1=9;x.u2+=x.a[1]*d[6],x.u2+=x.b[1];
  return 1;
}
int sitiao(state &x)
{
  if (u[x.a[1]]!=4 && u[x.a[2]]!=4) return 0;
  x.u1=8;
  if (u[x.a[1]]==4) x.u2+=x.a[1]*d[6];
  else x.u2+=x.a[2]*d[6];
  return 1;
}
int mantanghong(state &x)
{
  if (!((u[x.a[1]]==3 && u[x.a[4]]==2) || (u[x.a[1]]==2 && u[x.a[3]]==3))) return 0;
  x.u1=7;
  if (u[x.a[1]]==3 && u[x.a[4]]==2) x.u2+=x.a[1]*d[6];
  else x.u2+=x.a[3]*d[6];
  return 1;
}
int tonghua(state &x)
{
  if (o[x.b[1]]!=5) return 0;
  x.u1=6;yasuo(x);
  return 1;
}
int shunzi(state &x)
{
  int i;
  for (i=2;i<=5;i++)
    if (x.a[i]!=x.a[i-1]-1) return 0;
  x.u1=5;
  x.u2+=x.a[1]*d[6],x.u2+=x.b[1];
  return 1;
}
int santiao(state &x)
{
  if (!(u[x.a[1]]==3 || u[x.a[2]]==3 || u[x.a[3]]==3)) return 0;
  x.u1=4;
  if (u[x.a[1]]==3) x.u2+=x.a[1]*d[6];
  else if (u[x.a[2]]==3) x.u2+=x.a[2]*d[6];
  else x.u2+=x.a[3]*d[6];
  return 1;
}
int liangdui(state &x)
{
  if (!(u[x.a[2]]==2 && u[x.a[4]]==2)) return 0;
  x.u1=3;
  if (u[x.a[1]]==1) x.u2+=x.a[2]*d[6]+x.a[4]*d[5]+x.a[1]*d[4]+x.b[2];
  else if (u[x.a[3]]==1) x.u2+=x.a[1]*d[6]+x.a[4]*d[5]+x.a[3]*d[4]+x.b[1];
  else x.u2+=x.a[1]*d[6]+x.a[3]*d[5]+x.a[5]*d[4]+x.b[1];
  return 1;
}
int yidui(state &x)
{
  int i,j,k;
  for (i=2;i<=5;i++)
    if (x.a[i]==x.a[i-1]) break;
  if (i>5) return 0;
  x.u1=2;
  x.u2+=x.a[i-1]*d[6]+x.b[i-1];
  for (j=1,k=5;j<=5;j++) {
    if (j==i || j==i-1) continue;
    x.u2+=x.a[j]*d[k--];
  }
  return 1;
}
void check(int e)
{
  int i;
  ++tot;st[tot].e=e;
  for (i=1;i<=5;i++) st[tot].a[i]=a[i],st[tot].b[i]=b[i];
  sort(st[tot]);
  if (tonghuashun(st[tot])) return ;
  if (shunzi(st[tot])) return ;
  for (i=1;i<=5;i++) st[tot].a[i]=(st[tot].a[i]==1) ? 14 : st[tot].a[i];
  sort(st[tot]);
  if (tonghuashun(st[tot])) return ;
  if (sitiao(st[tot])) return ;
  if (mantanghong(st[tot])) return ;
  if (tonghua(st[tot])) return ;
  if (shunzi(st[tot])) return ;
  if (santiao(st[tot])) return ;
  if (liangdui(st[tot])) return ;
  if (yidui(st[tot])) return ;
  st[tot].u1=1,yasuo(st[tot]);
}
void dfs(int x,int e,int lim1,int lim2)
{
  if (x>5) {
    check(e);return ;
  }
  int i,j;
  for (i=lim1;i<=13;i++) {
    for (j=(i!=lim1) ? 1 : lim2;j<=4;j++)
      if (!v[i][j]) {
	a[x]=i,b[x]=j;
	v[i][j]=1;
	u[i]++,o[j]++,u[14]=(i==1) ? u[14]+1 : u[14];
	dfs(x+1,e,i,j);
	a[x]=b[x]=0;
	v[i][j]=0;
	u[i]--,o[j]--,u[14]=(i==1) ? u[14]-1 : u[14];
      }
  }
}
long long gcd(long long a,long long b)
{
  long long t;
  for (;a%b;) {
    t=a%b;
    a=b;
    b=t;
  }
  return b;
}
int cmp(const void *i,const void *j) 
{
    pi=*(int *)i,qi=*(int *)j;
    if (st[pi].u1!=st[qi].u1) return st[pi].u1-st[qi].u1;
    return st[pi].u2-st[qi].u2;
}
void init()
{
    int i,j,x,y,k,e,sum;
  scanf("%d\n",&n);
  for (i=2,d[1]=15;i<=6;i++) d[i]=d[i-1]*15;
  for (i=1;i<=n;i++) {
    scanf("%d%d\n",&x,&y);y=4-y+1;
    a[i]=x,b[i]=y;
    v[x][y]=1;
    u[x]++,o[y]++,u[14]=(x==1) ? u[14]+1 : u[14];
  }
  for (i=1;i<=n-1;i++) scanf("%d%d\n",&xx[i],&yy[i]),v[xx[i]][4-yy[i]+1]=1;
  dfs(n+1,0,1,1);
  memset(a,0,sizeof(a));memset(b,0,sizeof(b));memset(o,0,sizeof(o));
  for (i=1;i<=15;i++) u[i]=0;
  for (i=1;i<=n-1;i++) {
    x=xx[i],y=4-yy[i]+1;
    a[i]=x,b[i]=y;
    v[x][y]=1;
    u[x]++,o[y]++,u[14]=(x==1) ? u[14]+1 : u[14];
  }
  dfs(n,1,1,1);
  for (ans2=0,i=1;i<=tot;i++) {
    u[i]=i;
    if (st[i].e) continue;
    x=47-n+1,y=5-n+1;
    for (sum=1,j=x;j>=x-y+1;j--) sum*=j;
    for (j=1;j<=y;j++) sum/=j;
    ans2+=sum;
  }
  qsort(u+1,tot,sizeof(u[1]),cmp);
  for (i=1,sum=0;i<=tot;i++) {
    if (st[u[i]].e==0) f[0]+=sum;else sum+=1;
    if (st[u[i]].a[1]!=14) continue;
    for (j=1;j<=5;j++)
      st[u[i]].a[j]=(st[u[i]].a[j]==14) ? 1 : st[u[i]].a[j];
    sort(st[u[i]]);
  }
  for (i=2,p[1]=1;i<=5;i++) p[i]=p[i-1]*53;
  for (i=1;i<=tot;i++) {
      for (j=1;j<=30;j++) {
	  for (k=1,e=0,debug=0;k<=5;k++)
  	      if ((j>>(k-1))&1 && !v[st[u[i]].a[k]][st[u[i]].b[k]]) 
		  debug+=((st[u[i]].a[k]-1)*4+st[u[i]].b[k])*p[++e];else if (j>>(k-1)&1) break;
  	  if (k<=5) continue;
	  if (!st[u[i]].e) f[j]+=g[debug];
  	  else g[debug]++;
      }
  }
  for (ans1=0,i=0;i<=30;i++) {
      if (!f[i]) continue;
      for (e=1,k=i;k;k>>=1) if (k&1) e*=-1;
      ans1+=f[i]*e;
  }
  long long gc=gcd(ans1,ans2);
  printf(fmt64"/"fmt64,ans1/gc,ans2/gc);
}
int main()
{
  freopen("showhand.in","r",stdin);
  freopen("showhand.out","w",stdout);
   init();
  return 0;
}


电阻网络

基尔霍夫第一定律:在任一瞬时,流向某一结点的电流之和恒等于由该结点流出的电流之和

结合欧姆定律,对于树的每一个叶子,都可以列出方程,用其父亲表示成为kx+b,而其父亲又可以继续向上推一层,最终汇至根节点,对于根节点列出方程,则可以求出根节点的电势,如果向下迭代,就可以求出任意点的电势,而添加电压也只不过是修改系数或常数,记录一下delt就可以o(树高)的维护,注意到树高顶多只有50,该算法速度是很快的(由于我没考虑最坏情况,所以我是每次都将到根路径上的表达式重新计算,如果叶子很多是会被卡的,但是实际上好像没有此类数据)

程序不在机房,暂缺


最短路(提答题程序较多,且多数被改造成其他用途,所以不提供)

给定一个节点 1 和节点 N 连通的正权无向图 G,请你删除不超过 K 条边,使得节点 1 和节点 N 仍然连通的同时,且这两点之间的最短路尽可能长。

暂未实现,主要是应用状压dp(连通块较小或行数较小),暴搜删最短路的边,网格图手玩,最后一个点找哈密尔顿路径(怎么找?)


熟悉的文章

给定字符串集,和一个要询问的字符串,规定一个子串合法为同时出现在给定字符串集和询问字符串中并且大于常数L,先要将询问字符串划分为若干段,使合法串长度大于90%,要你确定一个最大的L

不考虑L的话,如果一个串,那么他的所有子串均合法,所以从一个点向前延伸的合法子串结束位置必定是连续的,所以我们只要能求出最长的即可,这一步可以用后缀自动机完成。对于L显然是满足可行性的,因此可以二分,用划分的最大长度判断,这一步可以由dp实现,这里的dp值是单增的,可行划分i-L是递增的,每个点向前延伸的最长区间也是递增的,因此可以用单调队列维护,时间复杂度o(n+nlogn)

#include <cstdio>

#include <cstdlib>

#include <cstring>

int st[2000000],len[3000000],rt[3000000],f[2000000],b[3000000],last,n,m,tot,next[3000000][3],l,s1;

char ch[2000000],s[2000000];

inline int check(int lim)

{

    int i,h,r,j;

    for (i=0;i<=lim-1;i++) f[i]=0;

    r=0,h=1;

    for (i=lim,j=0;i<=l;i++) {

	for (;i-j>=lim;j++) {

	    for (;h<=r && f[st[r]]-st[r]<=f[j]-j;r--) ;

	    st[++r]=j;

	}

	for (;h<=r && i-st[h]>b[i];h++) ;

	if (h<=r) f[i]=f[st[h]]+i-st[h];else f[i]=0;

	if (f[i]<f[i-1]) f[i]=f[i-1];

    }

    return f[l]-l*0.9>-1e-6;

}

void init()

{

    int i,j,x,y,ne,ll,rr,mid,sum;

    scanf("%d%d\n",&n,&m);

    for (i=1;i<=m;i++) {

	scanf("%s\n",ch+1);

	l=strlen(ch+1);

	for (j=1;j<=l;j++) s[++tot]=ch[j]-'0';

	s[++tot]=2;

    }

    for (last=0,i=1;i<=tot;i++) {

	ne=s[i];

	for (x=last,last=++s1;x && !next[x][ne];x=rt[x]) next[x][ne]=s1;

	y=next[x][ne],len[s1]=i;

	if (!y) {next[x][ne]=s1;continue;}

	else {

	    if (len[x]+1==len[y]) rt[s1]=y;

	    else {

		len[++s1]=len[x]+1;

		for (j=0;j<=2;j++) next[s1][j]=next[y][j];

		rt[last]=s1,rt[s1]=rt[y],rt[y]=s1;

		for (;x && next[x][ne]==y;x=rt[x]) next[x][ne]=s1;

		if (next[x][ne]==y) next[x][ne]=s1;

	    }

	}

    }

    for (i=1;i<=n;i++) {

	scanf("%s\n",ch+1);

	l=strlen(ch+1);

	for (sum=0,x=0,j=1;j<=l;j++) {

	    ne=ch[j]-'0';

	    for (;x && !next[x][ne];sum=len[x=rt[x]]) ;

	    if (next[x][ne]) sum++,x=next[x][ne];

	    b[j]=sum;

	}

	for (ll=1,rr=l;ll<=rr;) {

	    mid=(ll+rr)>>1;

	    if (check(mid)) ll=mid+1;else rr=mid-1;

	}

	printf("%d\n",ll-1);

    }

}

int main()

{

    freopen("cheat.in","r",stdin);

    freopen("cheat.out","w",stdout);

     init();

    return 0;

}



极点统计:给定两个点集N,M,我们要求M中有多少个点i满足N与M其他任意点j形成的凸包都不包含i

考虑将一个点添入凸包,那么更新的区域为点到凸包切线所能围出的区域,可以用二分快速得出切线,因为凸包是循环序,所以要找到一个可见点和一个不可见点将凸包划分为两部分,再在两个凸包分别二分转折点,具体做法是,我们先确定一个凸包内部的点G,预处理所有点关于G的极角,对于我们要找切线的点i,可以二分出关于G极角离i最近的点j,那么j一定被i看到,接着将i对称找出i‘,在对i’找出j‘,j与j’即可将凸包分为两个部分,二分极角变换量突变点即可找出切点

任意两切线确定一个点,如果点i被点k包含,那么从极角理解就是k的极角区间包含i的极角区间,比较烦的是跨界的情况,暂时还没想好怎么维护

由于fhq给的数据数据不合法,暂时还不打算写


统计学家:统计二维逆序对

点1~5,总有一维较小,用c(5,2)枚举列和莫队算法结合,跑几分钟基本就出来了(点4至今因不明原因未过)

点6:全是逆序对,等差数列求和公式直接上(代码超短)

点7:0\1交错,推起来比较麻烦,还没有推

点8~9:询问总是1~n行,设w[i][j]为i上的点关于j的逆序对,f[i][j]表示i~j列的答案,f[i][j]=f[i][j-1]+f[i+1][j]-f[i+1][j-1]+w[i][j]

点10:直接暴力二维树状数组统计


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值