Date:2019.8.2
Probelm | A | B | C |
---|---|---|---|
题目名称 | Attack | Contra | Bomb |
Difficulty | 1600/2800 | 2400 | 2000/3000 |
Finished | Yes | Yes | Yes |
AM
今天是自闭的第二天。
吸取了前一天的教训,今天总算来的早了一些
然后就看到几个大字**[集训队互测]**!!!
感觉今天的题多半不可做。
然后看A…二维矩阵第k小值带修改模板题???而且只有一个subtask???
一看就是不可做题…直接跳…
然后看B题,显然要二分,然后就是一个dp题了…好像要用矩阵乘法优化??
推了半天,发现得分和生命都是一个分段函数…
然后发现dp是四维的…subtask2都过不了…
然后看C题,看到题目感觉题目比较简单…
分析一下,发现最大值直接保存 x x x, y y y, x + y x+y x+y, x − y x-y x−y最大最小的8个点然后暴力即可…
用同样的思路去分析最小值,发现行不通。
然后想到了另外一种思路…可以先枚举 x x x最大的是哪一个,然后分类讨论它是 x + y x+y x+y或 x − y x-y x−y或者都不是的时候的情况,用线段树维护前面这些点的最接近当前 y y y坐标的点,再分类讨论这些点是作为哪个值最接近。
然后发现要用两颗线段树维护,而且代码很难打,考虑到前面的题目好像都很不可做,于是就尝试的打了一下,打了接近100行就发现了一堆bug…
于是就放弃了,感觉自己今天又要爆零了…
然后又回去看B题,发现自己把题目给看错了…
然后dp就是三维的了,赶紧把subtask2写了,调了好久才调出来…
然后时间就差不多到了…也没有时间写B的矩阵乘法了…
然后就开始自闭…
PM
下午来看题解惊讶的发现A题暴力可以直接AC…[据说是搬题目的人手残多打了一个0…]。
然后T2是一个矩阵快速幂…
T3果然是两个线段树分类讨论的题目…然而被其它大佬用随机化分治给乱搞过去了…
算法标签:暴力
/整体二分
+树套树
因为是集训队互测的题,真没想到暴力可以水过…
暴力做法就是先将点按val排序,然后暴力找到满足条件的第 k k k个即可,注意修改时需要使用指针。
整体二分+树套树的做法其实是一个板子,将整体二分的第k小板子的树状数组改成树状数组套线段树即可…
复杂度: O ( n log 3 n ) O(n\log^3 n) O(nlog3n)
#include
#include
#include
#include
using namespace std;
#define MAXN 60000
#define INF 1000000000
int n,m,val;
int id[MAXN+1];
struct point{
int x,y,z,id;
}p[MAXN+8];
bool cmp(point s1,point s2){
return s1.z<s2.z;
}
int read(){
int x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
p[i].x=read(),p[i].y=read(),p[i].z=read(),p[i].id=i;
sort(p+1,p+n+1,cmp);
for(int i=1;i<=n;i++)id[p[i].id]=i;
for(int i=1;i<=m;i++)
{
char s[10];
scanf("%s",s+1);
if(s[1]=='Q'){
int x0=read(),y0=read(),x1=read(),y1=read();
int k=read();
if(x0>x1)swap(x0,x1);
if(y0>y1)swap(y0,y1);
int nr=0;
for(int j=1;j<=n;j++)
if(p[j].x>=x0&&p[j].x<=x1&&p[j].y>=y0&&p[j].y<=y1)
if(nr<k){nr++,val=p[j].z;}
if(nr<k){printf("It doesn't exist.\n");continue;}
printf("%d\n",val);
}
else if(s[1]=='S')
{
int x=read()+1,y=read()+1;
swap(p[id[x]].x,p[id[y]].x);
swap(p[id[x]].y,p[id[y]].y);
swap(id[x],id[y]);
}
}
}
其实这个代码交上去会MLE…但我已经不想再改了…
5KB的代码写起来真爽…
#include
#include
#include
#include
using namespace std;
#define MAXN 60000
#define MAXM 12000000
#define MAXQ 100000
#define lowbit(x) x&(-x)
int n,m,tot;
int x[MAXN+5],y[MAXN+5],p[MAXN+5];
int px[MAXN+5],py[MAXN+5],pp[MAXN+5];
int ans[MAXN+5];
int read(){
int x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
struct Qry{
int kd,x1,y1,x2,y2,pos,val,qid;
}Q[MAXQ+1],Q1[MAXQ+1],Q2[MAXQ+1];
void Discret(int *a,int *tmp){
for(int i=1;i<=n;i++)tmp[i]=a[i];
sort(tmp+1,tmp+n+1);
tmp[0]=unique(tmp+1,tmp+n+1)-tmp-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(tmp+1,tmp+tmp[0]+1,a[i])-tmp;
}
struct Fen_tree{
int c[MAXN+5],Rx,Ry;
void Init(int x,int y){Rx=x,Ry=y;memset(c,0,sizeof(c));}
struct Seg_tree{
struct node{
int l,r,sum;
}p[MAXM+5];
int Stk[MAXM+1],pcnt;
Seg_tree(){pcnt=0;for(int i=MAXM;i;i--)Stk[++pcnt]=i;}
int New(){return Stk[pcnt--];}
void Del(int x){p[x].l=p[x].r=0;Stk[++pcnt]=x;}
void Insert(int &x,int l,int r,int pos,int val)
{
if(!x)x=New();
int mid=(l+r)>>1;
if(l==r){
p[x].sum+=val;
if(!p[x].sum)Del(x),x=0;
return ;
}
if(pos<=mid)Insert(p[x].l,l,mid,pos,val);
else Insert(p[x].r,mid+1,r,pos,val);
p[x].sum=p[p[x].l].sum+p[p[x].r].sum;
if(!p[x].sum)Del(x),x=0;
}
int Query(int x,int l,int r,int L,int R)
{
if(!x||R<l||L>r)return 0;
int mid=(l+r)>>1;
if(L<=l&&R>=r)return p[x].sum;
return Query(p[x].l,l,mid,L,R)+Query(p[x].r,mid+1,r,L,R);
}
}T;
void Insert(int x,int y,int val){
while(x<=Rx)
{T.Insert(c[x],1,Ry,y,val);x+=lowbit(x);}
}
int Query(int x,int l,int r){
int res=0;
while(x>0)
{res+=T.Query(c[x],1,Ry,l,r);x-=lowbit(x);}
return res;
}
}T;
void slove(int l,int r,int L,int R)
{
if(l>r)return ;
if(L==R){
for(int i=l;i<=r;i++)
if(Q[i].kd)ans[Q[i].qid]=L;
return ;
}
int mid=(L+R)>>1,lc=0,rc=0;
for(int i=l;i<=r;i++)
if(Q[i].kd){
int tmp=T.Query(Q[i].x2,Q[i].y1,Q[i].y2)-T.Query(Q[i].x1-1,Q[i].y1,Q[i].y2);
if(tmp<Q[i].val)
Q[i].val-=tmp,Q2[++rc]=Q[i];
else Q1[++lc]=Q[i];
}else{
if(Q[i].pos<=mid)
T.Insert(Q[i].x1,Q[i].y1,Q[i].val),Q1[++lc]=Q[i];
else Q2[++rc]=Q[i];
}
for(int i=1;i<=lc;i++)
if(!Q1[i].kd)
T.Insert(Q1[i].x1,Q1[i].y1,-Q1[i].val);
for(int i=l;i<=l+lc-1;i++)Q[i]=Q1[i-l+1];
for(int i=l+lc;i<=r;i++)Q[i]=Q2[i-l-lc+1];
slove(l,l+lc-1,L,mid);
slove(l+lc,r,mid+1,R);
}
int main()
{
n=read(),m=read();
for(int i=1;i<=n;i++)
x[i]=read(),y[i]=read(),p[i]=read();
Discret(x,px);Discret(y,py);Discret(p,pp);
for(int i=1;i<=n;i++)
Q[++tot]=(Qry){0,x[i],y[i],0,0,p[i],1,0};
int ID=0;
for(int i=1;i<=m;i++){
char s[10];
scanf("%s",s);
if(s[0]=='S'){
int a=read()+1,b=read()+1;
if(a==b)continue;
Q[++tot]=(Qry){0,x[a],y[a],0,0,p[a],-1,0};
Q[++tot]=(Qry){0,x[a],y[a],0,0,p[b],1,0};
Q[++tot]=(Qry){0,x[b],y[b],0,0,p[b],-1,0};
Q[++tot]=(Qry){0,x[b],y[b],0,0,p[a],1,0};
swap(p[a],p[b]);
}else{
int x1=read(),y1=read(),x2=read(),y2=read(),k=read();
if(x1>x2)swap(x1,x2);if(y1>y2)swap(y1,y2);
x1=lower_bound(px+1,px+px[0]+1,x1)-px;
x2=upper_bound(px+1,px+px[0]+1,x2)-px-1;
y1=lower_bound(py+1,py+py[0]+1,y1)-py;
y2=upper_bound(py+1,py+py[0]+1,y2)-py-1;
if(x1>x2||y1>y2)ans[++ID]=pp[0]+1;
else Q[++tot]=(Qry){1,x1,y1,x2,y2,0,k,++ID};
}
}
T.Init(px[0],py[0]);
slove(1,tot,1,pp[0]+1);
for(int i=1;i<=ID;i++)
if(ans[i]==pp[0]+1)printf("It doesn't exist.\n");
else printf("%d\n",pp[ans[i]]);
}
算法标签:二分
,矩阵快速幂
首先我们显然二分 p p p,每次算出来和 S S Scheck一下即可.
接着是一个基本的dp。
定义 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]为当前在第 i i i关,现在这轮可以得到 j j j分,现在还有 k k k条生命的概率。
注意是概率,因为实际上期望并不好统计…
我们每次统计一次当前步的概率乘上得分即可…
下面是简单实现.
dp[0][0][q]=1;
DB ans=0;
for(int i=0;i<n;i++)
for(int j=0;j<=min(i,r);j++)
for(int k=1;k<=q;k++){
dp[i+1][min(j+1,r)][min(k+1,q)]+=(dp[i][j][k]*p);
dp[i+1][0][k-1]+=(dp[i][j][k]*(1-p));
ans=ans+dp[i][j][k]*p*min(j+1,r);
}
现在,我们要用矩阵乘法做相同的工作。
具体来说,我们可以开一个 r ∗ q r*q r∗q的矩阵,再加一个节点表示结束节点.
将上面的dp过程模拟一遍即可。
复杂度: O ( R 3 Q 3 log n ) O(R^3Q^3 \log n) O(R3Q3logn)
#include
#include
#include
#include
using namespace std;
#define MAXN 40
#define DB double
int n,r,q;
DB s;
int num[MAXN+1][MAXN+1];
struct Matrix{
DB p[MAXN+1][MAXN+1];
Matrix(){memset(p,0,sizeof(p));}
Matrix operator*(const Matrix& y)const{
Matrix res;
for(int i=0;i<MAXN;i++)
for(int j=0;j<MAXN;j++)
for(int k=0;k<MAXN;k++)
res.p[i][k]=res.p[i][k]+p[i][j]*y.p[j][k];
return res;
}
};
Matrix fst_pow(Matrix A,int b)
{
Matrix res;
for(int i=0;i<MAXN;i++)
res.p[i][i]=1;
while(b){
if(b&1)res=res*A;
A=A*A;
b>>=1;
}
return res;
}
bool check(DB p)
{
Matrix A,B;int id=0;
for(int i=0;i<=q;i++){
int k=min(i-1,r);
if(i==q)k=r;
//方便统计答案
for(int j=0;j<=max(k,0);j++)
num[i][j]=++id;
}
A.p[1][1]=1;//num[0][0](=1)为end点
for(int i=1;i<=q;i++){
int k=min(i-1,r);
if(i==q)k=r;
for(int j=0;j<=max(k,0);j++){
int x=num[i][j],y=num[i-1][0];
int z=num[min(i+1,q)][min(j+1,r)];
A.p[x][y]=1-p,A.p[x][z]=p,A.p[x][1]=p*min(j+1,r);
}
}
Matrix ans=fst_pow(A,n);
return (ans.p[num[q][0]][1]>s);
}
int main()
{
scanf("%d%d%d%lf",&n,&r,&q,&s);
int l=0,r=10000000,p=r;
if(!check(1))
{printf("Impossible.");return 0;}
while(l+1<r)
{
int mid=(l+r)/2;
if(check((double)mid/p))r=mid;
else l=mid;
}
printf("%.6f",(double)l/p);
}
算法标签:线段树
/分治
首先答案显然就是 2 ∗ ( X m a x − X m i n + Y m a x − Y m i n ) 2*(X_{max}-X_{min}+Y_{max}-Y_{min}) 2∗(Xmax−Xmin+Ymax−Ymin)。
显然 X , Y X,Y X,Y独立。
先考虑最大值,由于只有3个点,那么显然我们找到, X X X, Y Y Y, X + Y X+Y X+Y, X − Y X-Y X−Y四个值得最小和最大值,然后组合一下即可。
找最小值就比较麻烦了。
正解的思路是线段树,考虑我们还是利用上述式子。
考虑答案点 a a a, b b b, c c c,满足 X a < X b < X c X_a
然后开始讨论:
然后还有一种分治乱搞算法…
先将点按 x , y x,y x,y双关键字排序,考虑只在 [ l , r ] [l,r] [l,r]区间内计算最小值。
当区间大小小于 12 12 12时,为了保证复杂度,我们直接暴力。
否则每次我们选择一个点 p o s pos pos进行划分,先计算 [ l , p o s ] , [ p o s + 1 , r ] [l,pos],[pos+1,r] [l,pos],[pos+1,r],然后仅计算 X m i n − X m a x X_{min}-X_{max} Xmin−Xmax经过 X p o s X_{pos} Xpos点的最小三角形周长,然后有一个简单的优化,就是当 ∣ X i − X p o s ∣ |X_i-X_{pos}| ∣Xi−Xpos∣已经大于当前最小答案,显然这是不优的。
然后对符合条件的点按 x , y x,y x,y双关键字排序,枚举 y y y最大的点,筛去与当前 y y y差距大于当前最小答案的点,然后暴力枚举符合条件的点更新即可。
如果我们随机化选择的 p o s pos pos,这样会更快一些,当然,不随机化也是可以过的…
只写了分治乱搞法…
因为T1 4KB+就要自闭了,更别说这个6KB+的代码了…
#pragma GCC optimize(2)
#include
#include
#include
using namespace std;
#define MAXN 100000
#define INF 1000000000
int n;
int ansmin,ansmax,xmin,xmax,ymin,ymax,k1min,k1max,k2min,k2max;
struct point{
int x,y;
}a[MAXN+5],tmp[MAXN+5];
int dis(point i,point j,point k) {
return 2*max(i.x,max(j.x,k.x))-2*min(i.x,min(j.x,k.x))+2*max(i.y,max(j.y,k.y))-2*min(i.y,min(j.y,k.y));
}
bool cmp1(point s1,point s2){
if(s1.x==s2.x)return s1.y<s2.y;
return s1.x<s2.x;
}
bool cmp2(point s1,point s2){
if(s1.y==s2.y)return s1.x<s2.x;
return s1.y<s2.y;
}
void divis(int l,int r){
int siz=r-l+1;
if(siz<12){
for(int i=l;i<=r;i++)
for(int j=i+1;j<=r;j++)
for(int k=j+1;k<=r;k++)
ansmin=min(ansmin,dis(a[i],a[j],a[k]));
return ;
}
int mid=(l+r)>>1;
divis(l,mid),divis(mid+1,r);
int tp=0;
for(int i=mid+1;i<=r;i++)
if(a[i].x-a[mid].x<ansmin)tmp[++tp]=a[i];
else break;
for(int i=mid-1;i>=l;i--)
if(a[mid].x-a[i].x<ansmin)tmp[++tp]=a[i];
else break;
sort(tmp+1,tmp+tp+1,cmp2);
int L=1;
for(int R=3;R<=tp;R++){
while(tmp[R].y-tmp[L].y>=ansmin)L++;
for(int j=L;j<R;j++)
for(int k=j+1;k<R;k++)
ansmin=min(ansmin,dis(tmp[j],tmp[k],tmp[R]));
}
}
int main()
{
scanf("%d",&n);
ansmax=-INF,ansmin=INF;
xmin=INF,xmax=-INF,ymin=INF,ymax=-INF,k1min=INF,k1max=-INF,k2min=INF,k2max=-INF;
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].y);
xmin=min(xmin,a[i].x),xmax=max(xmax,a[i].x),ymin=min(ymin,a[i].y),ymax=max(ymax,a[i].y);
k1min=min(k1min,a[i].x+a[i].y);k1max=max(k1max,a[i].x+a[i].y);
k2min=min(k2min,a[i].x-a[i].y);k2max=max(k2max,a[i].x-a[i].y);
}
ansmax=max(k1max-xmin-ymin,max(xmax+ymax-k1min,max(k2max+ymax-xmin,xmax-ymin-k2min)))*2;
sort(a+1,a+n+1,cmp1);
divis(1,n);
printf("%d\n%d\n",ansmax,ansmin);
}