Lijinrui299792458

不做沉底的木做举重若轻的船。

NOI Online 2022 游记

T1 丹钓战

水题,直接单调栈(谐音梗)。

考虑从 $[l,n]$ 转移到 $[l-1,n]$ ,注意到除了 $l$,只有在前一区间中出现在了栈顶,才有可能在新区间中出现在栈顶。维护在区间中所有出现在 $S$ 栈顶的元素,考虑 $l-1$ 对其影响:从前至后弹出不能导致 $l-1$ 弹出的元素,直到一个元素弹出了 $l-1$,此后 $l-1$ 对该区间无影响。

在计算到 $[l,n]$ 时,计算出所有 $[l,r]$,维护的是单调的位置序列,$r$ 对序列没有影响,二分 lowerbound 即可。

复杂度

$O(n\log n)$

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;

const int mxn=500005;

int n,q;
int a[mxn],b[mxn],pos[mxn],ans[mxn];
pair<pii,int> qu[mxn];
int t[mxn],l;

int read()
{
int ret=0;char c=getchar();
while(c>'9'||c<'0')c=getchar();
while(c<='9'&&c>='0')ret=ret*10+c-'0',c=getchar();
return ret;
}

int find(int L,int R,int v)
{
int mid;
while(L<=R)
{
mid=L+R>>1;
if(t[mid]>v)R=mid-1;
else L=mid+1;
}
return R;
}

int main()
{
// freopen("stack.in","r",stdin);
// freopen("stack.out","w",stdout);
n=read(),q=read();
for(int i=1;i<=n;i++)a[i]=read();for(int i=1;i<=n;i++)b[i]=read();
for(int i=1;i<=q;i++)qu[i].first.first=-read(),qu[i].first.second=read(),qu[i].second=i;
sort(qu+1,qu+q+1);l=n+1;
for(int i=n,j=1,an;i&&(j<=q);i--)
{
while(l<=n&&b[i]>b[t[l]]&&a[t[l]]!=a[i])
++l;
t[--l]=i;an=l;
for(;j<=q&&qu[j].first.first==(-i);j++)
{
an=find(an,n,qu[j].first.second);
ans[qu[j].second]=an-l+1;
}
}
for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
return 0;
}

T2 讨论

假设我们有一个容器,向其中一个一个加入这些集合。

如果两个人会讨论,则一定是后加入那个集合所覆盖的范围一半在一个集合里,一半在集合外。而在出现这种情况之前,两个集合要么包含,要么交集为空

那么如果我构造一种染色方案,使得每一道题,不同的覆盖方案染上不同的颜色,每一次看集合中颜色是否统一不就行了?

想得美

如果一个集合被该集合真包含,会被误判。

那我就避开这种情况的发生:我按集合大小从大到小排序,那后加入的集合就不可能真包含之前的集合。

那么如何染色呢?

每一次加入一个集合,如果没有被统计答案,则两个集合要么包含,要么交集为空,也就是说在这个集合加入的位置上覆盖情况相同。那么我给这个集合所包含的元素染上一个独一无二的颜色。

然后就愉快地结束了(越看越像暴力了

复杂度

多一个集合多一种颜色。给这些点染色或判断是否新加入集合导致讨论是 $O(\sum k_i)$ 的,排序是 $O(n\log n)$ 的,整体复杂度 $O(n\log n+\sum k_i)$。

Code

经过调整仍丑陋的考场代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int read()
{
int ret=0;char c=getchar();
while(c>'9'||c<'0')c=getchar();
while(c<='9'&&c>='0')ret=ret*10+c-'0',c=getchar();
return ret;
}

struct node
{
int k,id;
vector <int> p;
friend bool operator < (node n1,node n2)
{
return n1.k>n2.k;
}
}a[1000001];

vector <int> mp[1000001];
int T,n;
int lst,col[1000001];
bool f1[1000001],f2[1000001];

int main()
{
// freopen("discuss.in","r",stdin);
// freopen("discuss.out","w",stdout);
T=read();
for(;T;T--)
{
memset(col,0,sizeof(col));
int ans1=0,ans2=0;
n=read();
for(int i=1;i<=n;i++)
{
a[i].k=read();a[i].p.clear();
for(int j=1,x;j<=a[i].k;j++)x=read(),a[i].p.push_back(x),a[i].id=i;
}
sort(a+1,a+1+n);for(int i=1;i<=n;i++)mp[i].clear();

for(int i=1;i<=n;i++)
{
lst=-1;
for(int j=0;j<a[i].k;j++)
{mp[a[i].p[j]].push_back(a[i].id);
if(lst>=0&&col[a[i].p[j]]!=lst)
{
ans1=a[i].id;
for(int x=0;x<mp[a[i].p[j]].size();x++)f1[mp[a[i].p[j]][x]]=1;
for(int x=0;x<mp[a[i].p[j-1]].size();x++)f2[mp[a[i].p[j-1]][x]]=1;
for(int x=1;x<=n;x++)if(f1[x]^f2[x])
{
ans2=x;
break;
}
memset(f1,0,sizeof(f1));
memset(f2,0,sizeof(f2));
break;
}
lst=col[a[i].p[j]];
col[a[i].p[j]]=i;
}
if(ans1)break;
}
if(ans1)printf("YES\n%d %d\n",ans1,ans2);
else puts("NO");
}
return 0;
}

彩蛋

网站最后几分钟崩了没交上,后来延时差点以为自己提交忘删调试信息。不过还好 AC 了。


都多久了才来写

T3 看了一眼,感觉不会,也没时间了,就没做。

最终分数:200。