牛客周赛 Round 82


A. 夹心饼干

点击查看代码
void solve() {
    std::string s;
    std::cin >> s;
    if (s[0] == s.back()) {
    	std::cout << "YES\n";
    } else {
    	std::cout << "NO\n";
    }
}

B. 食堂大作战1.0 && C. 食堂大作战2.0

题意:有n个队伍,每个队伍有ai人,你要到每个队伍前面各去一次,每时刻每个队伍人数都减1,当你在这个队伍且队伍只有你一个人时你就到了这个队伍前面,然后你可以瞬移到另一个队伍后面。问能否可行以及给出方案。

不算自己的情况,如果一个队伍人数为0了,那么我们应该在之前排到了这个队伍后面。那么显然如果有两个队伍同时为0,我们无法兼顾这两个队伍。于是每个ai只能出现一次。如果每个ai只出现一次,那么我们显然可以按从小到大的顺序操作。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<std::pair<int, int>> a(n);
    for (int i = 0; i < n; ++ i) {
    	int x;
    	std::cin >> x;
    	a[i] = {x, i};
    }

    std::sort(a.begin(), a.end());
    for (int i = 0; i + 1 < n; ++ i) {
    	if (a[i].first == a[i + 1].first) {
    		std::cout << "NO\n";
    		return;
    	}
    }

    std::cout << "YES\n";
    for (int i = 0; i < n; ++ i) {
    	std::cout << a[i].second + 1 << " \n"[i == n - 1];
    }
}

D. 小苯的排列计数

题意:给出一个排列的前缀min数组,求有多少排列满足要求。

发现每次数字变化就代表这个数在这个位置上,那么我们从前往后遍历,找每一段相同的,这一段只有一个是固定的,其他只需要填大于这个数的数就可以了,假设[i,j]都是相同的,那么pi=ai[i+1,j]的位置选比ai大的数就行,不过之前以及填过了i1个大于ai的数,那么我们总共有nai(i1)个数可以选,总共有ji个位置,用组合数求即可,注意每个数可以任意排列,于是还要乘上一个ji的排列。
(代码使用了jiangly的取模类以及组合数板子)

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    for (int i = 1; i < n; ++ i) {
    	if (a[i] > a[i - 1]) {
    		std::cout << 0 << "\n";
    		return;
    	}
    }

    Z ans = 1;
    for (int i = 0; i < n; ++ i) {
    	int j = i;
    	while (j + 1 < n && a[i] == a[j + 1]) {
    		++ j;
    	}

    	int tot = n - a[i] - i;
    	ans *= comb.binom(tot, j - i) * comb.fac(j - i);
    	i = j;
    }

    std::cout << ans << "\n";
}

E. 和+和

题意:给你两个数组a,b,你要在a里选m个数,在b里选m个数,满足a里选的数的下标都小于b里选的数的下标,使得选出数的总和最小。

用优先队列预处理出[1,i]中从a里选m个元素的最小值,和[i,n]b里选m个数的最小值。那么就可以枚举i,取两边的值相加。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<int> a(n), b(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    for (int i = 0; i < n; ++ i) {
    	std::cin >> b[i];
    }

    const i64 inf = 1e18;
    std::priority_queue<int> heap;
    i64 sum = 0;
    std::vector<i64> pre(n, inf), suf(n, inf);
    for (int i = 0; i < n; ++ i) {
    	if (heap.size() == m) {
    		if (a[i] < heap.top()) {
    			sum -= heap.top();
    			heap.pop();
    			sum += a[i];
    			heap.push(a[i]);
    		}
    	} else {
    		heap.push(a[i]);
    		sum += a[i];
    	}

    	if (heap.size() == m) {
    		pre[i] = sum;
    	}
    }

    while (heap.size()) {
    	heap.pop();
    }

    sum = 0;
    for (int i = n - 1; i >= 0; -- i) {
    	if (heap.size() == m) {
    		if (b[i] < heap.top()) {
    			sum -= heap.top();
    			heap.pop();
    			sum += b[i];
    			heap.push(b[i]);
    		}
    	} else {
    		heap.push(b[i]);
    		sum += b[i];
    	}

    	if (heap.size() == m) {
    		suf[i] = sum;
    	}
    }

    i64 ans = inf;
    for (int i = 0; i + 1 < n; ++ i) {
    	ans = std::min(ans, pre[i] + suf[i + 1]);
    }

    std::cout << ans << "\n";
}

F. 怎么写线性SPJ

题意:构造一个长度为n的数组,满足值域都在[1,n]里,且任意一个子数组都至少有一个数在这个子数组里只出现过一次。求不同数字最少的方案。

手搓一下,长度为7的答案为1,2,1,3,1,2,1,长度为15的答案为1,2,1,3,1,2,1,4,1,2,1,3,1,2,1。发现是一个很对称的数组,并且不同数字只有log2n+1个,然后大胆猜这就是最小的种类数,于是就过了。
关于代码实现,可以用递归。也可以发现每个相同数之间的距离是固定了,两个x之间隔了2x的距离。

点击查看代码
void solve() {
    int n;
    std::cin >> n;

    std::vector<int> ans(n);
    int x = 0;
    for (int i = 0; i < n; ++ i) {
    	if (ans[i] == 0) {
    		++ x;
    		for (int j = i; j < n; j += 1 << x) {
    			ans[j] = x;
    		}

    	}
    }

    std::cout << x << "\n";
    for (int i = 0; i < n; ++ i) {
    	std::cout << ans[i] << " \n"[i == n - 1];
    }
}
posted @   maburb  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示