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;
}
原文地址:https://www.cnblogs.com/dowhile0/p/8688584.html