1. 程式人生 > 其它 >[Contest on 2022.4.12] 我又來水部落格了

[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 <cstdio>
#define print(x,y) write(x),putchar(y)

template <class T>
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 <class T>
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 <iostream>
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]<p? ask(o<<1,l,mid,p): ask(o<<1|1,mid+1,r,p);
}

int main() {
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
    n=read(9); dp[1]=L[0]=1, R[0]=n;
    for(int i=1;i<=n;++i) a[i]=read(9), L[i]=n+1;
    build(1,0,n);
    for(int i=1;i<=n;++i) { 
        modify(a[i],las[a[i]]=i);
        if(!R[a[i]]) { dp[i+1] = inc(dp[i],ans); continue; }
        int l=L[a[i]], r=R[a[i]]; L[a[i]]=n+1, R[a[i]]=0;
        ans = dec(ans,1ll*a[i]*dec(dp[r],dp[l-1])%mod);
        while(l<=r) { 
            int mex = ask(1,0,n,r), p = max(l-1,las[mex]);
            ans = inc(ans,1ll*mex*dec(dp[r],dp[p])%mod);
            L[mex] = min(L[mex],p+1), R[mex] = max(R[mex],r);
            r = p;
        } 
        dp[i+1] = inc(dp[i],ans);
    }
    print(ans,'\n');
    return 0;
}

\(\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 <cstdio>
# include <cctype>
# define print(x,y) write(x), putchar(y)

template <class T>
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 <class T>
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 <map>
# include <vector>
# include <random>
# include <cstring>
# include <iostream>
using namespace std;
typedef long long ll;
typedef pair <int,int> par;

const int maxn = 5e5+5;

bool vis[maxn*10];
ll cur,ans[maxn];
map <int,int> 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 <edge> e[maxn];
vector <int> g[maxn<<2];

namespace random_Generator {
 	mt19937 engine;
	void initialize(int Seed) { engine.seed(Seed); }
	int getVal() { return uniform_int_distribution <int> (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].mx<t[t[o].son[0]].mx? t[t[o].son[0]].mx: t[o].mx);
	t[o].mx = (t[o].mx<t[t[o].son[1]].mx? t[t[o].son[1]].mx: t[o].mx);
}
void rev(int o) {
	t[o].tag ^= 1;
	swap(t[o].son[0],t[o].son[1]);
}
void pushDown(int o) {
	if(!t[o].tag) return; t[o].tag=0;
	rev(t[o].son[0]), rev(t[o].son[1]);
}
void rotate(int o) {
	int f=t[o].fa, ff=t[f].fa;
	int d=dir(o), fd=dir(f);
	add(t[o].son[d^1],f,d);
	add(f,o,d^1), add(o,ff,fd);
	pushUp(f), pushUp(o);
}
void pushAll(int o) {
	static int stk[maxn*11], tp;
	stk[tp=1] = o;
	while(~dir(o)) stk[++tp] = (o=t[o].fa);
	while(pushDown(stk[tp--]), tp);
}
void splay(int o) {
	pushAll(o);
	for(int f; f=t[o].fa, ~dir(o); rotate(o))
		if(~dir(f)) rotate(dir(f)==dir(o)?f:o);
}
void access(int o) {
	for(int ch=0; o; o=t[ch=o].fa)
		splay(o), t[o].son[1]=ch, pushUp(o);
}
void makeRoot(int o) { access(o), splay(o), rev(o); }
void split(int x,int y) { makeRoot(x), access(y), splay(x); }
void link(int x,int y) { makeRoot(x), t[x].fa=y; }
void cut(int x,int y) { split(x,y), t[x].son[1]=t[y].fa=0, pushUp(x); }

}

namespace UFS {

int Stk[maxn*10],s_tp,f[maxn],sz[maxn];

int fin(int x) { return x==f[x]?x:fin(f[x]); }
void merge(int x,int y) {
	x = fin(x), y = fin(y); 
	if(sz[x]<sz[y]) swap(x,y);
	Stk[++s_tp] = y; f[y]=x, sz[x]+=sz[y];
}
void initialize() {
	for(int i=1;i<=idx;++i) f[i]=i, sz[i]=1;
}
void goBack(int goal) {
	while(s_tp^goal) {
		int u = Stk[s_tp--];
		sz[f[u]] -= sz[u], f[u]=u;
	}
}
	
}

void link(int x,bool opt=0) {
	cur += E[x].w; lct::t[x+idx].v=E[x].w; 
	if(!opt) rec[++now]=x;
	lct::link(x+idx,E[x].u), lct::link(x+idx,E[x].v);
}
void cut(int x,bool opt=0) {
	cur -= E[x].w; if(!opt) rec[++now]=-x;
	lct::cut(x+idx,E[x].u), lct::cut(x+idx,E[x].v);
}
void goBack(int goal,int Goal) {
	UFS::goBack(goal);
	while(Goal^now) {
		int x = rec[now--];
		if(x>0) 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<lct::t[id].v) cut(id-idx);
			else ban = true;
		} else UFS::merge(u,v);
		if(!ban) link(ID);
	}
	if(l==r) return ans[l]=cur, goBack(rest,Rest), void();
	dicon(o<<1,l,mid), dicon(o<<1|1,mid+1,r);
	goBack(rest,Rest);
}

void ins(int o,bool opt=0) {
	if(!reID.count(o)) reID[o] = ++idx;
	o = reID[o];
	if(opt) 
		for(int i=R-1, lim=min(k-1,R-L), w; R-i<=lim; --i)
			w = random_Generator::getVal(),
			e[o].emplace_back(edge(++cnt,T)), E[cnt]=(Edge){o,reID[i],w},
			e[reID[i]].emplace_back(edge(cnt,T));
	else
		for(int i=L+1, lim=min(k-1,R-L), w; i-L<=lim; ++i)
			w = random_Generator::getVal(),
			e[o].emplace_back(edge(++cnt,T)), E[cnt]=(Edge){o,reID[i],w},
			e[reID[i]].emplace_back(edge(cnt,T));
}
void del(int o) {
	o = reID[o];
	for(const auto& to:e[o]) if(!vis[to.id]) 
		ins(vis[to.id]=1,1,n,to.t,T-1,to.id);
	e[o].clear(); // important!!!
}

int main() {
	freopen("c.in","r",stdin);
	freopen("c.out","w",stdout);
	memset(ans,-1,sizeof ans);
	SEED=read(9), k=read(9), n=read(9);
	random_Generator::initialize(SEED);
	reID.insert(make_pair(0,idx=1));
	for(T=1;T<=n;++T) {
		int opt=read(9);
		if(opt==1) del(L++);
		else if(opt==2) del(R--);
		else if(opt==3) ins(--L);
		else if(opt==4) ins(++R,1);
		else ins(1,1,n); 
	}
	for(int i=1;i<=idx;++i) if(!e[i].empty()) 
		for(const auto& to:e[i]) if(!vis[to.id]) 
			ins(vis[to.id]=1,1,n,to.t,n,to.id);
	UFS::initialize();
	dicon(1,1,n);
	for(int i=1;i<=n;++i) if(~ans[i]) print(ans[i],'\n');
	return 0;
}