tg 82 solution
T1
对于每个包,时间复杂度要求\(O(n)\)或\(O(n\log n)\)
Subtask 1
这个包考虑\(O(n\log n)\)的二分就可以过,下面会讲
Subtask 2
考虑充分利用"连续"这个性质
事实上我们考虑查\(i\)和\(i-1\),相同就没事,否则开个新连续段
Subtask 3
\(cnt\leq 3,n\leq 1000,Q\leq 2000\)
这一部分的做法显然不能\(O(n\log n)\),考虑\(O(n)\)
注意到\(cnt\leq 3\),我们对于每一种颜色,记录它上一个出现的位置,记为\(last\)
对于每个\(i\),左端点定\(last\)从后往前扫,如果查询颜色数同\(i-1\)那说明\(i\)颜色同左端点
否则接着扫就完了,扫完记得更新\(last\)
询问\(3\cdot n\),最后一个\(last\)没必要扫,询问\(2\cdot n\)
Subtask 4
上面的东西询问变成\(3\cdot n\),对上面的东西做个优化
手动二分一下,倒着按\(3,2,4,1\)的顺序,不写很详细了
然后就没有然后了,对于每个点每个分支至多查\(2\)次
查询\(2\cdot n\)
这一部分没讲太细,所以放码了
点击查看代码
struct node{
int id,val;
bool operator<(node y){return id<y.id;}
}l[o];
int Query(int l,int r){
if(q[l].find(r)!=q[l].end())return q[l][r];
return q[l][r]=query(l,r);
}
void solve4(int n){
memset(l, 0, sizeof(l));
l[1].val = l[1].id = 1;
l[2].val = 2; l[3].val = 3; l[4].val = 4;
memset(c, 0, sizeof(c));
c[1] = 1;
ans.emplace_back(1);
for(int i=2;i<=n;i++){
sort(l + 1,l + 5);
if(query(max(l[3].id, 1),i) == 3){
if(query(max(1, l[2].id), i) == 3){
c[i] = l[2].val;
l[2].id = i;
}
else{
c[i] = l[1].val;
l[1].id = i;
}
}
else{
if(query(max(1, l[4].id), i) == 1){
c[i] = l[4].val;
l[4].id = i;
}
else{
c[i] = l[3].val;
l[3].id = i;
}
}
ans.emplace_back(c[i]);
}
// for(int i=1;i<=n;i++)ans.push_back(c[i]);
}
Subtask 5
出题人不会放出\(cnt=n\)的数据的
如果有,那直接\([1,n]\)挨个装一遍
否则,找到第一个重的位置,然后直接把重掉的位置换掉
如果重复,会有\([1,pos]\)的数量不是\(pos\)
扫第二遍重复的时候就是把\(1\)换成\(i\),重复就是区间数量不等于\(pos-i+1\)
查询次数\(2\cdot n\)
Subtask 6
这个放给暴力
Subtask 7
这个时候暴力废了,二分就完事了
如果端点一定,那区间长度增大,查询结果单调不降
考虑二分找到上一个和它相同的点
如果在\(mid\)位置,查询\([mid,i-1]\)结果小于\([mid,i]\)说明应该左移,否则右移
如果直接查询的话,询问\(2\cdot n\log n\)
考虑去掉常数,因为\([mid,i-1]\)这一段是知道的,所以直接在已有信息上暴扫就好了
花费在暴扫上的时间复杂度\(O(n^2\log n)\),查询\(n\log n\)
T2
把连续段缩到一起,问题可以有很简洁的表述方式。
对于长度为\(n\)的序列\(a\),每个位置还有一个值\(c\)表示连续段长度。
每次选择\(i<j\)且\(a[i]=a[j]\),操作是--c[i],++c[j]
。
每种颜色是独立的,只用考虑可以得到哪些c,这个可以用前缀和限制。
问题转化成了普通的子序列匹配问题。
T3
没有细想,直接看沈老师题解
问题明显是构造出连通图。
连通图至少有\(n-1\)条边,所以奇数的段不能超过两个。
这个就是存在解的充要条件。
把奇数段放在头尾,构造\(a[1]-1,a[2],a[3],a[m]+1\)即可,
画个图很清楚,好像这样构造出来是一条链。