2017ccpc哈尔滨现场赛

2017ccpc哈尔滨现场赛

就过了6个题,放现场赛上的话勉强能打个金吧。

A - Palindrome(hdu6230)

题目描述
https://vjudge.net/contest/258053#problem/A

题解
一眼马拉车。
对于两个回文中心 i,j(i>j),定义回文串长问fi,如果满足条件那么必然要满足 j>i-fi 且 j+f[j]>i。所以按i从小到大枚举,那个数据结构维护j,对于每个i+fi求比它大的数量。如果j+f[j]<=i就删除就好了。

代码

#include
#define ll long long
#define N 1000010
using namespace std;
int tt,n,tot,num[N],f[N],c[N];char s[N];ll ans;
struct info{
  int p,v;
  bool operator<(const info &p)const{return v>p.v;}
};
priority_queueq;
struct bit{
  void modify(int x,int v)
  {
    for(;x<=tot;x+=x&-x)c[x]+=v;
  }
  int qry(int x)
  {
    int res=0;
    for(;x;x-=x&-x)res+=c[x];
    return res;
  }
}T;

int main()
{
  int pos,p;
  scanf("%d",&tt);
  while(tt--)
  {
    scanf(" %s",s+1);n=strlen(s+1);tot=0;ans=0;
    for(int i=1;i<=n;i++)f[i]=0;
    for(int i=1,p=0;i<=n;i++)
    {
	  if(i<=p+f[p]-1)f[i]=min(f[p+p-i],p+f[p]-i);
	  else f[i]=1;
	  while(s[i-f[i]]==s[i+f[i]])f[i]++;
	  if(i+f[i]>p+f[p])p=i;
	  num[++tot]=i-f[i]+1;num[++tot]=i;
	}
	sort(num+1,num+tot+1);
	tot=unique(num+1,num+tot+1)-num-1;
	for(int i=1;i<=n;i++)
	{
	  while(q.size())
	  {
	    info x=q.top();
	    if(x.v>i)break;q.pop();
	    p=lower_bound(num+1,num+tot+1,x.p)-num;
	    T.modify(tot-p+1,-1);
	  }
	  pos=lower_bound(num+1,num+tot+1,i-f[i]+1)-num;
	  ans+=T.qry(tot-pos+1);
	  p=lower_bound(num+1,num+tot+1,i)-num;
	  T.modify(tot-p+1,1);q.push((info){i,i+f[i]});
	}
	printf("%I64d\n",ans);
	while(q.size())q.pop();
	for(int i=1;i<=tot;i++)c[i]=0;
  }
  return 0;
}

B - K-th Number(hdu6231)

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

题解
二分答案,计算第k大小于等于ans的区间的数量。
把大于ans的标1,小于等于的标0。如果前缀和 拿个树状数组维护一下就好了。

代码

#include
#define ll long long
#define N 200010
using namespace std;
int tt,n,k,maxn,s[N],t[N],c[N],sum[N];ll m;

class bit
{
  public:
  void modify(int x,int val)
  {
    for(;x<=n*2;x+=x&-x)c[x]+=val;
  }
  int qry(int x)
  {
  	int res=0;if(x<0)return 0;
  	for(;x;x-=x&-x)res+=c[x];
    return res;
  }
}T;

ll cal(int mid)
{
  ll res=0;
  for(int i=1;i<=n;i++)
  {
    if(s[i]>mid)t[i]=1;
    else t[i]=0;
  }
  for(int i=1;i<=n*2;i++)c[i]=0;
  for(int i=1;i<=n;i++)
  {
    if(i>=k)T.modify(n-sum[i-k]+1,1);
	sum[i]=sum[i-1]+t[i];
    res+=T.qry(n-sum[i]+k);
  }
  return res;
}

int main()
{
  scanf("%d",&tt);
  while(tt--)
  {
    scanf("%d%d%lld",&n,&k,&m);maxn=0;
    m=(ll)(n-k+2)*(n-k+1)/2-m+1;
    for(int i=1;i<=n;i++)
      scanf("%d",&s[i]),maxn=max(maxn,s[i]);
    int l=0,r=maxn;
    while(r-l>1)
    {
  	  int mid=l+r>>1;
      if(cal(mid)>=m)r=mid;
      else l=mid+1;
    }
    if(cal(l)>=m)printf("%d\n",l);
    else printf("%d\n",r);
  }
  return 0;
}

D - X-Men(hdu6233)

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

题解
这个题有点意思,根本没想到。
若干个人在树上随便走,对于每次局面中最远的两个点单次操作距离肯定会减少1。
因为每个人肯定会往某个某个人的方向走,对于最远的那对人如果不减少距离那说明这对人不是最远的,就矛盾了。
所以答案就是树上的最长链长度除以2。

代码

#include
#define N 1005
using namespace std;
int T,n,m,flag[N],f[N],q[N],k,la[N],ff[N*2];
struct node{int a,b;}e[N*2];
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 bfs(int S,int tp)
{
  for(int i=1;i<=n;i++)f[i]=0;
  int l=1,r=2,pos=0;q[1]=S;f[S]=1;
  while(lf[pos])pos=x;
    for(int a=la[x];a;a=ff[a])
      if(!f[e[a].b])
        q[r]=e[a].b,f[q[r]]=f[x]+1,r++;
  }
  if(!tp)return pos;
  return f[pos]-1>>1;
}

