Educational Codeforces Round 41(已补D,E)

D. Pair Of Lines

题目大意:

\(n\)个点,问是否存在两条直线(可以为同一条)能覆盖所有的点。

解题思路:

首先一个或者两个或者三个点都是肯定可以用两条线覆盖的。
如果点的个数大于等于三个,那么我们已经可以确定至少一条线的:
点1和点2可能在一条线上,点2和点3可能在一条线上,点3和点1可能在一条线上。
我们试着确定其中一条线后,去掉在线上的所有点,剩下的点肯定在另一条线上,这样利用剩下的点就可以确定另一条直线。
用到一个公式:\(abc\)三点共线\((c - a)\times(c - b)=0\)

E. Tufurama

题目大意:

\(n\)个数的数列\(a\),问有多少对下标\(x,y(x<y)\),使得\(a[x]\geq y,a[y]\geq x\)

解题思路:

大体思路是先遍历一遍数组,求出\(a[y]\geq x\)中最大的下标\(x\)
然后再遍历一遍数组,用树状数组求出\(a[x]\geq y\)的所有\(x\)
最大可能下标可以这样求:\(x=max\{a[y],y-1\}\)
为什么要求最大呢?因为是为了配合树状数组的求和,这样就不会重复。
每次求到的最大下标\(x\)对应的\(y\)可以用\(vector\)保存。
因为每个\(y\)只能出现一次,所以时间复杂度为\(O(nlogn)\)

#include <bits/stdc++.h>
#define N 200010

int n, a[N];

struct bit {
    static const int maxn = N;
    int f[maxn];
    inline void init() { std::fill(f, f + maxn, 0); }
    bit() { init(); }
    inline void add(int pos, int inc) {
        for (; pos < maxn; pos += (pos & -pos)) f[pos] += inc;
    }
    inline int sum(int rg) {
        if (rg < 1) return 0;
        int ans = 0;
        for(; rg; rg -= (rg & -rg)) ans += f[rg];
        return ans;
    }
    inline int sum(int l, int r) {
        return sum(r) - sum(l - 1);
    }
}mybit;

std::vector<int> mpos[N];

int main() {
    while (std::cin >> n) {
        for (int i = 0; i <= n; i++) mpos[i].clear();
        for (int i = 1; i <= n; i++) {
            std::cin >> a[i];
            a[i] = a[i] > n ? n : a[i];
            mpos[std::min(a[i], i - 1)].push_back(i);
        }
        mybit.init();
        long long ans = 0;
        for (int i = 1; i <= n; i++) {
            mybit.add(a[i], 1);
            for (int x: mpos[i]) ans += mybit.sum(x, n);
        }
        std::cout << ans << '\n';
    }
    return 0;
}
posted @ 2018-04-20 20:27  啊嘞  阅读(128)  评论(0编辑  收藏  举报