Codeforces Round #207 (Div. 1) A. Knight Tournament
题意:
-
n个骑士(编号1-n),有m场battle,保证每一场battle的人数>=2。给出m场battle的信息:每一场battle的信息是:给出一个区间[l, r],再给出一个该场battle的胜利者编号won,意思就是说[l, r]中还没有被打败的骑士都会在这一场被won打败了。问,输出1-n的每一个骑士是被哪个骑士打败的,其中赢得最后胜利的骑士输出0。
思路1:用线段树维护区间是被谁打败的。初始化肯定是0。然后输入一个区间,如果这个区间 还没有被打败过(lazy[rt] == 0)就将这个区间的lazy标记为won。pushdown更新儿子的时候也是要判断lazy[lsn]和lazy[rsn]是不是为0,为0才更新为lazy[rt],否则不更新。
//280ms
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)
#define MID (l + r ) >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll maxN = 3e5 + 5;
int n, m;
int lazy[maxN << 2];
void init() { memset(lazy, 0, sizeof(lazy)); }
void pushdown(int rt, int l, int r)
{
if(lazy[rt])
{
int mid = MID;
if(! lazy[lsn])
lazy[lsn] = lazy[rt];
if(! lazy[rsn])
lazy[rsn] = lazy[rt];
lazy[rt] = 0;
}
}
void update(int rt, int l, int r, int ql, int qr, int val)
{
if(ql <= l && qr >= r)
{
if(!lazy[rt])
lazy[rt] = val;
return;
}
pushdown(rt, l, r);
int mid = MID;
if(qr <= mid)
update(QL, val);
else if(ql > mid)
update(QR, val);
else { update(QL, val); update(QR, val); }
}
int query(int rt, int l, int r, int dot)
{
if(l == r)
return lazy[rt];
pushdown(rt, l, r);
int mid = MID;
if(dot <= mid)
return query(Lson, dot);
else if(dot > mid)
return query(Rson, dot);
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
init();
while(m -- )
{
int l, r, won;
scanf("%d%d%d", &l, &r, &won);
if(l <= won - 1)
update(1, 1, n, l, won - 1, won);
if(won + 1 <= r)
update(1, 1, n, won + 1, r, won);
}
for(int i = 1; i <= n; i ++ )
printf("%d%c", query(1, 1, n, i), " \n"[i == n]);
}
return 0;
}
/*
8 1
1 8 8
*/
/*
300000 10
1 10 1
1 100 1
1 1000 1
1 10000 1
1 100000 1
1 200000 1
1 200020 1
1 200700 1
1 299999 1
1 300000 300000
*/
思路2:倒序覆盖。其实倒着覆盖,就是一个染色问题。
//265ms
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)
#define MID (l + r ) >> 1
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 3e5 + 5;
int n, m;
struct node{
int l, r, won;
node(int a = 0, int b = 0, int c = 0): l(a), r(b), won(c) {}
}info[maxN];
int tree[maxN << 2];
void build_tree(int rt, int l, int r)
{
tree[rt] = 0;
if(l == r) return;
int mid = MID;
build_tree(Lson);
build_tree(Rson);
}
void pushdown(int rt)
{
if(tree[rt])
{
tree[lsn] = tree[rt]; tree[rsn] = tree[rt];
tree[rt] = 0;
}
}
void update_range(int rt, int l, int r, int ql, int qr, int val)
{
if(ql <= l && qr >= r)
{
tree[rt] = val;
return;
}
pushdown(rt);
int mid = MID;
if(qr <= mid) update_range(QL, val);
else if(ql > mid) update_range(QR, val);
else { update_range(QL, val); update_range(QR, val); }
}
int query(int rt, int l, int r, int dot)
{
if(l == r) return tree[rt];
pushdown(rt);
int mid = MID;
if(dot <= mid) return query(Lson, dot);
else if(dot > mid) return query(Rson, dot);
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
for(int i = 1; i <= m; i ++ )
scanf("%d%d%d", &info[i].l, &info[i].r, &info[i].won);
build_tree(1, 1, n);
for(int i = m; i > 0; i -- )
{
if(info[i].won - 1 >= info[i].l)
update_range(1, 1, n, info[i].l, info[i].won - 1, info[i].won);
if(info[i].won + 1 <= info[i].r)
update_range(1, 1, n, info[i].won + 1, info[i].r, info[i].won);
}
for(int i = 1; i <= n; i ++ )
printf("%d%c", query(1, 1, n, i), " \n"[i == n]);
}
return 0;
}
/*
8 1
1 8 8
*/
/*
300000 10
1 10 1
1 100 1
1 1000 1
1 10000 1
1 100000 1
1 200000 1
1 200020 1
1 200700 1
1 299999 1
1 300000 300000
*/
乱七八糟的思路:
-
最开始有想用set,但是马上就把这个想法否了(觉得一定会T的,确实也没想好到底是怎么处理吧。后来用set做了,但是不靠百度就不可能会过的,不会set的遍历,迭代器不会用。ps:话说回来我都还没有学过c++, www)
乱七八糟的正经第一个代码思路:
线段树离线查询算法。先将所有的信息存起来。然后倒序覆盖回去(正序的话显然有点诡异,因为区间中包括的骑士不全是没有被打败的,所以如果正序的话,后面更新还要记录之前已经被打败的骑士,麻烦)。倒序覆盖的话就不会影响最终结果。
所以这道题其实是更新一个区间中所有的点的值都为相同的won,但是其实区间并没有维护什么东西。所以开始我就有点迷,不知道该用线段树维护什么。最后就想,反正区间值没有什么实际意义也用不到,不如就最简单的求和吧。然后就开写了。
写完第一份代码,WA 7,(我这个人就特别不喜欢造样例,于是debug是一个非常自我折磨的过程www不知道错哪里),后来还是造了一个样例就代码下边第一个样例。发现query的时候没有pushdown。【但是不知道为什么手残把lazy的数组开了一倍,本来还是开四倍的,然后不知道为什么什么东西驱使我让我给删了】
然后交了第二份代码,WA 11,就更不知道哪里错了www。
试了很多样例都没错(因为造的都很简单www),然后就又试了代码下边第二个样例,发现跑不出来。然后调了一下发现是爆了int,3e5*3e5肯定爆了啊qaq,于是索性就所有数据类型都改了long long,然后就过了第三份代码。
线段树具体实现
初始化当然都是0,都是winner。也就是线段树建树每个结点为0。我们倒序更新所给的区间都为对应的won。把所有区间都更新完单个结点的值就是最终的答案。
【我……好菜,根本就不用求和,根本不知道在做什么,稍微卡一下空间我比赛就GG了……www,怪我上个寒假没好好学线段树染色问题。我都不知道!之前也做过一个倒序覆盖的问题,还不知道原来就是染色问题wwww】
//514ms//这个代码真的不用看了……//完全浪费了一个tree[]数组的空间
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)
#define MID ((l + r ) >> 1 )
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll maxN = 3e5 + 5;
ll n, m;
struct node{
ll s, e, won;
node(ll a = 0, ll b = 0, ll c = 0) : s(a), e(b), won(c) {}
}info[maxN];
ll tree[maxN << 2], lazy[maxN << 2];
void init() { memset(lazy, 0, sizeof(lazy)); }
void pushup(ll rt) { tree[rt] = tree[lsn] + tree[rsn]; return; }
void pushdown(ll rt, ll l, ll r)
{
if(lazy[rt] != 0)
{
ll mid = MID;
tree[lsn] = lazy[rt] * (mid - l + 1);
tree[rsn] = lazy[rt] * (r - mid);
lazy[lsn] = lazy[rt];
lazy[rsn] = lazy[rt];
lazy[rt] = 0;
}
}
void build_tree(ll rt, ll l, ll r)
{
if(l == r) { tree[rt] = 0; return ;}
ll mid = MID;
build_tree(Lson);
build_tree(Rson);
pushup(rt);
}
void updata_range(ll rt, ll l, ll r, ll ql, ll qr, ll val)
{
if(ql <= l && qr >= r)
{
tree[rt] = val * (r - l + 1);
lazy[rt] = val;
return;
}
pushdown(rt, l, r);
ll mid = MID;
if(qr <= mid)
updata_range(QL, val);
else if(ql > mid)
updata_range(QR, val);
else { updata_range(QL, val); updata_range(QR, val); }
pushup(rt);
}
ll query(ll rt, ll l, ll r, ll dot)
{
if(l == r)
return tree[rt];
pushdown(rt, l, r);
ll mid = MID;
if(dot <= mid)
return query(Lson, dot);
else if(dot > mid)
return query(Rson, dot);
}
int main()
{
while(~scanf("%lld%lld", &n, &m))
{
init();
for(ll i = 1; i <= m; i ++ )
scanf("%lld%lld%lld", &info[i].s, &info[i].e, &info[i].won);
build_tree(1, 1, n);
for(ll i = m; i > 0; i -- )
{
if(info[i].s <= info[i].won - 1)
updata_range(1, 1, n, info[i].s, info[i].won - 1, info[i].won);
if(info[i].won + 1 <= info[i].e)
updata_range(1, 1, n, info[i].won + 1, info[i].e, info[i].won);
}
for(ll i = 1; i <= n; i ++ )
printf("%lld%c", query(1, 1, n, i), " \n"[i == n]);
}
return 0;
}
/*
8 1
1 8 8
*/
/*
300000 10
1 10 1
1 100 1
1 1000 1
1 10000 1
1 100000 1
1 200000 1
1 200020 1
1 200700 1
1 299999 1
1 300000 300000
*/
下边两个都是set的做法。确实快,O(n)。
思路就是,将所有1-n插入set。然后每输入一个区间,就把整个区间里的值erase掉,并且存ans[] = won,完了之后就再将won插入set。直到跑完所有的区间,那么set里还剩余的那个就是final winner,将它的ans[]赋值为0.
除了迭代器的使用之外,还有一个erase的诡异用法。
-
如果erase了一个it指向的内容,那这个迭代器也被删了。所以要在被删之前保存一下it。那当然简单又高大上的处理就是st.erase(iter ++ );
-
还有一种处理是iter = st.erase(iter);. 因为删除了iter指向的内容之后就会指向下一个元素的地址。所以就是直接更新iter的位置为下一个元素的地址即可。
-
vector的迭代器和这个又不一样。对于vector来说,删除了一个元素之后,下一个元素会移动到被删元素原来的位置,也就是iter是不变的。
//343ms
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)
#define MID ((l + r ) >> 1 )
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 3e5 + 5;
int n, m;
int ans[maxN];
int main()
{
while(~scanf("%d%d", &n, &m))
{
memset(ans, 0, sizeof(ans));
set<int>st;
set<int>::iterator iter;
for(int i = 1; i <= n; i ++ )
st.insert(i);
for(int i = 1; i <= m; i ++ )
{
int l, r, won;
scanf("%d%d%d", &l, &r, &won);
iter = st.lower_bound(l);
while(iter != st.end() && *iter <= r)
{
ans[*iter] = won;
iter = st.erase(iter);//删除元素之后指向下一个元素的位置
//st.erase(iter ++ );也可。解释:删除该迭代器指向的内容之后iter被删除,所以在删除指针之前对其做一个备份
//如果是vector那么删除元素后,后面的元素自动前移,不用挪动指针也就是:vt.erase(it);
}
st.insert(won);
}
int won = *st.begin();
ans[won] = 0;
for(int i = 1; i <= n; i ++ )
printf("%d%c", ans[i], " \n"[i == n]);
}
return 0;
}
为了避免上边erase的iter的诡异变化,就直接删除区间。(我才知道可以删除区间,tcl)
//358ms
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define INF 0x3f3f3f3f
#define lowbit(x) x & (-x)
#define MID ((l + r ) >> 1 )
#define lsn rt << 1
#define rsn rt << 1 | 1
#define Lson lsn, l, mid
#define Rson rsn, mid + 1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxN = 3e5 + 5;
int n, m;
int ans[maxN];
int main()
{
while(~scanf("%d%d", &n, &m))
{
memset(ans, 0, sizeof(ans));
set<int>st;
set<int>::iterator iter;
for(int i = 1; i <= n; i ++ )
st.insert(i);
for(int i = 1; i <= m; i ++ )
{
int l, r, won;
scanf("%d%d%d", &l, &r, &won);
iter = st.lower_bound(l);
while(iter != st.end() && *iter <= r)
{
ans[*iter] = won;
iter ++;
}
st.erase(st.lower_bound(l), iter);
st.insert(won);
}
int won = *st.begin();
ans[won] = 0;
for(int i = 1; i <= n; i ++ )
printf("%d%c", ans[i], " \n"[i == n]);
}
return 0;
}