A组T1 bzoj 2674 Attack
説明
chnlich私は三国志のゲームをプレイするのが大好き、と戦略の驚きのいくつかを使用するようにしたいです。今、彼は世界を征服するために旅を開始したいと考えています。彼の敵の都市NとNの知事、N N都市は2次元平面上の点とみなすことができます。Nの都市は0,1,2、......、N-1の番号が付け。i番目の都市の座標は、(XI、李)紫の値の都市知事を保護する能力です。
各時間chnlichは側面が矩形領域の軸に平行な選択、及びK小都市の前記驚き県能力値(都市及び驚き後に依然として県)。
しかし、彼の敵は、多くの場合、密かにchnlich見つかった弱点を防ぐために、知事2都市を交換します。
さて、chnlichは毎回驚きを重視する彼の敵の能力を知りたいと思いました。
入力
入力の最初の行は、2つの整数N、Mを含み、NはMはMイベントが次に起こるであり、都市や知事の数を表します。
行1 + Nの最初の行への第2の入力、三つの整数をそれぞれ含むが、I + 2行3つの順次都市I西、李、紫、定義したような問題の整数を表します。
N + 2、N + M + 1入力ラインの第二行は、各ラインは、二つの可能な形態を有しています。
最初の
QUERY X0 Y0のX1のY1 kを
矩形の相対頂点chnlichクエリ(X0、Y0)、(X1、Y1)を表し、k値が小さすぎる容量であります
値を維持する能力。
第2
SWAPのXY
chnlich敵が数値xとyの2つの都市県を交換表します。
出力
クエリーごとに、出力ライン。
知事、出力がない場合はk値の小さな容量があり、「それは存在しませんが。」(引用符なし)。
そうでなければ、出力矩形の容量の整数値は、K値が小さい容量知事で表します。
サンプル入力
3 5
1 1 1
2 2 2
3 3 3
QUERY 1 1 3 3 3
SWAP 0 1
QUERY 2 2 4 4 1
SWAP 2 2
QUERY 2 2 3 3 3
サンプル出力
3
1
には存在しません。
データ制約
ヒント
データの100%に、N <= 60000、M < = 10000,0 <=西、李、紫<= 10 ^ 9、K <= 10 ^ 9、 すべての操作が有効であることを確実にします。
分析
その理由は、この問題の正の解は癌です。しかし、それはOJに安く手だった、その後、10秒1秒の時間になって!!!!!!!!
ペアは、ある10秒
尋ねられたとき、各点は、直接重量ソートする、O(N)ラインに再度スキャン遭遇は、矩形内で直接交換を切り替える、等k番目の最初の数まで記録されています。
複雑さはO(nm)で
なお、左上と右下の四角形は、2つのエンドポイントの左下と右上を与えることができる、2つのエンドポイントを与えることができます。
コード:
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,q[60005],ma[60005];char ch[5];
struct node{int x,y,z;}p[60005];
bool cmp(int a,int b){return p[a].z<p[b].z;}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z),q[i]=i;
sort(q+1,q+1+n,cmp);
for(int i=1;i<=n;i++)ma[q[i]]=i;
for(int i=1;i<=m;i++)
{
scanf("%s",ch+1);
if(ch[1]=='S')
{
int a,b;
scanf("%d%d",&a,&b);a++;b++;
swap(q[ma[a]],q[ma[b]]);swap(p[a].z,p[b].z);
swap(ma[a],ma[b]);
}
if(ch[1]=='Q')
{
int x1,y1,x2,y2,k,cnt=0,ans;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&k);
if(x1>x2)swap(x1,x2);
if(y1>y2)swap(y1,y2);
for(int i=1;i<=n&&cnt<k;i++)
if(x1<=p[q[i]].x&&p[q[i]].x<=x2&&y1<=p[q[i]].y&&p[q[i]].y<=y2)
{cnt++;if(cnt==k){ans=q[i];break;}}
if(cnt<k)printf("It doesn't exist.\n");
else printf("%d\n",p[ans].z);
}
}
}
A组T2 bzoj 2676 Contra
Description
偶然间,chnlich 发现了他小时候玩过的一个游戏“魂斗罗”,于是决定怀旧。但是这是一个奇怪的魂斗罗 MOD。
有 N 个关卡,初始有 Q 条命。
每通过一个关卡,会得到 u 分和1条命,生命上限为 Q。其中 u=min(最近一次连续通过的关数,R)。
若没有通过这个关卡,将会失去1条命,并进入下一个关卡。
当没有生命或没有未挑战过的关卡时,游戏结束,得到的分数为每关得到的分数的总和。
由于 chnlich 好久不玩这个游戏了,每条命通过每个关卡的概率均为p(0<=p<=1),原先 chnlich 的最高分纪录是 S。
现在 chnlich 想要知道,当 p 至少为多少时,chnlich 期望获得的总分数能够超过原先的最高分。
Input
输入共一行,分别表示整数 N,整数 R,整数 Q,原先的最高分整数 S。
Output
输出共一行,若不存在这样的 p,输出"Impossible."(不包含引号),否则输出 p(保留6位小数)。
Sample Input
【样例输入一】
4 2 1 5
【样例输入二】
12 3 2 12
Sample Output
【样例输出一】
0.880606
【样例输出二】
0.687201
Data Constraint
Hint【数据说明】
对于20%的数据,N<=15
对于50%的数据,N<=10000
对于100%的数据,N<=10^8,1<=R<=20,1<=Q<=5,保证 S 是一个可能出现的分数。
【补充说明】
补充说明】
例如,当 N=12,R=3,Q=2时
第一关:未通过 u=0 获得分数0 总分为0 剩余生命1
第二关:通过 获得分数1 总分为1 剩余生命2
第三关:通过 获得分数2 总分为3 剩余生命2
第四关:通过 获得分数3 总分为6 剩余生命2
第五关:通过 获得分数3 总分为9 剩余生命2
第六关:未通过 获得分数0 总分为9 剩余生命1
第七关:通过 获得分数1 总分为10 剩余生命2
第八关:未通过 获得分数0 总分为10 剩余生命1
第九关:未通过 获得分数0 总分为10 剩余生命0
游戏结束 总分为10
这是 chnlich 游戏的一种可能性
假装分析
显然这是一道期望题(废话)。二分一下概率p,然后看dp出来的期望值是否比s大。
设dp[i][j][k]表示已经过了i关,连胜了j次,还剩下k条命的概率。
那么dp[i][j][k]就可以转移到dp[i+1][0][k-1](失败)和dp[i+1][min(j+1,r)][min(k+1,q)](成功)。(取min是因为超过了限制的状态贡献相同,可以合在一起)
则dp[i+1][0][k-1]+=dp[i][j][k]*(1-p),dp[i+1][min(j+1,r)][min(k+1,q)]+=dp[i][j][k]*p。
如何求期望值呢?
因为期望是可以分开求的,所以,每个状态dp[i][j][k]的贡献就是dp[i][j][k]*j,加起来就好啦。
不过这样是会TLE的,要用矩阵加速。
如果就直接拿来加速不好写,所以将j与k拿过来压成一个状态就行了,给每种(j,k)编个号。
另外还要一边乘一边计算答案,只需要修改一下矩阵,拿一个没用的编号来存答案即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#define id(a,b) (min(a,r)*q+min(b,q))
using namespace std;
int n,m,q,r,s;
struct mar
{
double a[125][125];mar(){memset(a,0,sizeof a);}
inline mar operator *(const mar &b)const
{
mar c;
for(int i=1;i<=m;i++)for(int k=1;k<=m;k++)for(int j=1;a[i][k]>=1e-11&&j<=m;j++)
c.a[i][j]+=a[i][k]*b.a[k][j];
return c;
}
};
inline bool pan(double p)
{
mar A,B,C;B.a[m][m]=1.0;//将之前的答案传递下来
for(int i=0;i<=r;i++)for(int j=1;j<=q;j++)
{
B.a[id(i,j)][id(i+1,j+1)]=p;
B.a[id(i,j)][m]=i;//计算每个状态对答案的贡献
if(j>1)B.a[id(i,j)][id(0,j-1)]=1.0-p;
}
int k1=n+1;for(int i=1;i<=m;i++)C.a[i][i]=1.0;//n+1是因为dp[n][j][k]的答案之前没统计到,要多乘一次来统计。
while(k1){if(k1&1)C=B*C;B=B*B;k1/=2;}
A.a[1][id(0,q)]=1.0;A=A*C;
return A.a[1][m]>double(s);
}
int main()
{
scanf("%d%d%d%d",&n,&r,&q,&s);
double l1=0,r1=1,ans=0;m=(r+1)*q+1;//m就是那个来存答案的编号
if(pan(r1)<1e-7){printf("Impossible.");return 0;}
while(r1-l1>2*1e-7)
{
double mid=(l1+r1)*0.5;
if(pan(mid))ans=mid,r1=mid;
else l1=mid;
}
printf("%.6lf",l1);
}
A组T3 bzoj 2675 Bomb
Description
A 国和 B 国是两个超级大国,长期处于冷战状态;
A 国在 B 国中设有 N 个情报站,编号为 1,2,3, …… ,N ,每个情报站有一个坐标 (Xi,Yi) 。但是, A 国的工作人员发现,每个情报站里都被埋上了炸弹!
这些炸弹非常特殊 , 只要同时拆除其中的三个炸弹 , 所有炸弹就都不会爆炸了。
由于各个情报站联络需要代价 , 拆除炸弹需要花费的总代价为这些炸弹两两之间的曼哈顿距离和。
现在 A 国的指挥部门找到了你 , 希望知道可能需要的最大代价和最小代价 。
Input
输入的第一行包含一个整数 N 。接下来 N 行,第 i+1 行两个整数 Xi,Yi ,表示第 i 个情报站的坐标。
Output
输出两行 , 每行包含一个整数 , 第一行表示可能的最大代价 , 第二行表示可能的最小代价。
Sample Input
4
1 1
1 2
2 1
2 3
Sample Output
6
4
Data Constraint
Hint
对于 30% 的数据, N<=500
对于另外 10% 的数据,每个点出现至少两遍
对于 50% 的数据, N<=1000
对于 60% 的数据, N<=8000
对于 70% 的数据, N<=15000
对于 80% 的数据, N<=50000
对于 100% 的数据, N<=100000 , 0<=Xi,Yi<=10^8
【 注释 】
对于两个点 (X0,Y0),(X1,Y1) ,
它们之间的曼哈顿距离为 abs(X0-X1)+abs(Y0-Y1) 。
抄题解的分析
先来看最大值(接下来是题解):
我们考虑三个点两两之间的曼哈顿距离和的实质。通过画图, 我们容易发现,问题的实质是求能包含这三个点的最小矩形的周长,即 2*(Xmax-Xmin+Ymax-Ymin),因此我们只要最大化和最小化 Xmax-Xmin+Ymax-Ymin 即可。 考虑这四个未知量,显然,一个点最多确定一个 X 未知量和一个 Y 未知量,答案的组成可 能有两种情况:
①一个点确定了一个 X 和一个 Y,另外两个点分别确定了一个 X 和一个 Y。
②一个点确定了一个 X 和一个 Y,另一个点同样确定了一个 X 和一个 Y,还有一个点 什么都没有确定(但必须存在这个点)。
我们分别考虑这两种情况。先考虑最大值。显然,确定最大值的点一定在 Xmax+Ymax,-Xmin+Ymax,-Xmin-Ymin,Xmax-Ymin,Xmin,Xmax,Ymin,Ymax 最大的至多 8个 点中选择三个,因此只需选出能使这些值变得最大的点暴力即可。
个人理解就是把曼哈顿距离的绝对值展开来进行维护,以X+Y的最大值为例,它能保证我们在这里取了一个点。用它减去X与Y的最小值。若X与Y的最小值属于同一个点,那么就是情况②,若不属于同一个点就是情况①。
再来看看最小值
本来最小值我打算想写线段树的,但网上一两百行的代码直接把我劝退了。于是又去找了一种分治的做法。可为什么我怎么看都像暴力加剪枝?
按照x排序后就直接分治,若点数小于15就直接暴力。合并的时候将两边距离中点距离小于当前最小答案的点拿出来,再按y值排序,再次暴力即可,如果一个点与另一个点的y值之差大于等于当前最小答案那么也要舍去。
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
int addmax,addmin,submax,submin,xmax,xmin,ymax,ymin,ansmax,ansmin;
struct node{int x,y;}p[100005],q[100005];
bool cmp1(node a,node b){return a.x==b.x?a.y<b.y:a.x<b.x;}
bool cmp2(node a,node b){return a.y==b.y?a.x<b.x:a.y<b.y;}
int dis(node a,node b,node c){return abs(a.x-b.x)+abs(a.y-b.y)+abs(a.x-c.x)+abs(a.y-c.y)+abs(b.x-c.x)+abs(b.y-c.y);}
void solve(int l,int r)
{
if(r-l<15)
{
for(int i=l;i<r-1;i++)for(int j=i+1;j<r;j++)for(int k=j+1;k<r+1;k++)
ansmin=min(ansmin,dis(p[i],p[j],p[k]));return;
}
int mid=(l+r)/2,cnt=0,be=1;solve(l,mid);solve(mid,r);
for(int i=mid;i<=r&&abs(p[i].x-p[mid].x)<ansmin;i++)q[++cnt]=p[i];
for(int i=mid-1;i>=l&&abs(p[i].x-p[mid].x)<ansmin;i--)q[++cnt]=p[i];
sort(q+1,q+1+cnt,cmp2);
for(int i=3;i<=cnt;i++)
{
while(abs(q[be].y-q[i].y)>=ansmin)be++;
for(int j=be;j<i-1;j++)for(int k=j+1;k<i;k++)
ansmin=min(ansmin,dis(q[i],q[j],q[k]));
}
}
int main()
{
scanf("%d",&n);
addmin=submin=xmin=ymin=ansmin=1<<30;
addmax=submax=xmax=ymax=ansmax=-1<<30;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&p[i].x,&p[i].y);
xmax=max(xmax,p[i].x);ymax=max(ymax,p[i].y);
xmin=min(xmin,p[i].x);ymin=min(ymin,p[i].y);
addmax=max(addmax,p[i].x+p[i].y);addmin=min(addmin,p[i].x+p[i].y);
submax=max(submax,p[i].x-p[i].y);submin=min(submin,p[i].x-p[i].y);
}
ansmax=max(ansmax,addmax-xmin-ymin);ansmax=max(ansmax,xmax+ymax-addmin);
ansmax=max(ansmax,submax-xmin+ymax);ansmax=max(ansmax,xmax-ymin-submin);
sort(p+1,p+1+n,cmp1);solve(1,n);
printf("%d\n%d",ansmax*2,ansmin);
}