ICPC 2019-2020 North-Western Russia Regional Contest 补题

H. High Load Database

此题暴力可解。

时间复杂度证明:

对于每一个t来说,我们最多可以将其分为Σai/t 段区间,之后我们在每一段区间上进行二分,每次找到下一个区间的起点,复杂度是log(Σai),因为有 Σai/t段, 所以总时间复杂度就是 O(Σai/t * log(Σai))

这里我们把n定义为Σai。题目中已经给出 Σai≤1e6,所以n≤1e6。

如果我们考虑最坏的情况,m次询问中,t是从1递增到n的,那么总的时间复杂度就应该是(n + n/2 + n/3 + n/4 + ……+n/n) * logn(n),也就是n(1 + 1/2 + 1/3 + 1/4 + …… + 1/n) * log(n)
那么问题就在于(1 + 1/2 + 1/3 + 1/4 + …… + 1/n)这个式子有多大呢?

这个东西叫做调和数列求和,是一个历史性难题,没有公式可以求出准确的值,只有大概的值。

  • 当n有限时候:1+1/2+1/3+……+1/n=lnn,ln是自然对数。

  • 当n趋于无穷时:1+1/2+1/3+……+1/n=lnn+R。 R为欧拉常数,约为0.5772。

在本题中,n显然是有限的,所以总复杂度也就是ln(1e6) * 1e6 * log(1e6) ≈ 14 * 1e6 * 20 ,差不多2e8,时间是2s,所以可过。

  • 需要注意的细节:
    • 二分下一个区间的端点的时候,需要判断一下这个端点是不是上一次已经到过的端点,如果是的话,说明一直在同一个区间内跳跃,跳不出去了,这说明ai当中有数比x要大,就是不可能分成小于等于x的段的情况
    • 还有一个需要注意的点是记忆化答案,防止多次询问同种x的时候进行之前已经算过的运算。
    #include
    #define inf 0x3f3f3f3f
    #define INF 0x3f3f3f3f3f3f3f3f
    #define ll long long
    #define ull unsigned long long
    #define PII pair
    #define PLL pair
    #define PIL pair
    #define endl '\n'
    #define lson l, m, rt << 1
    #define rson m + 1, r, rt << 1|1
    #define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
    #define debug(x) cerr << #x <<" = "<< x << endl
    #define debug2(x, y) cerr << #x << " = " << x << " and " << #y << " = " << y << endl
    #define Pi acos(-1)
    using namespace std;

    const int mod = 1e9 + 7;
    const int N = 2e6 + 10, M = 1e6;

    int a[N], sum[N], ans[N];

    int main() 
    {
        int n, m;
        scanf("%d", &n);
        for(int i = 1; i <= n; ++ i) scanf("%d", &a[i]), sum[i] = sum[i - 1] + a[i];
        scanf("%d", &m);
        while(m --)
        {
            int x;
            scanf("%d", &x);
            if(ans[x] != 0)
            {
                if(ans[x] == -1) puts("Impossible");
                else printf("%d\n", ans[x]);
                continue;
            }
          	int d = 0, cnt = 1;
            int now = upper_bound(sum + 1, sum + 1 + n, d + x) - sum;
            d = sum[now - 1];
            while(now <= n) //保证搜索的范围不会超过n
            {
                int now2 = upper_bound(sum + 1, sum + 1 + n, d + x) - sum;
                if(now2 == now)  //如果上次二分已经到过这个点的话,说明存在ai > x。
                {
                    ans[x] = -1;
                    break;
                }
                cnt ++;  //段数+1
                d = sum[now2 - 1];
                now = now2;
             }
             if(ans[x] != -1) ans[x] = cnt, printf("%d\n", ans[x]);
             else puts("Impossible");
        }
        return 0;
    }


E - Equidistant

给定n个点n-1条边的树,求到m个点的距离相等的点

把m个点放进队列,每次bfs一层,找bfs相同层数时的有m个点可以走到的点。

#include
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define ull unsigned long long
#define PII pair
#define PLL pair
#define PIL pair
#define endl '\n'
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1|1
#define IOS std::ios::sync_with_stdio(false),cin.tie(0), cout.tie(0)
#define debug(x) cerr << #x <<" = "<< x << endl
#define debug2(x, y) cerr << #x << " = " << x << " and " << #y << " = " << y << endl
#define Pi acos(-1)
using namespace std;

const int mod = 1e9 + 7;
const int N = 5e5 + 10;

int n, m;
int h[N], ne[N], e[N], bj[N], belong[N], deep[N], idx; //deep表示当前层数,belong表示继承的点的个数。
vector<int> s;
int ans;

void add(int a, int b)
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx ++;
}

void bfs()
{
    queue<int> Q;
    for(int i = 0; i < s.size(); i ++)
        deep[s[i]] = 1, belong[s[i]] = 1, Q.push(s[i]);
    while(!Q.empty())
    {
        int head = Q.front();
        Q.pop();

        if(bj[head]) continue;
        bj[head] = 1;

        for(int j = h[head]; ~j; j = ne[j])
        {
            int to = e[j];
            if(deep[to] == 0 || deep[to] == deep[head] + 1) //如果当前点没有被走到过 或者 这个点是同一层的点走到过的点的话
            {
                belong[to] += belong[head]; //更新在同一层当中有多少个点能走到当前点
                deep[to] = deep[head] + 1;  //如果当前点没有走过的话,继承层数

                if(belong[to] == m) //如果同一层中有m个点可以走到当前点的话,说明当前点就是中心点
                {
                    ans = to;
                    return;
                }

                if(!bj[to])  
                    Q.push(to);
            }
        }
    }
    return;
}

int main() 
{
    IOS;
    memset(h, -1, sizeof h);
    cin >> n >> m;
    for(int i = 0; i < n - 1; i ++)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
        add(b, a);
    }
    for(int i = 1; i <= m; i ++)
    {
        int p;
        cin >> p;
        s.push_back(p);
    }

    bfs();

    if(n == 1) cout << "YES" << endl << 1 << endl;
    else 
    {
        if(ans)
            cout << "YES" << endl << ans << endl;
        else   
            cout << "NO" << endl;
    }
    return 0;
}






你可能感兴趣的:(ICPC 2019-2020 North-Western Russia Regional Contest 补题)