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;
    }
    
  • 相关阅读:
    设计一个安全邮件传输系统
    2014(2)系统规划,可行性分析,成本效益分析
    2014(1)需求工程,需求获取
    公务员的福利政策
    2015(5)系统设计
    程序猿媛 九:Adroid zxing 二维码3.1集成(源码无删减)
    Android API
    [Android]利用run-as命令在不root情况下读取data下面的数据
    Android实用代码七段(五)
    Android实用代码七段(四)
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/15012743.html
Copyright © 2011-2022 走看看