[Contest on 2022.4.12] 我又来水博客了


这场比赛的题目名真的太不走心了

\(\cal T_1\) 史莱姆 \(\rm A\)

\(\mathbb{Description}\)

史莱姆有一串长度为 \(n\) 的彩灯,其中第 \(i\) 盏彩灯的颜色为 \(a_i\).

史莱姆将这串彩灯剪成若干段并装饰在房间里,每一段彩灯的美丽度为这段彩灯的颜色形成的集合的 \(\rm mex\)。由于彩灯之间的奇特相互作用,整个房间的美丽度为每段彩灯的美丽度的积。史莱姆想知道所有剪彩灯的方案的美丽度之和是多少。答案对 \(998244353\) 取模。

\(n\le 10^6,0\le a_i\le n\).

\(\mathbb{Solution}\)

首先可以想到 \(dp_i=\sum dp_j\cdot \text{mex}(j+1,i)\),然后我就死了:先开始想 \(\rm mex\) 有易撤销的性质,所以考虑用贡献法,将 \(dp_j\) 乘上系数贡献到后面的 \(\mathtt{dp}\) 值,但问题是 \(\text{mex}(j+1,i)\) 显然在 \(j\) 增加的时候会改变,无法维护(虽说修改时是区间赋值,但事实上这个操作并没有区间乘/加好做)。

既然区间赋值不好做,我们能否考虑将 \(\rm mex\) 相同的 \(\mathtt{dp}\) 值分类处理呢?显然对于右端点固定的 \(\rm mex\) 值是单调不升的,每个 \(\rm mex\) 值就统治了一段区间 \([L_i,R_i]\). 假设加入 \(a_i\),我们找到 \(a_i\) 对应的区间 \([L,R]\),显然只有这一段区间的 \(\rm mex\) 值会受到影响:先找到 \([R,i]\)\(\rm mex\),设其为 \(k\),我们找到 \(i\) 之前第一个值为 \(k\) 的位置 \(p\),那么 \([p+1,R]\)\(\rm mex\) 就都更改为 \(k\),然后接着递归下去即可。

一个需要注意的点是 \(dp_j\)\(\text{mex}(j+1,i)\) 的下标相差 \(1\),方便的方法是直接令 \(dp_1=1\),也就是将 \(\mathtt{dp}\) 值整体右移了一位。

时间复杂度?考虑每次至多删除一种 \(\rm mex\) 值,而总共有 \(n+1\)\(\rm mex\) 值,所以是 \(\mathcal O(n\log n)\) 的。

\(\mathbb{Code}\)

#include 
#define print(x,y) write(x),putchar(y)

template 
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while((s=getchar())>'9' || s<'0')
		f |= (s=='-');
	while(s>='0' && s<='9')
		x = (x<<1)+(x<<3)+(s^48),
		s = getchar();
	return f?-x:x;
}

template 
inline void write(T x) {
	static int writ[50],tp=0;
	if(x<0) putchar('-'),x=-x;
	do writ[++tp] = x-x/10*10, x/=10; while(x);
	while(tp) putchar(writ[tp--]^48);
}

# include 
using namespace std;

const int maxn = 1e6+5;
const int mod = 998244353;

inline int dec(int x,int y) { return x-y<0?x-y+mod:x-y; }
inline int inc(int x,int y) { return x+y>=mod?x+y-mod:x+y; }

int L[maxn],R[maxn],node[maxn],las[maxn];
int n,a[maxn],dp[maxn],ans,mn[maxn<<2];

