中山纪中集训Day2又是测试(划水)

A组T1 bzoj 2674 Attack

Description

chnlich 非常喜欢玩三国志这款游戏,并喜欢用一些策略出奇制胜。现在,他要开始征服世界的旅途了。他的敌人有N 座城市和N 个太守, N个城市可以看作在二维平面上的N 个点。N 座城市的标号为0,1,2,......,N-1。第i 座城市的坐标为(Xi,Yi),镇守这座城市的太守的能力值为Zi。

chnlich 每次会选择一个边平行于坐标轴的矩形区域,并奇袭其中太守能力值第K小的城市(奇袭结束之后城市与太守依然存在)。

不过,他的敌人经常会偷偷交换两座城市的太守,防止弱点被chnlich 发现。

现在,chnlich 想要知道,每次奇袭时他的敌人的能力值。

Input

输入的第一行包含两个整数 N,M,N 表示城市与太守的个数,M 表示接下来发生了 M 个事件。

输入的第二行到第 N+1行,每行包含三个整数,第 i+2行的三个整数依次表示编号为 i 的城市的 Xi,Yi,Zi,含义如题所述。

输入的第 N+2行到第 N+M+1行,每行有两种可能形式:

第一种

QUERY x0 y0 x1 y1 k

表示 chnlich 询问一个相对顶点为(x0,y0),(x1,y1)的矩形中,第 k 小能力值太

守的能力值。

第二种

SWAP x y

表示 chnlich 的敌人交换了编号为 x 和 y 两座城市的太守。

Output

对于每一个 QUERY,输出一行。

若不存在第 k 小能力值的太守,输出"It doesn't exist."(不包含引号)。

否则输出一个整数,表示矩形内能力值第 k 小太守的能力值。

Sample Input

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

Sample Output

3
1
It doesn't exist.

Data Constraint

Hint
对于100%的数据,N<=60000,M<=10000,0<=Xi,Yi,Zi<=10^9,k<=10^9,保证所有操作均合法。

分析

讲道理,这个题的正解十分毒瘤。BUT,OJ上有人手贱,于是1s时限变成了10s!!!!!!!!

对的,是10秒

直接给每个点按权值排序,询问时O(n)扫一遍就行了,遇到在矩形内的就记录个数直到遇到第k个就好了,交换时就直接交换。

复杂度为O(nm)

注意,矩形可能给出左上和右下两个端点,也可能给出左下和右上的两个端点。

代码:

#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
今日推荐