开场看A,想了一会发现是个简单的数据结构题。
但是要写个分块+半平面交。
由于以前模拟赛场上写分块+半平面交写整场获得0分的好成绩的教训,所以想了一会怎么转成凸包。
看B后一会想出了80分做法。
然后想了一会100分,不太会
然后回头写A。
A的分块+凸包写的很不自信,写完后一直过不了样例。
想了一会C,发现也不太会。
然后写了个裸暴力打算进行对拍,发现裸暴力也错了。
搞了好久才发现样例错了,浪费了好多时间。
感觉时间不多了,于是写了个B的20分和C的10分。
准备写B的80分,但是发现细节有点多。
感觉离调出A不久了。
然后就一直调到比赛结束。
最后只有30分。。。。。
总结:由于A的样例出错浪费了大量时间。
而且由于分块好久没写了,导致A调很久。
这说明以后要多写分块,减少调试时间。
而且,对计算几何(凸包)不熟悉
题解:
A:从小到大枚举(c)。
问题转化成:维护一个数据结构,支持插入一个值,询问(p*a_i),其中(p)是(geq i)的有值位置数。
考虑维护一数组(b),如果插入一个位置(v),把(b_{1...v}++)。
我们需要查询(max(b_i*a_i))
考虑凸包。我们要支持区间加法。
对于整块的加法可以预处理出每块的半平面交然后移动指针。
对于散块的加法考虑重构,我们要把直线(a_ix+y)变成(a_ix+y+v)
发现斜率是不会变的,所以建立凸包不用排序
时间复杂度(O(nsqrt{n}))
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 100010
int n,w,mx,ans[N],vi[N],l[N],r[N],ss,bb[N],id[N],tg[N],tt[N];
struct no{
int x,y,id;
}a[N],b[N];
int va(no x,int y){
return x.x*y+x.y;
}
int operator <(no x,no y){
return x.y<y.y;
}
int cc(no x,no y){
return x.x<y.x;
}
int cp(no j,no k,no i){
return (k.y-j.y)*(i.x-k.x)>=(k.x-j.x)*(i.y-k.y);
}
struct hl{
no s[500];
int tp,pt,l,r,id;
void ps(no x){
if(tp<2){
s[++tp]=x;
return;
}
while(tp>1&&cp(s[tp-1],s[tp],x))
tp--;
s[++tp]=x;
}
void rb(){
tp=0;
for(int i=l;i<=r;i++)
if(vi[i]){
no va;
va.x=b[i].x;
va.y=(-tg[id]-tt[i])*b[i].x;
ps(va);
}
pt=1;
}
int qu(int x){
if(!tp)
return 0;
while(pt<tp&&va(s[pt],x)>=va(s[pt+1],x))
pt++;
return -va(s[pt],x);
}
}bl[500];
int qu(){
int ans=0;
for(int i=1;i<=ss;i++)
ans=max(ans,bl[i].qu(-tg[i]));
return ans;
}
void ad(int c,int d){
for(int i=1;i<=ss;i++){
if(c<=l[i]&&r[i]<=d)
tg[i]++;
else if(d>=l[i]){
for(int j=max(c,l[i]);j<=min(d,r[i]);j++)
tt[j]++;
for(int j=l[i];j<=r[i];j++)
tt[j]+=tg[i];
tg[i]=0;
bl[i].rb();
}
}
}
void ins(int x,no y){
vi[x]=1;
b[x]=y;
for(int i=l[bb[x]];i<=r[bb[x]];i++)
tt[i]+=tg[bb[x]];
tg[bb[x]]=0;
bl[bb[x]].rb();
}
signed main(){
freopen("laofu.in","r",stdin);
freopen("laofu.out","w",stdout);
scanf("%lld%lld",&n,&w);
int bs=sqrt(n),po=1,i=1;
while(po<=n){
l[i]=po;
r[i]=min(po+bs-1,n);
po+=bs;
i++;
}
ss=i-1;
for(int i=1;i<=ss;i++){
bl[i].l=l[i];
bl[i].r=r[i];
bl[i].id=i;
}
for(int i=1;i<=ss;i++)
for(int j=l[i];j<=r[i];j++)
bb[j]=i;
for(int i=1;i<=n;i++){
scanf("%lld%lld",&a[i].x,&a[i].y);
b[i]=a[i];
mx=max(mx,a[i].y);
}
int pt=1;
sort(b+1,b+n+1,cc);
for(int i=1;i<=n;i++)
id[b[i].x]=max(id[b[i].x],i);
for(int i=1;i<=n;i++){
a[i]=b[i];
a[i].id=i;
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
b[i].y=b[i].x=0;
for(int c=1;c<=mx+1;c++){
while(a[pt].y<c&&pt<=n){
ins(a[pt].id,a[pt]);
ad(1,id[a[pt].x]);
pt++;
}
printf("%lld ",qu()+(n-pt+1)*w*c);
}
}
C:
看了题解感觉不是很难,但是不知道为什么不会做。
想到了字符数量取差,想到前缀和就是想不到正解。
先考虑字符集为(2)的情况,令(f_i)表示(s_{1...i})中(a)个数和(b)个数的差。
一个字符串(s_{j...i})的差是(f_i-f_{j-1}),要求(-k leq f_i-f_{j-1}leq k)
发现(i,j)调换不影响答案。
所以合法条件是:(max(f_i)-min(f_i) leq k)
考虑枚举(min(f_i)),就能知道(max(f_i))的取值范围((min(f_i)+k)),可以用(k)次dp计算。
设(f_{i,j})表示填了(i)个字符,最大值为(j)的答案,可以用矩阵乘法计算。
发现这样子会算重,(max(f_i)-min(f_i)leq k-1)的会算重。
所以可以把(k--)再次进行前面的dp。
发现这样子就不会算重了,因为更小的区间([i,i+l])会被计算((k-l+1)-(k-l)=1)次
回到原问题。令(f_i)表示(s_{1...i})中(a)个数和(b)个数的差,(g_i)表示(s_{1...i})中(a)个数和(c)个数的差。
设(l1leq f_ileq l1+k,l2leq g_ileq l2+k,l3leq f_i-g_ileq l3+k)
设(f_{i,j,k})表示填了(i)个字符,(a_i=l1+j1,b_i=l2+j2)的个数。
在转移时需要满足(l3 leq (l1 + j1) − (l2 + j2) leq l3 + k)
就是(l3-l1+l2leq j1-j2leq l3-l1+l2+k)
枚举(l3-l1+l2)即可。
时间复杂度(O(k^7log_2n))
注意也要容斥。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define mo 998244353
int ct,n,k,v1,v2,v3,ans,va;
struct no{
int a[40][40];
}bz,t;
no operator *(no x,no y){
no ans=bz;
for(int i=0;i<ct;i++)
for(int j=0;j<ct;j++)
for(int k=0;k<ct;k++)
ans.a[i][j]=(ans.a[i][j]+x.a[i][k]*y.a[k][j]%mo)%mo;
return ans;
}
no qp(no x,int y){
no r=bz;
for(int i=0;i<ct;i++)
r.a[i][i]=1;
for(;y;y>>=1,x=x*x)
if(y&1)
r=r*x;
return r;
}
int pd(int s,int a,int b){
if(a<0||v2<a||b<0||v3<b||(s-v1>a-b)||(a-b>s))
return 0;
return 1;
}
int gt(){
ct=(v2+1)*(v3+1);
va=0;
for(int s=-k;s<=2*k;s++){
t=bz;
for(int i=0;i<=v2;i++)
for(int j=0;j<=v3;j++)
if(s-v1<=i-j&&i-j<=s){
int x=i*(v3+1)+j;
if(pd(s,i+1,j))
t.a[x][x+v3+1]++;
if(pd(s,i,j+1))
t.a[x][x+1]++;
if(pd(s,i-1,j-1))
t.a[x][x-v3-2]++;
}
no vv=qp(t,n);
for(int i=0;i<=v1;i++){
int a=s-i,b;
if(a<=k&&-k<=a){
for(int j=0;j<=v2;j++){
b=j-a;
if(b>=0&&b<=v3&&i-v1<=j-b-a&&j-b-a<=i)
for(int l=0;l<ct;l++)
va=(va+vv.a[j*(v3+1)+b][l])%mo;
}
}
}
}
return va;
}
signed main(){
freopen("baofushehui.in","r",stdin);
freopen("baofushehui.out","w",stdout);
scanf("%lld%lld",&n,&k);
v1=v2=v3=k;
ans=gt();
v1--;
ans=(ans-gt()*3%mo+mo)%mo;
v2--;
ans=(ans+gt()*3)%mo;
v3--;
ans=(ans-gt()+mo)%mo;
printf("%lld",ans);
}