就过了6个题,放现场赛上的话勉强能打个金吧。
题目描述
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;
}
题目描述
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;
}
题目描述
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
题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6235
题解
签到题,1,mid+1,2,mid+2…随便构造一下就好了。
代码
队友写的,不贴代码了。
题目描述
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;
}
题目描述
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;
}
题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6241
题解 代码 题目描述 题解 代码 H队友写的,没看题。
如果只有第一类限制那从下往上维护个L[x]表示x为子树至少要染几个点,就好了。
还有第二种限制,那就二分个答案。
对于第二种限制,子树外至少染x个点等价于子树内至多染ans-x个点。
所以再维护一个R[x]表示以x为根的子树最多染多少个点。
check一下L[i]和R[i]的关系就好了。
有个坑点,如果R[1]#include
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 就过了,我也不知道为什么。
队友写的,不贴代码了。
CEGI留坑