2017ccpc杭州现场赛

2017ccpc杭州现场赛

这场状态还行,过了8个题,不过罚时有点大,大概是少一题的去年我校4队的三倍。

A - Super-palindrome(hdu 6264)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6264

题解
签到题。显然这个串最后一定会变成ababab这样的形式,枚举奇数位和偶数位的字符,暴力算一下答案,取个最优就好了。

代码

#include<bits/stdc++.h>
#define N 105
using namespace std;
int T,n,sum,ans;char s[N];
int main()
{
  scanf("%d",&T);
  while(T--)
  {
    scanf(" %s",s+1);n=strlen(s+1);ans=0;
    for(int i=1;i<=n;i++)s[i]-='a';
	for(int x=0;x<26;x++)
      for(int y=0;y<26;y++)
      {
	    sum=0;
		for(int i=1;i<=n;i++)
	    {
	      if((i&1)&&s[i]==y)sum++;
	      if(!(i&1)&&s[i]==x)sum++;
	    }
	    ans=max(ans,sum);
	  }
	for(int x=0;x<26;x++)
	{
	  sum=0;
	  for(int i=1;i<=n;i++)if(s[i]==x)sum++;
	  ans=max(ans,sum);
    }
    printf("%d\n",n-ans);
  }
  return 0;
} 

B - Master of Phi (hdu 6265)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6265

题解
对于每个质数的若干倍,它对答案的贡献是一样的。
所以2^20次爆搜每个质数选或不选,统计一下就好了。
这个题其实可以O(m)的做。
ans=nΣ(qi(1-1/pi)+1)

代码
队友写的,不贴代码了。

C - Hakase and Nano (hdu 6266)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6266

题解
先手的情况,只有场上所有石堆大小都是1且石堆数量为3的倍数才会输,不然先手必胜。
后手的情况,只要对手能一步搞到上面那种必败的情况你就必败,否则你必胜。

代码

#include<bits/stdc++.h>
using namespace std;
int T,n,d,x,cnt;

int main()
{
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d%d",&n,&d);cnt=0;
    for(int i=1;i<=n;i++)scanf("%d",&x),cnt+=(x==1);
    if(d==1)
    {
	  if(cnt==n&&n%3==0)printf("No\n");
	  else printf("Yes\n");
    }
    else
    {
	  if(n%3==1&&cnt>=n-1)printf("No\n");
	  else if(n%3==0&&cnt==n-1)printf("No\n");
	       else printf("Yes\n");
	}
  }
  return 0;
}

D - Master of Random (hdu 6267)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6267

题解
每个点如果要有贡献,那么选择的子树的根必须是它到根的路径上的某个点。基于这个东西算下概率就好了。

代码

#include<bits/stdc++.h>
#define mod 998244353
#define ll long long
#define N 100005
using namespace std;
int T,n;ll num,sum,ans,s[N],f[N];
ll Pow(ll a,int b)
{
  ll res=1;
  while(b)
  {
    if(b&1)res=res*a%mod;
    a=a*a%mod;b>>=1;
  }
  return res;
}

int main()
{
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d",&n);
	sum=0;ans=0;num=Pow(n,mod-2);
    for(int i=1;i<=n;i++)scanf("%lld",&s[i]);
    for(int i=1;i<=n;i++)
    {
      f[i]=(sum*Pow(i-1,mod-2)%mod+1)%mod;
      sum=(sum+f[i])%mod;
      ans=(ans+s[i]*f[i]%mod*num)%mod;
	}
	printf("%lld\n",ans);
  }
  return 0;
}

E - Master of Subgraph (hdu 6268)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6268

