7/14 栓q,理解题意真的很难。
E.SPOJ.com - Problem CLFLARR
思路:
1.线段树维护
2.小技巧,输出的时候倒叙遍历卡出当前数所在的最后一个区间,若不属于任何区间则输出0。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
struct node {
int r, l, c;
} arr[N];
void work() {
int n, q;
cin >> n >> q;
for (int i = 1; i <= q; ++i) {
cin >> arr[i].l >> arr[i].r >> arr[i].c;
}
for (int i = 1; i <= n; ++i) {
bool flag = 0;
for (int j = q; j > 0; --j) {
if (i <= arr[j].r && i >= arr[j].l) {
flag = 1;
cout << arr[j].c << "\n";
break;
}
}
if (!flag)
cout << "0" << "\n";
}
}
signed main() {
io;
work();
return 0;
}
G.C - Distinct Numbers (atcoder.jp)
题目注意点:
1. 每次操作的是最大的数。2. arr 数列不允许重复元素(pairwise distinct,两两不同)
思路:
前置知识:
公平组合游戏(ICG):
(1)游戏规则对两个玩家公平
(2)游戏状态有限,能走的步数也有限
(3)轮流走,当一个玩家不能走时判负
(4)游戏的局势不能区分玩家的身份,例如黑白棋就不行
小结论:
给定初始局势,指定先手玩家,如果双方都采取最优策略,那么获胜者已经确定了,即ICG问题存在必胜策略。引理1:存在一个情况使得0~n-1中有k个空位,其余k个数从n开始连续。在该情况下,k为奇数时我必胜,否则我必败。
- 证明:递推,每一个状态都由前一个状态推来,我可以进行一步操作使对手面对k-1局面,此时我的状态为 我面临k局面 的反状态。由此推到1,易知我必胜,于是后面状态都已知。
引理2:存在一个情况使得0~n-1中有2个空位,末尾两个数连续。在该情况下,arr[n]-(n-1)为奇数时我必胜,否则我必败。
- 证明:1. 注意到当末尾两数为非连续的时候,当前的人可以控制将arr[ n ]放在arr[ n-1 ]前或后,当前人只需贴住对手即可制造必赢局面,所以当两人绝对聪明的时候必定是每次操作都让arr[ n ]=arr[ n-1 ] -1。
- 证明:2. 在1成立的条件下,注意到操作次数必为(arr[n]-n)/2+(arr[n-1]-n-1)/2+2或(arr[n]-n-1)/2+(arr[n-1]-n)/2+2,此时arr[n]=arr[n-1]+1,即得arr[n]-(n-1),于是操作数为奇数时我必胜。
加强引理2:存在一个情况使得0~n-1中有k个空位,末尾两个数连续。在该情况下,arr[n]-(n-1)为奇数时我必胜,否则我必败。
- 证明:在引理2成立的条件下,我们可以换一个角度思考arr[n]-(n-1)得数的算法,该得数说明了一轮游戏将遍历arr[ n ] ~ 0的所有数,且【题目注意点】保证了不会存在一个点被多次经过(*),于是拓展为当前情况,可以发现距离只与arr[n]和(n-1)有关,与其他点所在的位置无关。
- 证明(*):反证法思考,如果一个点被多次遍历,那说明在当前最大值移动之前,该点已被移走,此时与【题目注意点1】矛盾。
- 一些多余的思考(手推理解):当连续的末尾两数遇到倒数第三个数时,最大值可以移动到之前的任意地方,维持两数相连的对峙局面或保持三数相连(当前状态)。
证明:
由以上引理可知,必胜的方法为当我面临末尾两数连续时,arr[n]-(n-1)为奇数。
对于每局游戏都有以下几种可能:
1.末尾两数不连续,即arr[ n ] > arr[ n-1 ] + 1,此时我们需要保证创造自己的必胜局面,即判断arr[ n-1 ]的位置制造连续末尾,由于胜负由距离的奇偶性决定,而我们此时可以控制距离的奇偶性(即将arr[ n ]放于arr[ n-1 ]+1位或arr[ n-1 ]左边*),由此我们必胜。
*:若arr[ n-2 ]与arr[ n-1 ]连续则可放于左边任意位,否则放于arr[ n-1 ]-1位。
2.末尾两数连续:由【加强引理2】易得胜负情况。
这个博弈我写的真的很认真!!!我想了很久很久,偌大的优快云竟无一篇博客能解决我思路上的问题,看了两篇都是先假设再假设(我觉得有点伪证,也或许是我笨思路不通顺)。
其实做题者是不需要想明白必胜策略的,我们只需要知道面对某种情况我是否存在必胜策略即可,这也是其他题解假设可以成立的依据,这个思想还是比较重要的,但我还是习惯于把逻辑捋通顺,不然忘得很快,这题懂了下一题也不会做。
博弈是极好极好的,只是我不会搞!
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define pb push_back
#define mem(a,b) memset(a,b,sizeof a)
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
int arr[N];
void work() {
int n;
cin >> n;
for (int i = 0; i < n; ++i) {
cin >> arr[i];
}
sort(arr, arr + n);
if (arr[n - 1] - arr[n - 2] > 1 || (arr[n - 1] - n + 1) % 2 != 0) {
cout << "Alice\n";
return;
}
cout << "Bob\n";
}
signed main() {
io;
work();
return 0;
}
I.Problem - 1474A - Codeforces
思路:
贪心,判断局部最优即可达到整体最优。注意首位必为1。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
#define int ll
#define inf 0x3f3f3f3f3f3f3f3f
const int N = 3e5 + 50;
void work() {
int n;
cin >> n;
string b;
cin >> b;
int cur = b[0] - '0' + 1;
cout << "1";
for (int i = 1; i < n; ++i) {
if (b[i] == '0') {
if (cur == 1) {
cout << "0";
cur = 0;
} else {
cout << "1";
cur = 1;
}
} else {
if (cur == 2) {
cout << "0";
cur = 1;
} else {
cout << "1";
cur = 2;
}
}
}
cout << '\n';
}
signed main() {
io;
int t;
cin >> t;
while (t--) {
work();
}
return 0;
}
J.F - Two Spanning Trees (atcoder.jp)
题意:
给一个无向无环图,要求以1为根节点建两棵树:
T1:对于每一个不属于T1的边,该边的两个端点必存在子孙关系。
T2:对于所有不属于T2的边,该边两个端点都不存在子孙关系。
思路:
T1是dfs树:深搜不是很好理解,但我们可以用反证法的思想想一下,当存在一个不属于dfs树的边,有几种情况:
- 回边(返祖边),此时存在子孙关系。
- 边的两个端点分别在树的两支上,此时说明出现了环,不成立。
这里推荐一个讲dfs树的博客(原博在CF,这是翻译),可以看看那个图DFS 树_malanlllll的博客-优快云博客_dfs树
T2是bfs树:广搜很好理解,对于每一个点在都会遍历到他所有的边并建立子孙关系,故所有边都会建立子孙关系。
#include <bits/stdc++.h>
using namespace std;
#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
//#define int ll
#define pb push_back
#define inf 0x3f3f3f3f
const int N = 3e5 + 50;
vector<int>aa[N];
void add(int a, int b) {
aa[a].pb(b);
aa[b].pb(a);
}
int vis[N];
void dfs(int a) {
vis[a] = 1;
for (int i = 0; i < aa[a].size(); ++i) {
if (!vis[aa[a][i]]) {
cout << a << " " << aa[a][i] << '\n';
dfs(aa[a][i]);
}
}
}
queue<int>q;
void bfs(int a) {
memset(vis, 0, sizeof vis);
q.push(a);
vis[a] = 1;
while (!q.empty()) {
int cur = q.front();
q.pop();
for (int i = 0; i < aa[cur].size(); ++i) {
if (!vis[aa[cur][i]]) {
q.push(aa[cur][i]);
cout << cur << " " << aa[cur][i] << '\n';
vis[aa[cur][i]] = 1;
}
}
}
}
void work() {
int n, m;
cin >> n >> m;
for (int i = 0; i < m; ++i) {
int a, b;
cin >> a >> b;
add(a, b);
}
dfs(1);
bfs(1);
}
int main() {
io;
work();
return 0;
}
有时间再看看dp吧,倦了。