zoukankan      html  css  js  c++  java
  • SEERC2014训练日志

    solve 4  (D H A E)

    rank  29/72

    1.赛时ac的题在下面补充思路解法(思路较复杂可以贴代码);

    2.赛后补上的题在下面补充详细的思路,以及代码,要清晰;

    3.补到银牌题。

    A - Banks (通过)

    题意:通过多少步操作能把序列所有值都变成非负。

    思路:

    伪证明一下。对于一个负数a[pos],要把它变成正数,左右可能产生2个负数,也可能产生1个负数,也可能不产生负数。

    对于新产生的负数,我们发现,如果取之变成正数,那么a[pos]的值依旧是正数。(此处易证。)

    因此,要消除一个负数,同时消除掉他带来的新的负数,只需要经过较少次操作便可完成。

    可以暴力实现。

    B - Circle of digits

    C - UFO

    D - Frame (通过)

     <dzc>

    题意:给出n,m,k,问你能不能用长度为k的木条拼出n*m的矩形框来?

    思路:模拟即可,从某一个角开始一条边一条边的拼即可,每次用当前需要拼的边的长度对k取余,

    如果余数>1,那么一定不能拼成

    如果余数=1,那么可以继续拼下一条边,且下一条边是完整的

    如果余数=0,那么可以继续拼下一条边,且下一条边的长度是下一条边边长-1

    注意:需要分别以两个相邻顶点模拟,因为只有从对角的两个顶点开始铺才是等价的,相邻顶点并不等价,还有如果当前拼的是最后一条边,那么长度还要额外-1

    E - Points (通过)

    题意:给n个点,构造出一个凸包,使得n个点都严格在凸包里面,并且凸包的边只能是平行或垂直x轴,或与x轴夹角呈45°。

    思路:

    给定的每个点都要在凸包内部,那么我们可以把每个点的上下左右四个点记录下来,用它们构造一个凸包

    (注意点集要去重。)

    得到凸包后,有考虑边是否是平行或垂直x轴的边。

    (1) 平行或垂直x轴:这条边不需要重新构造。

    (2) 否则:这条线段可以转换成一条折线(夹角为135°)。

    F - Most Influential Pumpkin

    一、题意

      给一个数字$N$,一个是数字$K$,一个初始序列,$a_0, a_1, a_2, \cdots, a_{N-1}$;接下来,有$K$次修改操作,每次给一个区间$[L, R]$,表示将区间$[L, R]$之间的元素加$1$。然后输出整个序列的中位数。

    二、思路

      分块的思想

      把区间$[0, N)$分成$\sqrt{N}$个子区间,每个区间内$\sqrt{N}$个元素。当然,如果$N$不是完全平方数,要多用一个块,俗称桶子(下同)。对每个桶子排序,同时记录每个桶子内的整体加的值$sum[i]$,第一个大于中位数(一开始可以计算出来)的数字相对整个序列的位置(因为桶子内元素是有序的)$le[i]$。注意,序列的数据结构不能是int,因为对于每个数字,除了值之外,还需要记录它的初始下标。

      每次区间修改时,枚举所有与$[L, R+1)$相交的区间$[l, r)$,有两种情况:

        (1)如果$[l, r) \subset [L, R+1)$,直接将$[l, r)$整体$+1$;

        (2)如果$[l, r) \not\subset [L, R+1)$且$[l, r) \cap [L, R+1) \ne \varnothing$,暴力修改。

       对于情况(1),区间整体$+1$后该桶子内的序列仍然有序;对于情况(2),区间$[l, r)$内满足$L \le a_i \le R(l \le i < r)$的所有元素的值都增加了$1$,这将导致区间$[l, r)$无序,但是,这样的无序序列可以用$O(\sqrt{N})$的时间复杂度解决。

      修改完成之后,枚举每一个桶子,通过数组$le$统计每个桶子内不大于中位数的个数$cnt$(注意,统计的时候,$le[i]$也要随着改变。改变的时候,只需朴素的查找即可),如果$cnt \le \frac{N}{2}$,说明整个序列中最多只有$\frac{N}{2}$个数不大于中位数,而中位数必定是第$\frac{N+1}{2}$个小的数(从1开始记),所以,不大于中位数的元素个数至少为$\frac{N+1}{2}$。所以,说明,整个序列的中位数已经$+1$了,那么,把中位数++即可,然后输出。

      要注意的是,

        (1)题目中给定的修改区间的下标从$1$开始,而分块法中,下标要从$0$开始(当然也可以从$1$开始,但是,编码麻烦不止一点点),所以,要把修改区间左端点$L-1$;

        (2)暴力修改右边界所在的区间时要注意,最右端点是$min(N, r)$;

        (3)通过数组$le$统计每个桶子内不大于中位数的个数时,实际上统计的是$median - sum[i]$,因为第$i$个区间的元素$e$的真实值是$e + sum[i]$,而不是$e$。但是在区间整体修改的时候,区间内所有的元素并没有被修改。

    三、总结

      1、每个桶子内的容量$c = \lfloor\sqrt{N}\rfloor$,桶子的个数$n = \lceil\frac{N}{c}\rceil$;

      2、分块处理中,待修改区间的右边界所在区间的右端点是$min(N, r)$;

      3、void nth_element (RandomAccessIterator first, RandomAccessIterator nth, RandomAccessIterator last)函数的用法:

        参数一:序列初始位置;

        参数二:第(nth-first)小(从0开始记)的元素会被放置的位置;

        参数三:序列末位置(不包括);

        比如:把序列(不要求有序)$a_0, a_1, a_2, \cdots, a_{N-1}$的中位数(第$\frac{N}{2}$小)放在$\frac{N}{2}$位置:

          nth_element(a, a + N / 2, a + N);

        那么,$a[N/2]$就是整个序列中第$\frac{N}{2}$小的元素。

    四、源代码

    #include<bits/stdc++.h>
    using namespace std;
    #define mk(x, y) make_pair(x, y)
    #define MAXN 60010
    #define SQRTN 300
    #define INF 0x3f3f3f3f
    typedef pair<int, int> PII;
    int N, K, sum[SQRTN], le[SQRTN];
    PII a[MAXN], aux[MAXN];
    
    void update(int ul, int ur, int l, int r){
        for(int i = l;i < r;++i){
            if(a[i].second >= ul && a[i].second < ur)a[i].first++;
        }
        int pre = -1, idx;
        for(int i = l;i < r;++i){
            if(a[i].first < pre)swap(a[idx++], a[i]);
            else if(a[i].first > pre)idx = i;
            pre = a[i].first;
        }
    }
    
    int main() {
    //    freopen("input.txt", "r", stdin);
    //    freopen("output2.txt", "w", stdout);
        int cap, num, ul, ur, med;
        while(~scanf("%d%d", &N, &K)) {
            if(N == 0 || K == 0)continue;
            memset(sum, 0, sizeof(sum));
    
            cap = floor(sqrt(N));
            num = ceil(double(N) / cap);
            for(int i = 0; i < N; ++i)scanf("%d", &a[i].first), a[i].second = i;
            for(int i = 0; i < num; ++i)sort(a + i * cap, a + min(N, (i + 1) * cap));
            copy(a, a + N, aux);
            nth_element(aux, aux + N / 2, aux + N);
            med = aux[N / 2].first;
            for(int i = 0; i < num; ++i)le[i] = upper_bound(a + i * cap, a + min(N, (i + 1) * cap), mk(med, INF)) - a;
    
            while(K--) {
                scanf("%d%d", &ul, &ur);
                ul--;
                int b1 = ul / cap, b2 = ur / cap;
                for(int i = b1 + 1; i <= b2 - 1; ++i)sum[i]++;
                update(ul, ur, b1 * cap, min(N, (b1 + 1) * cap));
                if(b1 != b2)update(ul, ur, b2 * cap, min(N, (b2 + 1) * cap));
    
                int cnt = 0;
                for(int i = 0; i < num; ++i) {
                    int x = med - sum[i];
                    int& p = le[i];
                    while(p > i * cap && a[p - 1].first > x)--p;
                    while(p < min(N, (i + 1) * cap) && a[p].first <= x)++p;
                    cnt += p - i * cap;
                }
                //median has increased by one.
                if(cnt <= N / 2)++med;
                cout << med << endl;
            }
        }
        return 0;
    }

    G - Grammar

    H - Triples (通过)

    <qj>

    题意:求区间内有多少组(x,y,z)满足公式

    思路:

    预处理所有数字的高次方,注意对1e9+7取余。

    由于数据范围很小,暴力枚举所有的n,x,y,z,如果满足条件,答案+1 。

    I - Multi-Machine Scheduling of Two Applications

    J - Strange Antennas

    题意:在n*n的区域里有a个雷达站,雷达站覆盖区域是三角形,重叠覆盖信号会冲突,求多少点有信号。

    思路:

    由于n较大,a较小,不妨把三角形转换成若干条扫描线来考虑,同时,可以把a条线段离散化成a组 pair<l,r> 。

    这样我们就把问题的时间复杂度降低为O(n*a)了。

    这里还有一个小结论不难证明:

    当前扫描线上,我们用a组<l,r>可以把线段划分成若干个小块,可证:第2k+1块被奇数条线段覆盖,而第2k块则被偶数条线段覆盖。(k=0,1,2,3,...)

    (以上证明需要随便构造一组数据然后作图,很直观的。)

    所以直接把所有奇数段的长度求出来,求和。

    #include <bits/stdc++.h>
    using namespace std;
    
    struct T
    {
        int x,y,l,k;
    }ts[101],tt;
    
    int main(){
        int n,a,arr[222],ans;
        while(scanf("%d%d",&n,&a)!=EOF){
            for(int i=0;i<a;i++){
                scanf("%d%d%d%d",&ts[i].x,&ts[i].y,&ts[i].l,&ts[i].k);
            }
            ans=0;
            int cnt;
            for(int i=0;i<n;i++){
                cnt=0;
                for(int j=0;j<a;j++){
                    tt = ts[j];
                    if((tt.k&2)?(i>=tt.x||i<tt.x-tt.l):(i<tt.x||i>=tt.x+tt.l))continue;
                    arr[cnt++]=tt.y;
                    if(tt.k==0){
                        arr[cnt++]=max(tt.y-tt.l-tt.x+i,0);
                    } else if(tt.k==1){
                        arr[cnt++]=min(tt.y+tt.l+tt.x-i,n);
                    } else if(tt.k==2){
                        arr[cnt++]=min(tt.y+tt.l-tt.x+i+1,n);
                    } else {
                        arr[cnt++]=max(tt.y-tt.l+tt.x-i-1,0);
                    }
                }
                arr[cnt++]=n;
                sort(arr,arr+cnt);
                for(int j=1;j<cnt;j+=2){//取奇数段
                    ans+=arr[j]-arr[j-1];
                }
            }
            printf("%d\n",ans);
        }
        return 0;
    }
  • 相关阅读:
    springMVC 使用WebApplicationContext获取ApplicationContext对象
    idea for mac 最全快捷键整理
    装饰模式 应用场景和实现
    RabbitMQ基础知识详解
    jetty 介绍以及小例子
    业务对象的贫血模型与充血模型
    同构与异构
    Java设计模式之策略模式与状态模式
    C++之内部类(嵌套类)与外部类及友元
    深入理解Java中为什么内部类可以访问外部类的成员
  • 原文地址:https://www.cnblogs.com/dowhile0/p/8688584.html
Copyright © 2011-2022 走看看