题解
我们考虑枚举根,对于已知的根,求根必选的情况下,和根联通的联通快数值和的所有可能值。
考虑f[i]表示根节点必选,i的父亲必选,i不一定选的联通快的和的所以可能值的集合。
如果这个点选,那么就转移到它的dfs序下一位。
如果这个点不选,那么它的子树也不能选,就再dfs序上跳过它的子树转移到下一个点上。
(f[i]<<s[i])->f[i+1]f[i]->f[i+sdize[i]]
集合拿个bitset存一下就好了。
这样时间复杂度是 O(nn1e5/32) ,拿个点分治优化一下,少算一些重复状态,变成 O(nlogn1e5/32) 就能过了。

代码

#include<bits/stdc++.h>
#define inf 2100000000
#define N 3005
#define M 100010 
using namespace std;
int T,n,m,s[N],check[N],flag[N],fa[N],size[N],w[N];
int k,la[N],ff[N*2],q[N],cnt,po[N],dfn[N];
struct node{int a,b;}e[N*2];
bitset<M>f[N],ans;
void add(int a,int b)
{
  e[++k]=(node){a,b};ff[k]=la[a];la[a]=k;
  e[++k]=(node){b,a};ff[k]=la[b];la[b]=k;
}

int find_root(int S)
{
  int l=1,r=2,num=inf,res=0;q[1]=S;flag[S]=1;fa[S]=0; 
  while(l<r)
  {
    int x=q[l];l++;w[x]=0;size[x]=1;
    for(int a=la[x];a;a=ff[a])
      if(!flag[e[a].b]&&!check[e[a].b])
        q[r]=e[a].b,flag[q[r]]=1,fa[q[r]]=x,r++;
  }
  for(int i=r-1;i;i--)
  {
    int x=q[i],val=max(w[x],r-size[x]-1);flag[x]=0;
    if(fa[x])size[fa[x]]+=size[x],w[fa[x]]=max(w[fa[x]],size[x]);
    if(val<num)num=val,res=x;
  }
  return res;
}

void dfs(int x)
{
  po[dfn[x]=++cnt]=x;size[x]=1;
  for(int a=la[x];a;a=ff[a])
    if(!check[e[a].b]&&!dfn[e[a].b])
	  dfs(e[a].b),size[x]+=size[e[a].b];
}

void work()
{
  f[po[1]][0]=1;
  for(int i=1;i<=cnt;i++)
  {
  	int x=po[i];ans|=(f[x]<<s[x]); 
	if(i+size[x]<=cnt)f[po[i+size[x]]]|=f[x];
	f[po[i+1]]|=(f[x]<<s[x]);
  }
  for(int i=1;i<=cnt+5;i++)dfn[po[i]]=0,f[po[i]].reset();
}

void build(int x)
{
  x=find_root(x);check[x]=1;cnt=0;dfs(x);work();
  for(int a=la[x];a;a=ff[a])
    if(!check[e[a].b])build(e[a].b);
}

int main()
{
  int a,b;
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d%d",&n,&m);k=0;ans.reset();
    memset(la,0,sizeof(la));
    memset(ff,0,sizeof(ff));
    memset(check,0,sizeof(check));
    for(int i=1;i<n;i++)scanf("%d%d",&a,&b),add(a,b);
    for(int i=1;i<=n;i++)scanf("%d",&s[i]);
    build(1);
    for(int i=1;i<=m;i++)printf("%d",(int)ans[i]);
    printf("\n");
  } 
  return 0;
}

G - Marriage (hdu 6270)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6270

题解
考虑容斥,我们需要求么n个家庭加起来至少有k对近亲的方案数。
每对家庭的方案数是C(a,k)*C(b,k)*k!
多个合并起来其实就是卷积,这个东西可以分治fft合并一下。
我们的做法是拿个堆每次取最小的两个多项式合并。
然后我们再根据至少k对近亲的方案数回推恰好k对近亲的方案数就好了。

代码
队友写的,不贴代码了。

J - Master of GCD(hdu 6273)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6273

题解
签到题。
前缀和维护下每个位置2的次幂和3的次幂,求个全局最小值算一下就好了。
队友傻逼错自闭了好久才过。。。

代码
队友写的,不贴代码了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值