void build(int o,int l,int r) {
    if(l==r) return node[l]=o, void();
    int mid = l+r>>1; 
    build(o<<1,l,mid); build(o<<1|1,mid+1,r);
}
void modify(int p,int k) {
    mn[p=node[p]] = k;
    while(p>>=1) mn[p] = min(mn[p<<1],mn[p<<1|1]);
}
int ask(int o,int l,int r,int p) {
    if(l==r) return r; int mid = l+r>>1;
    return mn[o<<1]

\(\cal T_2\)

\(\mathbb{Description}\)

\(\mathbb{Solution}\)

\(\mathbb{Code}\)


\(\cal T_3\) 史莱姆 \(\rm C\)

\(\mathbb{Description}\)

史莱姆有一张无向图,最开始图仅有 \(0\) 号节点。现在有 \(n\) 次操作,每次操作为以下 \(5\) 种之一(不妨假设每次操作前这张图的节点编号区间为 \([l,r]\)):

  1. 删去 \(l\) 号节点,并删去 \(l\) 号节点连接的所有边;
  2. 删去 \(r\) 号节点,并删去 \(r\) 号节点连接的所有边;
  3. 增加 \(l-1\) 号节点,并连接 \(\min\{k-1,r-l+1\}\) 条边,第 \(i\) 条边连接 \(l-1,l-1+i\),边有边权;
  4. 增加 \(r+1\) 号节点,并连接 \(\min\{k-1,r-l+1\}\) 条边,第 \(i\) 条边连接 \(r+1,r+1-i\),边有边权。
  5. 对当前图询问最小生成树的边权和。

输入保证任意时刻 \(l\le r\).

\(2\le k\le 10, n\le 5\cdot 10^5\).

\(\mathbb{Solution}\)

不会正解,所以写了一发 \(\rm lct\),结果草过去了 /xk.

\(\mathbb{Code}\)

# include 
# include 
# define print(x,y) write(x), putchar(y)

template 
inline T read(const T sample) {
	T x=0; char s; bool f=0;
	while(!isdigit(s=getchar())) f|=(s=='-');
	for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
	return f? -x: x;
}
template 
inline void write(T x) {
	static int writ[50], w_tp=0;
	if(x<0) putchar('-'), x=-x;
	do writ[++w_tp]=x-x/10*10, x/=10; while(x);
	while(putchar(writ[w_tp--]^48), w_tp);
}

# include 
# include 
# include 
# include 
# include 
using namespace std;
typedef long long ll;
typedef pair  par;

const int maxn = 5e5+5;

bool vis[maxn*10];
ll cur,ans[maxn];
map  reID;
int SEED,k,n,cnt,L,R,idx,T,rec[maxn*10],now;
struct edge {
	int id,t;
	edge() {}
	edge(int ID,int ti):id(ID),t(ti) {}
};
struct Edge { int u,v,w; } E[maxn*10];
vector  e[maxn];
vector  g[maxn<<2];

namespace random_Generator {
 	mt19937 engine;
	void initialize(int Seed) { engine.seed(Seed); }
	int getVal() { return uniform_int_distribution  (0,1e9)(engine); }
}

namespace lct {

struct node { bool tag; int fa,son[2],v; par mx; } t[maxn*11];

int dir(int o) {
	return t[t[o].fa].son[1]==o?1:
		   t[t[o].fa].son[0]==o?0:-1;
}
void add(int o,int f,int d) {
	if(o) t[o].fa=f;
	if(~d && f) t[f].son[d]=o;
}
void pushUp(int o) {
	t[o].mx = make_pair(t[o].v,o);
	t[o].mx = (t[o].mx0) cut(x,1);
		else link(-x,1);
	}
}

bool exi[maxn<<2];
void ins(int o,int l,int r) {
	exi[o] = true;
	if(l==r) return; int mid = l+r>>1;
	if(T<=mid) ins(o<<1,l,mid);
	else ins(o<<1|1,mid+1,r);
}
void ins(int o,int l,int r,int L,int R,int p) {
	if(l>=L && r<=R) return g[o].emplace_back(p), void();
	int mid = l+r>>1; if(L<=mid) ins(o<<1,l,mid,L,R,p);
	if(R>mid) ins(o<<1|1,mid+1,r,L,R,p);
}
void dicon(int o,int l,int r) {
	if(!exi[o]) return;
	int rest = UFS::s_tp, mid = l+r>>1, Rest = now;
	for(const auto& ID:g[o]) {
		int u=E[ID].u, v=E[ID].v; bool ban=0;
		if(UFS::fin(u)==UFS::fin(v)) {
			lct::split(u,v); int id=lct::t[u].mx.second;
			if(E[ID].w