zoukankan      html  css  js  c++  java
  • 2019.9.21模拟赛

    2019.9.21模拟赛

    T1:奇怪的队列

    类似蓝书《算法竞赛进阶指南》上树状数组一节中的一道例题。

    不难想到先把所有人按身高排序,这样问题会变得容易下手。

    身高按升序排序,然后依次为每个人在队列中安排位置。这样,假设我们已经安排到第x个人,他的身高和记住的数字分别是(a_x)(b_x),现在队列中所有空余的位置上的人的身高都大于(a_x),已经安排好的x-1个位置上的人的身高都小于(a_x)

    问题转化为:找一个位置,满足该位置没有被占用,且该位置之前或之后恰有(b_x)个空位。一个位置有没有被占用可以用0/1表示,1表示空位(没有被占用),0表示已经被占用。这样就可以用前/后缀和表示,若第x个人的位置(pos_x)之前恰有(b_x)个空位,且(pos_x)本身也是空位,那么该位置前缀和(sum_x=b_x+1),同理可用后缀和表示(pos_x)之后恰有(b_x)个空位的情况。但同时维护前后缀和比较麻烦,于是我们尝试用前缀和表示(pos_x)之后恰有(b_x)个空位的情况:前x-1个人已经安排好位置,现在还有n-x+1个空位,(pos_x)之后有(b_x)个空位,那么该位置的前缀和就是(sum_x=n-x+1-b_x)

    01序列前缀和单调不减,于是我们可以二分找到满足要求的(pos_x)位置,分(pos_x)前/后有(b_x)个人两种情况,题目要求字典序最小,我们又是按升序排序,所以当两个位置都可以的时候选择靠前的位置即可。

    注意我们每次安排好第x个人的位置,都会改变(pos_x)位置的0/1情况,即要求动态维护01序列的前缀和。我选择了用树状数组维护,每次需要二分(pos_x),对每次二分到的值在树状数组上查询前缀和,总复杂度(O(nlog^2n))

    标程是用线段树维护01序列,利用线段树结构可直接在线段树上二分,类似平衡树查第k大元素的写法,这样复杂度为(O(nlogn))

    Code:

    #include <bits/stdc++.h>
    using namespace std;
    typedef pair<int, int> pii;
    const int N = 2e5 + 5, inf = 0x3f3f3f3f;
    int n, q[N], c[N];
    pii p[N];
    void add(int p, int v) {
    	for(; p <= n; p += p & -p) c[p] += v;
    }
    int ask(int p) {
    	int ans = 0;
    	for(; p; p ^= p & -p) ans += c[p];
    	return ans;
    }
    int calc(int v) {
    	int l = 1, r = n, ans = n;
    	while(l <= r) {
    		int mid = (l + r) >> 1, res = ask(mid);
    		if(res >= v) ans = min(ans, mid), r = mid - 1;
    		else l = mid + 1;
    	}
    	return ask(ans) == v ? ans : inf;
    }
    int main() {
    	scanf("%d", &n);
    	for(int i = 1; i <= n; ++i) {
    		scanf("%d%d", &p[i].first, &p[i].second);
    		add(i, 1);
    	}
    	sort(p + 1, p + n + 1);
    	for(int i = 1; i <= n; ++i) {
    		int pos = min(calc(p[i].second + 1), calc(n - i + 1 - p[i].second));
    		if(pos == inf) return puts("impossible"), 0;
    		q[pos] = p[i].first;
    		add(pos, -1);
    	}
    	for(int i = 1; i <= n; ++i) printf("%d ", q[i]);
    	return 0;
    }
    

    T2:质数

    首先考虑单组询问的情况

    注意到询问上界R=1e7,可以直接用bool数组标记符合要求的数。质数可以直接(O(n))线性筛求出,对于两个质数的乘积,可以在求出的质数数组中两两枚举相乘进行标记。分析复杂度:看似两重循环,实际上内层循环在两数乘积大于R就break,所有枚举到的乘积都是合法的,且每两个质数的乘积只会被枚举一次。所以复杂度不会超过(O(n))

    对于多组询问,对bool数组(O(n))求出前缀和数组,每次(O(1))回答即可。

    Code:

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e7 + 5;
    int T, n, tot, l[N], r[N], p[N], sum[N];
    bool mark[N], vis[N];
    void prime() {
    	for(int i = 2; i <= n; ++i) {
    		if(!vis[i]) p[++tot] = i;
    		for(int j = 1; j <= tot && i * p[j] <= n; ++j) {
    			vis[i * p[j]] = true;
    			if(i % p[j] == 0) break;
    		}
    	}
    }
    int main() {
    	scanf("%d", &T);
    	for(int i = 1; i <= T; ++i) {
    		scanf("%d%d", &l[i], &r[i]);
    		n = max(n, r[i]);
    	}
    	prime();
    	for(int i = 1; i <= tot; ++i) {
    		mark[p[i]] = true;
    		for(int j = i; j <= tot; ++j) {
    			if(1ll * p[i] * p[j] > n) break;
    			mark[p[i] * p[j]] = true;
    		}
    	}
    	for(int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + mark[i];
    	for(int i = 1; i <= T; ++i) printf("%d
    ", sum[r[i]] - sum[l[i] - 1]);
    	return 0;
    }
    

    T3:好文章

    字符串算法基本忘光了,只好打hash了。

    思路很简单,对整个串hash,之后把每m个字符的hash值塞入set中,最后输出set的size,复杂度(O(nlogn))

    数据较大,且可能经过构造,自然溢出会被卡。可以用双模数hash,选两个大质数和底数分别hash,最后比较pair类型。

    最初写的时候函数传入参数是bool类型区分两个模数,结果传参时写成1和2,相当于单模数hash,WA了一整页...

    Code:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<ll, ll> pll;
    const int N = 8e6 + 5;
    const ll mod1 = 1e9 + 7, mod2 = 1e9 + 9, base1 = 131, base2 = 31;
    int n, m;
    ll hash1[N], hash2[N], fac_mod1[N], fac_mod2[N];
    char s[N];
    set<pll> U;
    ll ask(int l, int r, bool op) {
    	if(op == 1) return ((hash1[r] - hash1[l - 1] * fac_mod1[r - l + 1] % mod1) % mod1 + mod1) % mod1;
    	else return ((hash2[r] - hash2[l - 1] * fac_mod2[r - l + 1] % mod2) % mod2 + mod2) % mod2;
    }
    int main() {
    	scanf("%d%d%s", &n, &m, s + 1);
    	fac_mod1[0] = fac_mod2[0] = 1;
    	for(int i = 1; i <= n; ++i) {
    		fac_mod1[i] = fac_mod1[i - 1] * base1 % mod1;
    		fac_mod2[i] = fac_mod2[i - 1] * base2 % mod2;
    	}
    	for(int i = 1; i <= n; ++i) {
    		hash1[i] = (hash1[i - 1] * base1 % mod1 + s[i] - 'a' + 1) % mod1;
    		hash2[i] = (hash2[i - 1] * base2 % mod2 + s[i] - 'a' + 1) % mod2;
    	}
    	for(int i = 1; i + m - 1 <= n; ++i)
    		U.insert(make_pair(ask(i, i + m - 1, 1), ask(i, i + m - 1, 0)));
    	cout << U.size();
    	return 0;
    }
    
  • 相关阅读:
    弗尤博客(二)
    弗尤博客(一)
    第一系列完
    C# 关闭子窗体释放子窗体对象问题
    C#设置IE代理
    C# 计算位置居中
    C# 绘图
    From传值
    pictureBox绑定Base64字符串
    C# 绘制圆角矩形
  • 原文地址:https://www.cnblogs.com/yu-xing/p/11579951.html
Copyright © 2011-2022 走看看