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<bits/stdc++.h>
#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_queue<info>q;
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。如果前綴和<k那區間第k大就小於等於k。
拿個樹狀數組維護一下就好了。

代碼

#include<bits/stdc++.h>
#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<bits/stdc++.h>
#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(l<r)
  {
    int x=q[l++];
    if(flag[x]&&f[x]>f[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<n;i++)
	  scanf("%d%d",&x,&y),add(x,y);
	printf("%d.00\n",bfs(bfs(1,0),1));
  }
  return 0;
}

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<bits/stdc++.h>
#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<bits/stdc++.h>
#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 r<p.r;}
}t[N];

double find(int x)
{
  int l=1,r=top;
  while(r-l>1)
  {
  	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]<mid那要判錯,因此WA了好幾發。。

代碼

#include<bits/stdc++.h>
#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];
vector<int>t1[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;i<t1[x].size();i++)L[x]=max(L[x],t1[x][i]);
  for(int i=0;i<t2[x].size();i++)R[x]=min(R[x],ans-t2[x][i]);
} 

void dfs1(int x,int pre)
{
  size[x]=1;
  for(int a=la[x];a;a=ff[a])
    if(e[a].b!=pre)dfs1(e[a].b,x),size[x]+=size[e[a].b];
}

bool check(int mid)
{
  dfs(1,0,mid);
  for(int i=1;i<=n;i++)
  {
    if(L[i]>R[i])return false;
    if(L[i]>mid)return false;
    if(L[i]>size[i])return false;
  }
  if(R[1]<mid)return false;
  return true; 
}

int main()
{
  int a,b,x,y,A,B;
  scanf("%d",&T);
  while(T--)
  {
  	scanf("%d",&n);k=0;
  	for(int i=1;i<=n+5;i++)
	    la[i]=0,t1[i].clear(),t2[i].clear();
    for(int i=1;i<n;i++)scanf("%d%d",&a,&b),add(a,b);
	dfs1(1,0);flag=0;
    scanf("%d",&A);
    for(int i=1;i<=A;i++){
	  scanf("%d%d",&x,&y),t1[x].push_back(y);
	  if(size[x]<y)flag=1;
    }
	scanf("%d",&B);
	for(int i=1;i<=B;i++){
	  scanf("%d%d",&x,&y),t2[x].push_back(y);
	  if(n-size[x]<y)flag=1;
    }
    if(flag||!check(n)){printf("-1\n");continue;}
    int l=0,r=n;
    while(r-l>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留坑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章