int main()
{
  int x,y;
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d%d",&n,&m);k=0;
    for(int i=1;i<=n;i++)la[i]=flag[i]=0;
    for(int i=1;i<=m;i++)
	  scanf("%d",&x),flag[x]=1;
    for(int i=1;i

F - Permutation(hdu6235)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6235
题解
签到题,1,mid+1,2,mid+2…随便构造一下就好了。
代码
队友写的,不贴代码了。

J - Interview(hdu 6238)

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

题解
D=1:ans=(n+2)/4
D=2:ans=3*(n+2)/8
推导过程留坑。

代码

#include
#define mod 1000000007
#define ll long long
using namespace std;
int T,n,D;
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%d",&n,&D);
    if(D==1)printf("%d\n",(ll)(n+2)*Pow(4,mod-2)%mod);
    else printf("%d\n",(3ll*n+6)%mod*Pow(8,mod-2)%mod);
  }
  return 0;
}

K - Server(hdu 6420)

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

题解
一眼01分数规划。
二分答案mid,s[i]=ai-bi*mid,求个最小覆盖。
这个题有个坑,就是可以重叠覆盖。
所以先把负的全都丢上去,并把si改成0,然后从左往右dp一下,f[i]表示i结尾的最小覆盖。
转移的话维护个单调递减的栈,每次在栈上二分就好了。

代码

#include
#define inf 99999999999999999.0
#define N 100010
using namespace std;
int T,n,m,sum,top,l[N],r[N],A[N],B[N],q[N];
double s[N],f[N];
struct info{
  int l,r,a,b;
  bool operator<(const info &p)const{return r1)
  {
  	int mid=l+r>>1;
    if(q[mid]>=x)r=mid;
    else l=mid+1;
  }
  if(q[l]>=x)return f[q[l]];
  if(q[r]>=x)return f[q[r]];
  return inf;
}

bool check(double mid)
{
  double res=0;
  for(int i=1;i<=m;i++)f[i]=inf;
  for(int i=1;i<=n;i++)
  {
    s[i]=(double)t[i].a-mid*t[i].b;
    if(s[i]<0)res+=s[i],s[i]=0;
  }
  top=1;q[top]=0;
  for(int i=1;i<=n;i++)
  {
  	f[t[i].r]=min(f[t[i].r],find(t[i].l-1)+s[i]);
    if(t[i+1].r==t[i].r)continue;
    while(top&&f[q[top]]>=f[t[i].r])top--;
    q[++top]=t[i].r;
  }
  return f[m]+res<=0;
}

int main()
{
  int l,r,a,b;
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d%d",&n,&m);sum=0;
    for(int i=1;i<=n;i++)
    {
	  scanf("%d%d%d%d",&l,&r,&a,&b);
	  t[i]=(info){l,r,a,b};sum+=a;
    }
    sort(t+1,t+n+1);
	double L=0,R=sum;
	for(int i=1;i<=100;i++)
	{
	  double mid=(L+R)*0.5;
	  if(check(mid))R=mid;
	  else L=mid;
	}
	if(check(L))printf("%.3lf\n",L);
	else printf("%.3lf\n",R);
  } 
  return 0;
}

L - Color a Tree(hdu 6241)

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

题解
如果只有第一类限制那从下往上维护个L[x]表示x为子树至少要染几个点,就好了。
还有第二种限制,那就二分个答案。
对于第二种限制,子树外至少染x个点等价于子树内至多染ans-x个点。
所以再维护一个R[x]表示以x为根的子树最多染多少个点。
check一下L[i]和R[i]的关系就好了。
有个坑点,如果R[1]

代码

#include
#define N 100010
using namespace std;
int T,n,L[N],R[N],flag,size[N],k,la[N],ff[N*2];
struct node{int a,b;}e[N*2];
vectort1[N],t2[N];
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;
}

void dfs(int x,int pre,int ans)
{
  L[x]=0;R[x]=1;
  for(int a=la[x];a;a=ff[a])if(e[a].b!=pre)
	dfs(e[a].b,x,ans),L[x]+=L[e[a].b],R[x]+=R[e[a].b];
  for(int i=0;iR[i])return false;
    if(L[i]>mid)return false;
    if(L[i]>size[i])return false;
  }
  if(R[1]1)
    {
	  int mid=l+r>>1;
	  if(check(mid))r=mid;
	  else l=mid+1;
	}
	if(check(l))printf("%d\n",l);
	else printf("%d\n",r);
  }
  return 0;
}

M - Geometry Problem(hdu 6242)

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

题解
队友太强了,秒出随机算法。我这种智障选手反正是想不出来。。
题目满足至少的一半的点在大圆上。
所以每个点再大圆上的概率是1/2、
每次随机3个点,都在大圆上的概率是1/8。
失败的概率是7/8,7/8的几十次方就很小了。
所以大概跑个十几次就肯定能出解了。
n<=4的时候随便选两个点就好了,特判一下。
演了好几发,long double 改 double 就过了,我也不知道为什么。

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

H队友写的,没看题。
CEGI留坑

你可能感兴趣的:(题解,套题总结,马拉车,二分,——树状数组,脑洞,——树形dp,计算几何)