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;
    }
  • 相关阅读:
    无法重用Linq2Entity Query
    The Joel Test
    MSBuilder directly instead of default VSComplie with keyborad shotcut 原创
    客户端缓存(Client Cache)
    关于代码重构和UT的一些想法,求砖头
    ExtJS2.0实用简明教程 应用ExtJS
    Perl information,doc,module document and FAQ.
    使用 ConTest 进行多线程单元测试 为什么并行测试很困难以及如何使用 ConTest 辅助测试
    史上最简单的Hibernate入门简介
    汽车常识全面介绍 传动系统
  • 原文地址:https://www.cnblogs.com/dowhile0/p/8688584.html
Copyright © 2011-2022 走看看