郁闷的出纳员
(传送门)
题意
分析
看到多操作一般都是数据结构题,阀盖splay裸题,不赘述了
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN=2e5+100;
int lim;
struct SplayTree
{
int sz[MAXN];
int ch[MAXN][2];
int pre[MAXN];
int rt,top;
inline void up(int x)
{
sz[x]=cnt[x]+sz[ ch[x][0] ]+sz[ ch[x][1] ];
}
inline void Rotate(int x,int f)
{
int y=pre[x];
ch[y][!f]=ch[x][f];
pre[ ch[x][f] ]=y;
pre[x]=pre[y];
if(pre[x]) ch[ pre[y] ][ ch[pre[y]][1]==y ] =x;
ch[x][f]=y;
pre[y]=x;
up(y);
}
inline void Splay(int x,int goal)
{//将x旋转到goal的下面
while(pre[x] != goal)
{
if(pre[pre[x]]==goal) Rotate(x ,ch[pre[x]][0]==x);
else
{
int y=pre[x],z=pre[y];
int f=(ch[z][0]==y);
if(ch[y][f]==x) Rotate(x,!f),Rotate(x,f);
else Rotate(y,f),Rotate(x,f);
}
}
up(x);
if(goal==0) rt=x;
}
inline void RTO(int k,int goal)
{//将第k位数旋转到goal的下面
int x=rt;
while(sz[ ch[x][0] ] != k-1)
{
if(k < sz[ ch[x][0] ]+1) x=ch[x][0];
else
{
k-=(sz[ ch[x][0] ]+1);
x=ch[x][1];
}
}
Splay(x,goal);
}
inline void Newnode(int &x,int c)
{
x=++top;
ch[x][0]=ch[x][1]=pre[x]=0;
sz[x]=1; cnt[x]=1;
val[x]=c;
}
inline void init()
{
sum=ch[0][0]=ch[0][1]=pre[0]=sz[0]=0;
rt=top=0; cnt[0]=0;
}
inline void Insert(int &x,int key,int f)
{
if(!x)
{
Newnode(x,key);
pre[x]=f;
Splay(x,0);
return ;
}
if(key==val[x])
{
cnt[x]++;
sz[x]++;
Splay(x,0);
return ;
}else
if(key<val[x])
{
Insert(ch[x][0],key,x);
}
else
{
Insert(ch[x][1],key,x);
}
up(x);
}
void del(int &x,int f)
{
if(!x) return ;
if(val[x]>=lim)
{
del(ch[x][0],x);
} else
{
sum+=sz[ch[x][0]]+cnt[x];
x=ch[x][1];
pre[x]=f;
if(f==0) rt=x;
del(x,f);
}
if(x) up(x);
}
inline void update()
{
del(rt,0);
}
inline int find_kth(int x,int k)
{
if(k<sz[ch[x][0]]+1)
{
return find_kth(ch[x][0],k);
}else if(k > sz[ ch[x][0] ]+cnt[x] )
return find_kth(ch[x][1],k-sz[ch[x][0]]-cnt[x]);
else
{
Splay(x,0);
return val[x];
}
}
int cnt[MAXN];
int val[MAXN];
int sum;
}spt;
int main()
{
int n,m;
char op[5];
scanf("%d%d",&n,&m);
int w=0;
spt.init();
while(n--)
{
int k;
scanf("%s%d",op,&k);
if(op[0]=='I')
{
if(k<m)
{
continue;
}
spt.Insert(spt.rt,k-w,0);
}
else if(op[0]=='A')
{
w+=k;
}
else if(op[0]=='S')
{
w-=k;
lim=m-w;
spt.update();
}
else
{
int sz=spt.sz[spt.rt];
if(k>sz) printf("-1\n");
else printf("%d\n",spt.find_kth(spt.rt,sz-k+1)+w);
}
}
printf("%d\n",spt.sum);
return 0;
}
曼哈顿
(传送门)
题意
分析
这是一道不太明显的动态规划题。
对于两条横向道路A,B,考虑四种不同的方向,然后纵向道路需要满足怎样的方向才可以满足题目要求,预处理出来。
这样,问题就转换成:寻找一种最小花费的纵向道路方案,使得其满足一系列类似这样的要求:在某个区间内至少存在一种某方向的纵向街道。然后就一目了然了。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=100+10,maxl=200+10;
const int INF=0x3f3f3f3f;
int n,m,K,r[3],rr[3][maxn],ll[3][maxn],costm[maxn][3],costn[maxn][3];
int sm[maxn],sn[maxn],vm[maxn],vn[maxn],f[maxn][maxl][maxl];
char s[maxn];
struct req { int x1,x2,y1,y2;} a[maxn];
struct inter { int l,r;} in[3][maxl];
inline bool cmp(const inter &a,const inter &b)
{ return a.l<b.l || (a.l==b.l && a.r<b.r);}
void maintain()
{
sort(in[0]+1,in[0]+r[0]+1,cmp);
sort(in[1]+1,in[1]+r[1]+1,cmp);
for(int q=0;q<2;++q)
{
for(int i=1;i<=n;++i)
{
int j=1;ll[q][i]=1;
for(;j<=r[q];++j)
if(in[q][j].l>i) break;
else if(in[q][j].r<i) ll[q][i]=j+1;
rr[q][i]=j;
}
}
}
void add(int p,int L,int R)
{
if(L>R) swap(L,R);
for(int i=1;i<=r[p];++i)
if(L<=in[p][i].l && in[p][i].r<=R) return;
in[p][++r[p]].l=L,in[p][r[p]].r=R;
}
bool prepare(int s)
{
memset(rr,0 ,sizeof rr);
memset(ll,0,sizeof ll);
r[0]=0,r[1]=0;
for(int i=1;i<=K;++i)
{
int x1=a[i].x1,y1=a[i].y1,x2=a[i].x2,y2=a[i].y2;
int p=(x1<x2),q=(y1<y2);
if(x1!=x2 && y1!=y2)
{
if(((s >> (x1-1)) & 1)==q && ((s >> (x2-1)) & 1)==q) add(p,y1,y2);
else if(((s >> (x1-1)) & 1)==q) add(p,y2,y2);
else if(((s >> (x2-1)) & 1)==q) add(p,y1,y1);
else
{
int t=0,fr=min(x1,x2)+1,en=max(x1,x2);
for(int j=fr;j<en;++j)
if(((s >> (j-1)) & 1)==q) t=1;
if(!t) return 0;
add(p,y1,y1);add(p,y2,y2);
}
}
else if(x1==x2 && y1!=y2 && ((s >> (x1-1)) & 1) != q) return 0;
else if(x1!=x2 && y1==y2) add(p,y1,y2);
}
maintain();
return 1;
}
int dp()
{
memset(f,0x7,sizeof f);
f[1][1][1]=0;
for(int i=1;i<=n;++i)
for(int j=ll[0][i];j<=rr[0][i];++j)
for(int k=ll[1][i];k<=rr[1][i];++k)
if(f[i][j][k]<INF)
{
if(f[i+1][rr[0][i]][k]>f[i][j][k]+costn[i][0])
f[i+1][rr[0][i]][k]=f[i][j][k]+costn[i][0];
if(f[i+1][j][rr[1][i]]>f[i][j][k]+costn[i][1])
f[i+1][j][rr[1][i]]=f[i][j][k]+costn[i][1];
}
return f[n+1][r[0]+1][r[1]+1];
}
int main()
{
scanf("%d%d",&m,&n);
scanf("%s",s);
for(int i=1;i<=m;++i) sm[i]=(s[i-1]=='E');
scanf("%s",s);
for(int i=1;i<=n;++i) sn[i]=(s[i-1]=='S');
for(int i=1;i<=m;++i)
{
scanf("%d",vm+i);
costm[i][sm[i]]=0,costm[i][1-sm[i]]=vm[i];
}
for(int i=1;i<=n;++i)
{
scanf("%d",vn+i);
costn[i][sn[i]]=0,costn[i][1-sn[i]]=vn[i];
}
scanf("%d",&K);
for(int i=1;i<=K;++i)
scanf("%d%d%d%d",&a[i].x1,&a[i].y1,&a[i].x2,&a[i].y2);
int tot=(1<<m),ans=INF;
for(int i=0;i<tot;++i)
{
int t=0;
for(int j=1;j<=m;++j) t += costm[j][(i>>(j-1))&1];
if(prepare(i))
{
t += dp();
ans=min(ans,t);
}
}
if(ans<INF)
{
printf("possible\n");
printf("%d\n",ans);
} else printf("impossible\n");
return 0;
}
降雨量
(传送门)
题意
一些在不同高度的伞(线段)可以左右移动,给定初始方向和速度。求开始下雨到T时间有多少雨水落在了地面上
分析
可以用Simpson自适应公式来做,但显然那样会比较麻烦。
我们用分段求面积的方法来做,这利用了所有图形都是平行四边形的特殊性。
将坐标轴反过来,t为x轴,位置为y轴,然后找到所有线段的交点(图自己画一下吧)
由图可知,所有的交点将图分了好多部分,每一部分的面积是连续变化的,用公式area(a,b)=(s(b)+s(a))*(b-a)/2计算,s(a)指直线x=a在图形上截得的总面积。将所有区间面积加起来。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=301,maxm=5010;
const double eps=1e-8;
int n,m,w,T,V,ind;
double a[maxm];
struct line
{
double x,y,len,k,L,R;
} L[maxm];
struct inter
{
double l,r;
} c[maxn];
bool cmp(const inter &a,const inter &b)
{
return a.l<b.l || (a.l == b.l && a.r<b.r);
}
double f(double x)
{
double s=0;int p=0;
for(int i=1;i<=m;i++)
if(L[i].L<=x && x<=L[i].R)
{
c[++p].l=(x-L[i].x)*L[i].k+L[i].y;
c[p].r=c[p].l+L[i].len;
}
if(!p) return 0;
sort(c+1,c+p+1,cmp);
double l=c[1].l,r=c[1].r;
for(int i=2;i<=p;i++)
if(c[i].l>r)
{
s+=r-l;
l=c[i].l;
r=c[i].r;
}
else r=max(c[i].r,r);
s+=(r-l);
return s;
}
double area()
{
double s=0;
for(int i=2;i<=ind;i++)
s += (f(a[i])+f(a[i-1]))*(a[i]-a[i-1])/2;
return s;
}
void check(double x,double y,double len,double v,double t)
{
L[++m].L=x,L[m].R=x+t,L[m].k=v;
L[m].x=x,L[m].y=y,L[m].len=len;
double l=y+t*v,r=y+len+t*v;
if(r>w+eps)
{
L[m].R=x+(w-len-y)/v;
check(L[m].R,w-len,len,-v,t-(w-len-y)/v);
} else
if(l<-eps)
{
L[m].R=x-y/v;
check(L[m].R,0,len,-v,t+y/v);
} else a[++ind]=x+t;
a[++ind]=x;
}
bool cross(int i,int j)
{
return L[i].R>=L[j].L && L[i].L<=L[j].R && fabs(L[i].k-L[j].k)>eps;
}
void get_p(int i,int j)
{
double x1=L[i].x,x2=L[j].x,y1=L[i].y,y2=L[j].y;
double k1=L[i].k,k2=L[j].k,L1=L[i].L,L2=L[j].L;
double R1=L[i].R,R2=L[j].R,len1=L[i].len,len2=L[j].len;
double x=(y2-y1+x1*k1-x2*k2)/(k1-k2);
if(L1<=x && x<=R1 && L2<=x && x<=R2)
a[++ind]=x;
x=(y2+len2-y1+x1*k1-x2*k2)/(k1-k2);
if(L1<=x && x<=R1 && L2<=x && x<=R2)
a[++ind]=x;
x=(y2-len1-y1+x1*k1-x2*k2)/(k1-k2);
if(L1<=x && x<=R1 && L2<=x && x<=R2)
a[++ind]=x;
x=(y2+len2-len1-y1+x1*k1-x2*k2)/(k1-k2);
if(L1<=x && x<=R1 && L2<=x && x<=R2)
a[++ind]=x;
}
int main()
{
scanf("%d%d%d%d",&n,&w,&T,&V);
for(int i=1;i<=n;i++)
{
double x,l,v;
scanf("%lf%lf%lf",&x,&l,&v);
if(x || l!=w) check(0,x,l,v,(double)T);
else
{
printf("0.00");
return 0;
};
}
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
if(i!=j && cross(i,j))
get_p(i,j);
sort(a+1,a+ind+1);
printf("%.2lf\n",(T*w-area())*V);
return 0;
}
小H的小屋
(传送门)
题意
小屋东西长为100,东西墙均平行于y轴,南北墙分别是斜率为k1和k2的直线(k1和k2为正实数)。南北墙的墙角处有很多块草坪,每块草坪都是一个矩形,矩形的每条边都平行于坐标轴。相邻两块草坪的接触点恰好在墙上,接触点的横坐标必须是1到99的整数。在北墙角要有m块草坪,在南墙角要有n块草坪,并约定m≤n。如果记北墙和南墙的分点集合分别为X1,X2,则应满足X1是X2的约数,即北墙的任何一个分点一定是南墙的分点。同时草坪的占地总面积最小
分析
首先想到贪心,但是只会有40分,这是一道动态规划题,比较难想
dp[i][j][k]表示长度为k,北边有i个草坪,南边有j个草坪时的最小面积
因为北边的草坪数少于南边,所以按北边来递推,这样也可不用考虑状态是否合法
预处理s[i][j]表示距离为i时,南边有j块草坪的最小面积
dp[i][j][k]=min(dp[i][j][k],dp[i-1][j'][k']+kb*(k-k')^2+s[k-k'][j-j'])
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN=100+5;
const double INF=0x3f3f3f3f;
double kb,kn,g[MAXN][MAXN],dp[MAXN][MAXN][MAXN];
int m,n;
int main()
{
scanf("%lf%lf%d%d",&kb,&kn,&m,&n);
for(int i=0;i<MAXN;i++)
for(int j=0;j<MAXN;j++)
{
for(int k=0;k<MAXN;k++)
dp[i][j][k]=INF;
g[i][j]=INF;
}
g[0][0] =0;
for(int i=1;i<=100;i++)
for(int j=1;j<=i;j++)
{
for(int k=j-1;k<i;k++)
g[i][j]=min(g[i][j],g[k][j-1]+(i-k)*(i-k)*kn);
}
dp[0][0][0]=0;
for(int k=1;k<=100;k++)
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
for(int kk=i-1;kk<k;kk++)
for(int jj=i-1;jj<j;jj++)
dp[i][j][k]=min(dp[i][j][k],dp[i-1][jj][kk]+(k-kk)*(k-kk)*kb+g[k-kk][j-jj]);
printf("%.1lf",dp[m][n][100]);
return 0;
}