中山Jizhong研修2日目のテスト(水)です

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);
}

 

おすすめ

転載: www.cnblogs.com/firecrazy/p/11296213.html