zoukankan      html  css  js  c++  java
  • 省选模拟测试9

    期望得分: (0+30+30 = 60)

    实际得分:(0+40+36=76)

    数据水了点,暴力分多拿了点,但还是一道题都不会做。

    T1 折纸游戏

    题意描述

    有一张纸平放在桌面上,划分为 (n imes m) 个格子,它们构成了 (n)(m) 列的网格,每个格子大小相同。第 (i) 行第 (j) 列的格子的颜色为 (c_{i,j})

    每次折叠操作可以描述为:

    • 将当前的纸沿着一条平行于边界且不穿过任何一个格子的线划分为 (A,B) 两个部分,且 (S_Aleq S_B)(S_A)(A) 的面积,这样 (A) 折叠后不会覆盖到当前纸的外面)。
    • (A) 沿着这条线折叠并覆盖到 (B) 的关于这条线的对称位置上,并保持 (B) 的位置不变。
    • 必须保证折叠后 (A,B) 重合在一起的格子颜色相同。
    • 折叠之后原来的 (B) 即看成新的一张纸。

    你可以进行任意多次这个操作。

    你需要求出有多少个子矩形满足它固定在桌面上不动时,可以将其余部分全部折到它上面。

    数据范围: (n,mleq 3000,n imes mleq 10^6)

    solution

    不会做,咕咕咕。

    T2 爬上山顶

    题意描述

    给出一些山顶的坐标 ((x_i,y_i)) ,我们认为山是这些山顶构成的一段折线 (l),其中每条线段连接 ((x_i,y_i))((x_{i+1},y_{i+1})) ((1leq ileq n))(保证 (x_i) 单调递增)。

    每到一个山顶以后,你会左右张望找到能看到的最高的山顶 (P)(一个山顶 (P) 能被他们看到当且仅当连接你与 (P) 的线段与 (l) 只在你和 (P) 上相交),并向到目前为止你看到过的最高的山顶那个方向继续前进(爬到左边相邻的一个山顶或右边相邻的一个山顶)。

    爬到最高的山顶后你会停下来。

    对于每个山顶,求出若你选择此处作为爬山起点的话,你会爬过几个山顶(重复经过算多次,不同的询问相互独立)。

    注意:(y) 坐标相同情况下选取 (x) 坐标最大的为最高山顶。

    数据范围:(1leq nleq 5 imes 10^5,x_i,y_ileq 10^6)

    solution

    首先,我们可以求出 (l_i,r_i) 分别表示 从 ((x_i,y_i)) 向左右两边能看到的最高的山顶。

    不难发现 (l_i) 为前 (i) 个点构成的上凸壳中的倒数第二个点, (r_i) 为后 (i) 个点构成的上凸壳中第二个点。

    有了 (l_i,r_i) 我们很容易求出 (to_i) 表示从 ((x_i,y_i)) 能看到的最高的山峰。

    我们是往看到过的最高的山顶方向前进,也就是说当前在 (i) 点的前进方向可能不是 (to_i)

    假设当前看到的最高的山顶为 (h = y_{to_i}) ,找到一个 (j) 满足 (y_{to_j} > h) ,那么到 (j) 之前我们的方向是不会改变的。

    (pre[i])(i) 左边满足 (y_{to_i} < y_{to_j}) 的最大的 (j)(y_i) 相等的情况下,就比较横坐标的大小),如果不存在则 (pre[i] = to[i])

    (suf[i])(i) 右边满足 (y_{to_i} < y_{to_j}) 的最小的 (j)(y_i) 相等的情况下,就比较横坐标的大小),如果不存在则 (suf[i] = to[i])

    (pre[i])(suf[i]) 维护一个单调栈就可以得到。

    容易发现,当 (i > to[i]) 的时候,从 (i) 走到 (pre[i]) 的时候,方向是不会改变的,且到 (pre[i]) 的时候,可以看到更高的山峰或者达到最高的山峰,此时我们的方向就会发生改变。

    同理当 (i<to[i]) 的时候,从 (i) 走到 (suf [i]) 的时候,方向是不会改变的,且到 (pre[i]) 的时候,可以看到更高的山峰或者达到最高的山峰,此时我们的方向就会发生改变。

    因此,如果 (i>to[i]) 我们就从 (i)(max(to[i], pre[i])) 连一条边权为 (i-max(to[i],pre[i])) 的边。

    (i<to[i]) 就从 (i)(min(to[i],suf[i])) 连一条边权为 (min(to[i],suf[i])) 的边。

    不难发现,这样建图会构成一棵树。

    (i) 号点的答案则为 (i) 到根节点的距离。

    复杂度:(O(n))

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N = 5e5+10;
    int n,st,top,tot;
    int ls[N],rs[N],pre[N],net[N],dis[N],sta[N],head[N],to[N],x[N],y[N],k[N];
    struct node
    {
        int to,net,w;
    }e[10000010];
    void add(int x,int y,int w)
    {
        e[++tot].to = y;
        e[tot].w = w;
        e[tot].net = head[x];
        head[x] = tot;
    }
    int pmax(int i,int j)
    {
    	if(y[i] == y[j]) return x[i] > x[j] ? i : j;
    	else return y[i] > y[j] ? i : j;
    }
    double slope(int i,int j)
    {
        return 1.0*(y[j]-y[i])/(x[j]-x[i]);
    }
    bool pd(int i,int j)
    {
    	if(y[to[i]] == y[to[j]]) return x[to[i]] > x[to[j]];
        return y[to[i]] > y[to[j]];
    }
    void dfs(int x,int fa)
    {
        for(int i = head[x]; i; i = e[i].net)
        {
            int to = e[i].to;
            if(to == fa) continue;
            dis[to] = dis[x] + e[i].w;
            dfs(to,x);
        }
    }
    signed main()
    {
    	freopen("mountain.in","r",stdin);
    	freopen("mountain.out","w",stdout);
        scanf("%d",&n);
        for(int i = 1; i <= n; i++) 
        {
            scanf("%d%d",&x[i],&y[i]);
            if(y[i] >= y[st]) st = i;
        }
        sta[++top] = 1; sta[++top] = 2; ls[1] = 1; ls[2] = 1;
        for(int i = 3; i <= n; i++)
        {
            while(top > 1 && slope(sta[top],sta[top-1]) < slope(i,sta[top-1])) top--;
            ls[i] = sta[top]; sta[++top] = i;
        }
        top = 0; sta[++top] = n; sta[++top] = n-1; rs[n] = n; rs[n-1] = n;
        for(int i = n-2; i >= 1; i--)
        {
            while(top > 1 && slope(sta[top],sta[top-1]) > slope(i,sta[top-1])) top--;
            rs[i] = sta[top]; sta[++top] = i;
        }
        for(int i = 1; i <= n; i++) to[i] = pmax(ls[i],rs[i]);
        top = 0;
        for(int i = 1; i <= n; i++)
        {
            while(top && pd(i,sta[top])) top--;
            if(top) pre[i] = sta[top];
            else pre[i] = to[i];
            sta[++top] = i;
        }
        top = 0;
        for(int i = n; i >= 1; i--)
        {
            while(top && pd(i,sta[top])) top--;
            if(top) net[i] = sta[top];
            else net[i] = to[i];
            sta[++top] = i;
        }
        for(int i = 1; i <= n; i++)
        {
        	if(i == st) continue;
            if(i > to[i]) add(max(to[i],pre[i]),i,i-max(pre[i],to[i]));
            if(i < to[i]) add(min(to[i],net[i]),i,min(to[i],net[i])-i);
        }
        dfs(st,0); 
        for(int i = 1; i <= n; i++) printf("%d
    ",dis[i]);
        fclose(stdin); fclose(stdout);
        return 0;
    }
    

    T3 鸽子饲养

    题目描述

    小 K 养了 (n) 只鸽子,这 (n) 只鸽子可以看成平面直角坐标系内的 (n) 个固定的整点。

    同时,为了看住这些格子,小 K 可以在 (m) 个给定的点上选择其中的若干个点安装监视器。

    对于一只鸽子,它被监视当且仅当下面三个条件之一成立:

    • 这只鸽子的位置恰好和某个监视器重合。
    • 这只鸽子在其中两个位置不同的监视器连接形成的线段上。
    • 这只鸽子在三个不共线的监视器所构成的三角形内。

    现在小 K 要让所有鸽子都被监视,请问他最少需要选择多少个给定的位置设置监视器。

    数据范围:(nleq 10^5,mleq 500)

    solution

    简化题意:求一个最小的凸多边形,满足凸多边形的顶点为 (m) 个节点之一,且包含给定的 (n) 个点。

    先把给定的 (m) 个点,按极角排序。

    新建一个图,对于点 (i,j) 如果满足给定的 (n) 个点都在直线 (l_{i,j}) 的左侧,则 (i)(j) 连一条边。

    不难发现,求的最小凸多边形等价于新图中的最小环。

    最小环可以用 (Floyed) 求出来。

    现在问题转化为怎么判断 (n) 个点都在直线 (l_{i,j}) 的左侧。

    暴力做的复杂度是 (O(nm^2))

    但实际上我们只需要判断离 直线 (l_{i,j}) 最远的两个点是否在 (l_{i,j}) 的左侧。

    显然离直线 (l_{i,j}) 最远的点肯定在 (n) 个点构成的凸包上。

    把凸包分为上下两个凸壳,然后在凸壳上二分即可。

    横坐标最大/小的两个点要拿出来特判一下。

    复杂度:(O(nlogn+m^2logn+m^3))

    Code

    //太难写了,咕咕咕
    
  • 相关阅读:
    2.13 day 10
    2.12 day9
    Mongo基础知识
    给mongodb设置密码
    前端框架
    SecureCRT的一些设置
    node 服务器框架
    python 知识博客
    数据库记录
    有用的网站
  • 原文地址:https://www.cnblogs.com/genshy/p/14512070.html
Copyright © 2011-2022 走看看