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留坑