zoukankan      html  css  js  c++  java
  • 20210-07-14 集训题解

    货币

    题目传送门

    Description

    Solution

    假设 ( ext{nxt}_i) 为与 (i) 同块的下一个点的位置,那么设 (f_l) 表示左端点在 (l) 时最靠右的合法右端点,那么可以得到:

    [f_l=max_{i=0}^{l-1}{ ext{nxt}_i} ]

    特殊的,我们定义 ( ext{nxt}_0) 为每个块第一次出现位置的最大值。

    那么,答案就是 (min{f_l-l+1})

    可以发现,每次将两个块合并可以启发式合并,并且操作相当于将一部分元素的 ( ext{nxt}_i) 减小,考虑如何 (Theta(log n)) 实现该操作。

    你发现我们并不需要知道具体的 (f_i),我们只需要实时更新答案,而答案是不增的,所以你可以只考虑当前产生的贡献。然后你发现似乎直接暴力更新即可,均摊复杂度就可以做到 (Theta(nlog^2 n))

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define MAXN 200005
    
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c != ' ' && c != '
    ') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    template <typename T> void chkmax (T &a,T b){a = max (a,b);}
    template <typename T> void chkmin (T &a,T b){a = min (a,b);}
    
    set <int> st[MAXN];
    int n,m,type,ans,fa[MAXN],nxt[MAXN];
    int findSet (int x){return fa[x] == x ? x : fa[x] = findSet (fa[x]);}
    
    struct Segment{
    	int maxn[MAXN << 2];
    	void pushup (int x){maxn[x] = max (maxn[x << 1],maxn[x << 1 | 1]);}
    	void build (int x,int l,int r){
    		if (l == r) return maxn[x] = nxt[l],void ();
    		int mid = l + r >> 1;
    		build (x << 1,l,mid),build (x << 1 | 1,mid + 1,r);
    		pushup (x);
    	}
    	void modify (int x,int l,int r,int pos,int k){
    		if (l == r) return maxn[x] = k,void ();
    		int mid = (l + r) >> 1;
    		if (pos <= mid) modify (x << 1,l,mid,pos,k);
    		else modify (x << 1 | 1,mid + 1,r,pos,k);
    		pushup (x);
    	}
    	int query (int x,int l,int r,int ql,int qr){
    		if (l >= ql && r <= qr) return maxn[x];
    		int mid = l + r >> 1,res = 0;
    		if (ql <= mid) chkmax (res,query (x << 1,l,mid,ql,qr));
    		if (qr > mid) chkmax (res,query (x << 1 | 1,mid + 1,r,ql,qr));
    		return res;
    	}
    	int findit (int x,int l,int r,int k){
    		if (maxn[x] <= k) return r;
    		int mid = (l + r) >> 1;
    		if (maxn[x << 1] > k) return findit (x << 1,l,mid,k);
    		else return findit (x << 1 | 1,mid + 1,r,k);
    	}
    }T;
    
    void modify (int x,int k){//将nxt[x]修改为k
    	if (nxt[x] == k) return ;
    	int l = x,r = T.findit (1,0,n,nxt[x]);
    	T.modify (1,0,n,x,nxt[x] = k);
    	while (l < r){
    		int t = T.query (1,0,n,0,l),p = T.findit (1,0,n,t);
    		chkmin (ans,t - p + 1),l = p + 1;
    	}
    }
    
    void unionSet (int x,int y){
    	x = findSet (x),y = findSet (y);if (x == y) return ;
    	if (st[x].size() > st[y].size()) swap (x,y);fa[x] = y;
    	if (*st[x].begin() < *st[y].begin()) st[0].erase (*st[y].begin());
    	else st[0].erase (*st[x].begin());
    	modify (0,*st[0].rbegin());
    	for (Int k : st[x]) st[y].insert (k);
    	for (Int k : st[x]){
    		set<int>::iterator it = st[y].find (k);
    		if (it != st[y].begin()) -- it,modify (*it,k),++ it;
    		++ it;if (it != st[y].end()) modify (k,*it),-- it;
    	}
    }
    
    signed main(){
    	freopen ("currency.in","r",stdin);
    	freopen ("currency.out","w",stdout);
    	read (n,m,type),ans = nxt[0] = n;
    	for (Int i = 1;i <= n;++ i) fa[i] = i,nxt[i] = 1e9 + i,st[i].insert (i),st[0].insert (i);
    	T.build (1,0,n);
    	while (m --> 0){
    		int u,v;read (u,v),u = (u + type * ans - 1) % n + 1,v = (v + type * ans - 1) % n + 1;
    		unionSet (u,v),write (ans),putchar ('
    ');
    	}
    	return 0;
    }
    

    比赛

    题目传送门

    Description

    Solution

    可以发现,设 (x=(i-a_i)mod n),在 (x<i) 的时候将 (i) 设为 (x) 的儿子,那么一个点是必胜态,当且仅当它的所有儿子都是必败态。

    那么,可以发现答案不是 (0) 就是 (0) 的儿子(实际上在原问题上考虑也可以得到相同的结论)。考虑如何快速维护这个东西。

    可以使用 ( ext{LCT}),每次 link,cut 之后都将根的儿子加入堆里面,然后状态可以设 (v_{0/1}) 储存,表示在 Splay 中以一个点为根的子树后加入 必败态/必胜态 之后是什么状态,然后你发现这个可以合并,答案也就是 (v_0)

    复杂度 (Theta(nlog n)) 的。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define MAXN 300005
    
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c != ' ' && c != '
    ') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    template <typename T> void chkmax (T &a,T b){a = max (a,b);}
    template <typename T> void chkmin (T &a,T b){a = min (a,b);}
    
    int n,m,a[MAXN],p[MAXN];
    
    struct node{
    	bool v[2];
    	bool & operator [] (const int key){return v[key];}
    	node operator + (const node &p)const{return node{v[p.v[0]],v[p.v[1]]};}
    };
    
    priority_queue <int,vector <int>,greater<int> > q;
    
    struct LCT{
    #define ls(x) son[x][0]
    #define rs(x) son[x][1]
    	node val[MAXN],sum[MAXN];
    	LCT(){val[0] = sum[0] = node{0,1};}
    	int fa[MAXN],son[MAXN][2],cnts[MAXN],isnt[MAXN];
    	bool rnk (int x){return son[fa[x]][1] == x;}
    	bool isroot (int x){return son[fa[x]][rnk(x)] != x;}
    	void pushup (int x){sum[x] = sum[ls(x)] + val[x] + sum[rs(x)];}
    	void rotate (int x){
    		int y = fa[x],z = fa[y],k = rnk (x),w = son[x][!k];
    		if (!isroot (y)) son[z][rnk(y)] = x;son[x][!k] = y,son[y][k] = w;
    		if (w) fa[w] = y;fa[y] = x,fa[x] = z;
    		pushup (y),pushup (x);
    	}
    	void Splay (int x){
    		while (!isroot (x)){
    			int y = fa[x];
    			if (!isroot (y)) rotate (rnk(x) == rnk(y) ? y : x);
    			rotate (x);
    		}
    	}
    	void List (int &x){
    		if (!x) return ;
    		Splay (x);while (ls(x)) x = ls(x);Splay (x);
    	}
    	bool calc (int x){
    		return Splay (x),(val[x] + sum[rs(x)])[0];
    	}
    	void Access (int x){
    		for (Int y = 0,z;x;x = fa[y = x]){
    			List (y),Splay (x),z = rs(x),rs(x) = 0,List (z);
    			if (z) cnts[x] += (isnt[z] = calc (z));
    			cnts[x] -= isnt[y],isnt[y] = 0,rs(x) = y,val[x] = node{!cnts[x],0},pushup (x);
    		}
    	}
    	void Link (int x,int y){
    		Access (y),Splay (y),Splay (x),son[fa[x] = y][1] = x,pushup (y);
    		List (y);
    		if (y == 1){
    			y = rs(y);while (ls(y)) y = ls(y);
    			q.push (y - 1);
    		}
    	}
    	void Cut (int x,int y){
    		Access (x),List (x),x = rs(x);
    		while (ls(x)) x = ls(x);
    		q.push (x - 1),Splay (y),x = son[y][1],son[y][1] = fa[x] = 0,pushup (y);
    		
    	}
    }T;
    
    void makeit (){
    	while (!q.empty()){
    		int u = q.top();
    		if (!p[u] && T.calc (u + 1)) return write (u),putchar ('
    '),void ();
    		q.pop ();
    	}
    	puts ("0");
    }
    
    signed main(){
    	freopen ("match.in","r",stdin);
    	freopen ("match.out","w",stdout);
    	read (n,m);
    	for (Int i = 1;i <= n;++ i) T.sum[i] = T.val[i] = node{1,0};
    	for (Int i = 0;i < n;++ i){
    		read (a[i]),p[i] = (i - a[i] + n) % n;
    		if (p[i] < i) T.Link (i + 1,p[i] + 1);
    	}
    	makeit();
    	while (m --> 0){
    		int x,y;read (x,y);
    		if (p[x] < x) T.Cut (x + 1,p[x] + 1);
    		p[x] = (x - y + n) % n;
    		if (p[x] < x) T.Link (x + 1,p[x] + 1);
    		makeit();
    	}
    	return 0;
    }
    

    字符串

    题目传送门

    Description

    Solution

    发现确定根之后可以 (Theta(n)) 构造和判断。

    考虑如何找根,设 (B)((u,v)) 为在 (E_S,E_T) 中都出现的边,可以发现如果存在一个点 (B) 边度数 (ge 3),如果它不是根的话一定不合法,因为这种边意味着根到 (v) 上的字符都相同。

    还剩下 (B) 边度数都 (le 2) 的情况,可以发现,这种情况下,一条 (B)((u,v)) 如果 (v) 存在分岔那么分岔点的 ( ext{fail}) 一定是根或者与根存在 (B) 边相连的点。枚举判断即可。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define MAXN 100005
    
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c != ' ' && c != '
    ') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    template <typename T> void chkmax (T &a,T b){a = max (a,b);}
    template <typename T> void chkmin (T &a,T b){a = min (a,b);}
    
    vector <int> gS[MAXN],gT[MAXN];
    int n,fat[MAXN],dep[MAXN],col[MAXN],par[MAXN],fail[MAXN];
    
    void dfs (int u,int fa){
    	fat[u] = fa,dep[u] = dep[fa] + 1;
    	for (Int v : gS[u]) if (v ^ fa) dfs (v,u);
    }
    
    bool flg;
    stack <int> sta[MAXN];
    void dfs2 (int r,int u,int fa){
    	for (Int v : gS[u]) if (v ^ fat[u]){
    		flg &= (fail[v] == (sta[col[v]].empty() ? r : sta[col[v]].top()));
    		sta[col[v]].push (v);
    	}
    	for (Int v : gT[u]) if (v ^ fa) dfs2 (r,v,u);
    	for (Int v : gS[u]) if (v ^ fat[u]) sta[col[v]].pop();
    }
    
    bool check (int r){
    	dfs (r,0);
    	memset (fail,0,sizeof (fail));
    	for (Int u = 1;u <= n;++ u) if (u != r){
    		for (Int v : gT[u]){
    			if (dep[v] == dep[u]) return 0;
    			if (dep[v] < dep[u]){
    				if (!fail[u]) fail[u] = v;
    				else return 0;
    			}
    		}
    		if (!fail[u]) return 0;
    	}
    	int tot = 0;queue <int> q;
    	for (Int v : gS[r]) 
    		if (fail[v] != r) return 0;
    		else col[v] = ++ tot,q.push (v);
    	while (!q.empty()){
    		int u = q.front();q.pop ();
    		for (Int v : gS[u]) if (dep[v] > dep[u]){
    			if (fail[v] != r) col[v] = col[fail[v]],q.push (v);
    			else col[v] = ++ tot,q.push (v);
    		}
    	}
    	flg = 1,dfs2 (r,r,0);
    	return flg;
    }
    
    int su[MAXN],sv[MAXN],deg[MAXN];
    
    void print (int rt){
    	write (rt),putchar ('
    ');
    	for (Int i = 2;i <= n;++ i){
    		if (dep[su[i]] > dep[sv[i]]) swap (su[i],sv[i]);
    		write (col[sv[i]]),putchar (' ');
    	}
    	putchar ('
    ');
    	return ;
    }
    
    #define pii pair<int,int>
    vector <int> B[MAXN];
    map <pii,int> mp;
    
    void fuckit (){
    	for (Int i = 1;i <= n;++ i) if (B[i].size()){
    		for (Int e : gS[i]) if (!B[e].size()){
    			for (Int p : gT[e]) if (B[p].size()){
    				if (check (p)){print(p);return ;}
    				for (Int q : B[p]) if (check (q)){print(q);return ;}
    			}
    		}
    	}	
    	check (1),print(1);
    }
    
    signed main(){
    	freopen ("string.in","r",stdin);
    	freopen ("string.out","w",stdout);
    	read (n);
    	for (Int i = 2,u,v;i <= n;++ i) read (u,v),su[i] = u,sv[i] = v,gS[u].push_back (v),gS[v].push_back (u),mp[make_pair (u,v)] = mp[make_pair (v,u)] = 1;
    	for (Int i = 2,u,v;i <= n;++ i){
    		read (u,v),gT[u].push_back (v),gT[v].push_back (u);
    		if (mp[make_pair (u,v)]) B[u].push_back (v),B[v].push_back (u);
    	}
    	for (Int i = 1;i <= n;++ i) if (B[i].size() >= 3){check (i);print(i);return 0;}
    	fuckit ();
    	return 0;
    }
    
  • 相关阅读:
    Linux内核RPC请求过程
    二分图
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 文本加密
    Java实现 蓝桥杯 算法提高 文本加密
    Java蓝桥杯 算法提高 九宫格
    Java蓝桥杯 算法提高 九宫格
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/15012743.html
Copyright © 2011-2022 走看看