zoukankan      html  css  js  c++  java
  • TJOI2016 && HEOI2016 解题报告

    好吧我来写一波题解骗访问量QAQ

    题目可以在cogs提交

    bzoj4551~4456

    D1T1 tree

    树剖可做,然而有更简单的做法,10min搞定

    维护一个并查集,时光倒流,如果当前点没有标记就把并查集合并到父亲上,查询就是并查集就可以了

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define maxn 200010
    using namespace std;
    
    int n;
    
    struct opt{int tp, x;}q[maxn];
    
    struct Edge{
    	int to, nxt;
    }edge[maxn << 1];
    
    int h[maxn], cnt;
    void add(int u, int v){
    	cnt ++;
    	edge[cnt].to = v;
    	edge[cnt].nxt = h[u];
    	h[u] = cnt;
    }
    
    int mark[maxn];
    
    int par[maxn];
    int find(int x){return x == par[x] ? x : par[x] = find(par[x]);}
    
    int fa[maxn];
    
    void dfs(int u){
    	if(mark[u])par[u] = u;
    	else par[u] = find(fa[u]);
    	for(int i = h[u]; i; i = edge[i].nxt){
    		int v = edge[i].to;
    		if(v == fa[u])continue;
    		fa[v] = u;
    		dfs(v);
    	}
    }
    
    int ans[maxn];
    
    int main(){
    	freopen("tree.in", "r", stdin);
    	freopen("tree.out", "w", stdout);
    	int test, u, v;
    	scanf("%d%d", &n, &test);
    	for(int i = 1; i < n; i ++){
    		scanf("%d%d", &u, &v);
    		add(u, v), add(v, u);
    	}
    	mark[1] ++;
    	char cmd[5];
    	for(int i= 1; i <= test; i ++){
    		scanf("%s%d", cmd, &q[i].x);
    		if(cmd[0] == 'C'){
    			q[i].tp = 1;
    			mark[q[i].x] ++;
    		}
    		else q[i].tp = 0;
    	}
    
    	dfs(1);
    
    	int x;
    	for(int i = test; i >= 1; i --){
    		x = q[i].x;
    		if(q[i].tp){
    			mark[x] --;
    			if(mark[x] == 0)
    				par[x] = find(fa[x]);
    		}
    		else ans[i] = find(x);
    	}
    
    	for(int i = 1; i <= test; i ++)
    		if(q[i].tp == 0)
    		    printf("%d
    ", ans[i]);
    	fclose(stdin);
    	fclose(stdout);
        return 0;
    }
    

    D1T2 sort

    (其实这道题目是BC原题。。)

    首先题目的瓶颈在排序

    考虑如果枚举a[q]的答案x,那么可以无视其他的变量的值,将序列转化为0,1,2,其中0代表比x小,1代表等于x,2代表大于x,这样答案就可以变成判定q这个位置是不是1。排序操作变得很好搞,是线段树的区间覆盖和查询。

    然而是O(n^2logn)的。。

    考虑二分答案x。。0代表比x小,1代表大于等于x

    排序如上。如果a[q]为0代表答案大于正确答案,继续向下二分,如果为1代表答案小于等于当前答案,继续向上二分。

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define maxn 100010
    using namespace std;
    
    int n, m;
    
    struct opt{
    	int tp, l, r;
    }q[maxn];
    
    int a[maxn], Q;
    
    int t[maxn << 2], lazy[maxn << 2];
    int Lc[maxn << 2], Rc[maxn << 2];
    #define lc id << 1
    #define rc id << 1 | 1
    
    void build(int id, int l, int r){
    	int mid = l + r >> 1;
    	Lc[id] = l, Rc[id] = r;
    	if(l == r)return;
    	build(lc, l, mid);
    	build(rc, mid+1, r);
    }
    
    void pushdown(int id){
    	if(lazy[id] > 0){
    		t[lc] = Rc[lc] - Lc[lc] + 1;
    		t[rc] = Rc[rc] - Lc[rc] + 1;
    		lazy[lc] = 1;
    		lazy[rc] = 1;
    	}
    
    	else if(lazy[id] < 0){
    		t[lc] = 0;
    		t[rc] = 0;
    		lazy[lc] = -1;
    		lazy[rc] = -1;
    	}
    
    	lazy[id] = 0;
    }
    
    void update(int id, int L, int R){
        if(L > R)return;
    	if(Lc[id] == L && Rc[id] == R){
    		lazy[id] = 1;
    		t[id] = Rc[id] - Lc[id] + 1;
    		return;
    	}
    	pushdown(id);
    	int mid = Lc[id] + Rc[id] >> 1;
    	if(R <= mid)update(lc, L, R);
    	else if(L > mid)update(rc, L, R);
    	else update(lc, L, mid), update(rc, mid+1, R);
    	t[id] = t[lc] + t[rc];
    }
    
    int ask(int id, int L, int R){
    	if(Lc[id] == L && Rc[id] == R){
    		int ret = t[id];
    		t[id] = 0, lazy[id] = -1;
    		return ret;
    	}
    	pushdown(id);
    	int mid = Lc[id] + Rc[id] >> 1, ret = 0;
    	if(R <= mid)ret = ask(lc, L, R);
    	else if(L > mid)ret = ask(rc, L, R);
    	else ret = ask(lc, L, mid) + ask(rc, mid+1, R);
    	t[id] = t[lc] + t[rc];
    	return ret;
    }
    
    void Modify1(int l, int r){
    	int nw = ask(1, l, r);
    	update(1, r - nw + 1, r);
    }
    
    void Modify2(int l, int r){
        int nw = ask(1, l, r);
    	update(1, l, l + nw - 1);
    }
    
    
    int ask(int id, int p){
    	if(Lc[id] == Rc[id])
    	    return t[id];
    	pushdown(id);
    	int mid = Lc[id] + Rc[id] >> 1, ret = 0;
    	if(p <= mid)ret = ask(lc, p);
    	else ret = ask(rc, p);
    	t[id] = t[lc] + t[rc];
    	return ret;
    }
    
    bool check(int nw){
    	lazy[1] = -1;
    	t[1] = 0;
    	for(int i = 1; i <= n; i ++)
            if(a[i] >= nw)
    			update(1, i, i);
    
    	for(int i = 1; i <= m; i ++){
            if(q[i].tp == 0)Modify1(q[i].l, q[i].r);
    		else Modify2(q[i].l, q[i].r);
    	}
    
    	return ask(1, Q);
    }
    
    int main(){
    	freopen("sort.in", "r", stdin);
    	freopen("sort.out", "w", stdout);
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= n; i ++)
    	    scanf("%d", &a[i]);
    	for(int i = 1; i <= m; i ++)
    		scanf("%d%d%d", &q[i].tp, &q[i].l, &q[i].r);
    	scanf("%d", &Q);
    	build(1, 1, n);
    	int l = 1, r = n;
    	while(l < r){
    		int mid = l + (r - l + 1) / 2;
    		if(check(mid))l = mid;
    		else r = mid - 1;
    	}
    	printf("%d
    ", l);
    	return 0;
    }
    

    D1T3 seq

    感觉10W就是可以看出来用CDQ来搞DP吧。。

    用max[i]代表i位置变化最大值,min[i]代表i位置变化最小值

    搞出偏序关系,j<=i, max[j] <=a[i], a[j] <= min[i]

    然后用CDQ搞就可以了

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    
    #define maxn 100010
    using namespace std;
    
    int n, m;
    
    int a[maxn], mn[maxn], mx[maxn];
    
    struct opt{
    	int p, mx, mn, a, f;
    }q[maxn];
    
    bool cmpp(const opt& a, const opt& b){
    	return a.p < b.p;
    }
    
    bool cmpmx(const opt& x, const opt& y){
    	if(x.mx != y.mx)return x.mx < y.mx;
    	return x.p < y.p;
    }
    
    bool cmpa(const opt& x, const opt& y){
    	if(x.a != y.a)return x.a < y.a;
    	return x.p < y.p;
    }
    
    namespace BIT{
    	int tim, n, t[maxn], vis[maxn];
    	#define lowbit(x) (x & -x)
    	void update(int pos, int val){
    		for(int i = pos; i <= n; i += lowbit(i))
    			if(vis[i] == tim)t[i] = max(t[i], val);
    			else vis[i] = tim, t[i] = val;
    	}
    
    	int ask(int pos){
    		int ret = 0;
    		for(int i = pos; i; i -= lowbit(i))
    			if(vis[i] == tim)
    				ret = max(ret, t[i]);
    		return ret;
    	}
    }
    
    void solve(int l, int r){
    	if(l == r)return;
    	int mid = (l + r) >> 1;
    	solve(l, mid);
    	sort(q + l, q + mid + 1, cmpmx);
    	sort(q + mid + 1, q + r + 1, cmpa);
    	BIT::tim ++;
    	int j = l;
    	for(int i = mid+1; i <= r; i ++){
    		for(; j <= mid && q[j].mx <= q[i].a; j ++)
    		    BIT::update(q[j].a, q[j].f);
    		q[i].f = max(q[i].f, BIT::ask(q[i].mn) + 1);
    	}
    	sort(q + l, q + r + 1, cmpp);
    	solve(mid+1, r);
    }
    
    int main(){
    	freopen("seq.in", "r", stdin);
    	freopen("seq.out", "w", stdout);
    	scanf("%d%d", &n, &m);
    
    	for(int i = 1; i <= n; i ++){
    		scanf("%d", &a[i]);
    		mn[i] = mx[i] = a[i];
    	}
    	int x, y;
    	for(int i = 1; i <= m; i ++){
    		scanf("%d%d", &x, &y);
    		mn[x] = min(mn[x], y);
    		mx[x] = max(mx[x], y);
    	}
    
    	int Mx = 0;
    	for(int i = 1; i <= n; i ++){
    		q[i].p = i;
    		q[i].mx = mx[i];
    		q[i].mn = mn[i];
    		q[i].a = a[i];
    		q[i].f = 1;
    		Mx = max(Mx, mx[i]);
    	}
    	BIT::n = Mx;
    	solve(1, n);
    
    	int ans = 0;
    	for(int i = 1; i <= n; i ++)
    	    ans = max(ans, q[i].f);
    	printf("%d
    ", ans);
    	fclose(stdin);
    	fclose(stdout);
        return 0;
    }
    

    D2T1 game

    每次DAY2第一题都挂是个什么Flag啊。。

    此题和ZOJ1654放置机器人同题。。

    样例都差不多好嘛QAQ!

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define maxn 52
    using namespace std;
    
    int n, m;
    
    char s[maxn][maxn];
    
    int H[maxn][maxn], L[maxn][maxn], size;
    
    struct Edge{
    	int to, nxt;
    }edge[maxn * maxn];
    
    int girl[maxn * maxn];
    
    int h[maxn * maxn], cnt, mark[maxn * maxn];
    
    void add(int u, int v){
    	cnt ++;
    	edge[cnt].to = v;
    	edge[cnt].nxt = h[u];
    	h[u] = cnt;
    }
    
    int vis[maxn * maxn], tim;
    int find(int u){
    	vis[u] = tim;
    	for(int i = h[u]; i; i = edge[i].nxt){
    		int v = edge[i].to;
    		if(vis[v] != tim){
    			vis[v] = tim;
    			if(girl[v] == 0 || find(girl[v])){
    				girl[v] = u;
    				return true;
    			}
    		}
    	}return false;
    }
    
    int main(){
    	freopen("game.in", "r", stdin);
    	freopen("game.out", "w", stdout);
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= n; i ++)
    		scanf("%s", s[i] + 1);
    	for(int i = 1; i <= n; i ++){
    		for(int j = 1; j <= m; j ++){
    			if(s[i][j] == '#')continue;
    			if(H[i][j-1])H[i][j] = H[i][j-1];
    			else{
    				H[i][j] = ++ size;
    				mark[size] = true;
    			}
    
    			if(L[i-1][j])L[i][j] = L[i-1][j];
    			else L[i][j] = ++ size;
    		}
    	}
    
    	for(int i = 1; i <= n; i ++){
    		for(int j = 1; j <= m; j ++){
    			if(s[i][j] == '*')
    			    add(H[i][j], L[i][j]);
    		}
    	}
    
    	int ans = 0;
    	for(int i = 1; i <= size; i ++)
    	    if(mark[i] && !girl[i])
    			tim ++, ans += find(i);
    	printf("%d
    ", ans);
    	return 0;
    }
    

    D2T2 sum

    考虑这个式子的意义。

    Bell数代表的意义是将n个物品随意划分成几个非空集合的划分数。

    这里多了一个阶乘代表的是这几个集合有序,多了2^j可以直接分配到递推式中。。

    其实我也并不能解释清楚啦。。意会一下~

    因为只求F(n)那么我们可以设一个G(n)

    令答案ans = sigma(G[i])  [i = 1 .. n]

    就有G[i]的递推式G[i] = 2 * sigma{C(j, i) * G[i - j]} j = [1 .. i]

    将组合数拆开是卷积形式,i的阶乘可以除到G函数下面

    然后NTT

    这样做是n^2logn的,所以外面套一层CDQ就可以了。

    UPD:据说直接上NTT就可以了?求教..

    upd:感谢大神们,从分治转化为多项式求逆,需要把f函数移到一边,化成一个卷积的形式,右边是一个函数,那么就有f * g = h
    多项式求逆就可以了

    upd2:其实这道题目还有stirling反演代入的式子,可以直接化成卷积,具体见Owaski犇的blog

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define maxn 500010
    using namespace std;
    typedef long long ll;
    const ll md = 998244353, G = 3;
    int n;
    
    ll g[maxn], f[maxn], h[maxn];
    
    ll power_mod(ll a, ll b = md - 2){
    	ll ret = 1;
    	while(b > 0){
    		if(b & 1)ret = ret * a % md;
    		b >>= 1;
    		a = a * a % md;
    	}return ret;
    }
    
    
    void NTT(ll A[], int n, int type){
    	for(int i = 0, j = 0; i < n; i ++){
    		if(i > j)swap(A[i], A[j]);
    		for(int t = n >> 1; (j ^= t) < t; t >>= 1);
    	}
    
    	for(int k = 2; k <= n; k <<= 1){
    		ll wn = power_mod(G, type > 0 ? (md-1)/k : md-1-(md-1)/k);
    		for(int i = 0; i < n; i += k){
    			ll w = 1;
    			for(int j = 0; j < k >> 1; j ++){
    				ll T = w * A[i+j+(k>>1)] % md;
    				A[i+j+(k>>1)] = (A[i+j] - T + md) % md;
    				A[i+j] = (A[i+j] + T) % md;
    				w = w * wn % md;
    			}
    		}
    	}
    
    	if(type < 0){
    		ll inv = power_mod(n);
    		for(int i = 0; i < n; i ++)
    		    (A[i] *= inv) %= md;
    	}
    }
    
    ll inv[maxn], fac[maxn];
    
    void solve(int l, int r){
    	if(l == r)return;
    	int mid = l + r >> 1;
    	solve(l, mid);
    	int len = r - l + 1, n;
    	for(n = 1; n <= len; n <<= 1);
    	for(int i = 0; i < n; i ++)f[i] = h[i] = 0;
    	for(int i = 0; i < n; i ++)f[i] = inv[i];
    	for(int i = l; i <= mid; i ++)h[i - l] = g[i];
    	NTT(f, n, 1), NTT(h, n, 1);
    	for(int i = 0; i < n; i ++)
    		h[i] = f[i] * h[i] % md;
    	NTT(h, n, -1);
    	for(int i = mid + 1; i <= r; i ++)
    		(g[i] += 2 * h[i - l] % md) %= md;
    	solve(mid + 1, r);
    }
    
    
    int main(){
    	freopen("sum.in", "r", stdin);
    	freopen("sum.out", "w", stdout);
    	scanf("%d", &n);
    	g[0] = inv[0] = fac[0] = 1;
    	for(int i = 1; i <= n; i ++)
    		fac[i] = fac[i-1] * i % md;
    	inv[n] = power_mod(fac[n]);
    	for(int i = n - 1; i; i --)
    		inv[i] = inv[i+1] * (i+1) % md;
    
    	solve(0, n);
    	ll ans = 0;
    	for(int i = 0; i <= n; i ++)
    	    (ans += g[i] * fac[i] % md) %= md;
        (ans += md) %= md;
    	printf("%lld
    ", ans);
    	return 0;
    }
    

    D2T3 str

    标解是后缀数组

    我写的是后缀自动机

    字符串的每一个子串都是一个后缀的前缀。将原串反建后缀自动机,每一个点的link指向它的最长前缀。

    考虑二分答案L,那么可以在后缀自动机上倍增出c~d的前缀节点p

    我们需要找到只有[a~b-L+1]的叶子节点是否在p的子树内,这样一定能保证答案的正确性。

    然后对parent树的dfs序搞一个主席树就可以了

    然后有各种杂技可以加快速度,不过我觉得这样比较好写。

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #define maxn 200010
    using namespace std;
    
    int n, m;
    //--------------------------------------------------//
    char str[maxn];
    int whe[maxn], anc[maxn][20];
    struct Node{
    	int nxt[26], link, len;
    }st[maxn];
    int Root, size, last;
    void init(){
    	Root = size = last = 0;
    	st[Root].len = 0;
    	st[Root].link = -1;
    }
    
    void Extend(int c){
    	int p = last, cur = ++ size;
    	st[cur].len = st[p].len + 1;
    	for(; ~p && st[p].nxt[c] == 0; p = st[p].link)
    	    st[p].nxt[c] = cur;
    	if(p == -1)
    	    st[cur].link = Root;
    	else{
    		int q = st[p].nxt[c];
    		if(st[q].len == st[p].len + 1)
    		    st[cur].link = q;
    		else{
    			int clone = ++ size;
    			st[clone] = st[q];
    			st[clone].len = st[p].len + 1;
    			for(; ~p && st[p].nxt[c] == q; p = st[p].link)
    			    st[p].nxt[c] = clone;
    			st[q].link = st[cur].link = clone;
    		}
    	}last = cur;
    }
    
    //--------------------------------------------------//
    int dfs_clock, In[maxn], Out[maxn];
    namespace DFS{
        struct Edge{int to, nxt;}edge[maxn];
    	int h[maxn], cnt;
    	void add(int u, int v){
    		cnt ++;
    		edge[cnt].to = v;
    		edge[cnt].nxt = h[u];
    		h[u] = cnt;
    	}
    
    	void dfs(int u){
    		In[u] = ++ dfs_clock;
    		for(int i = h[u]; i; i = edge[i].nxt)
    			dfs(edge[i].to);
    		Out[u] = dfs_clock;
    	}
    }
    //--------------------------------------------------//
    #define M 5000010
    int lc[M], rc[M], v[M], root[maxn], Cnt;
    
    int Insert(int rt, int l, int r, int p){
    	int cur = ++ Cnt;
    	lc[cur] = lc[rt], rc[cur] = rc[rt], v[cur] = v[rt] + 1;
    	if(l == r)return cur;
    	int mid = l + r >> 1;
    	if(p <= mid)lc[cur] = Insert(lc[rt], l, mid, p);
    	else rc[cur] = Insert(rc[rt], mid+1, r, p);
    	return cur;
    }
    
    int ask(int rt, int l, int r, int L, int R){
    	if(rt == 0)return 0;
    	if(l == L && R == r)
    	    return v[rt];
    	int mid = l + r >> 1;
    	if(R <= mid)return ask(lc[rt], l, mid, L, R);
    	if(L > mid) return ask(rc[rt], mid+1, r, L, R);
    	return ask(lc[rt], l, mid, L, mid) + ask(rc[rt], mid+1, r, mid+1, R);
    }
    
    int Find(int r, int len){
    	int t = whe[r];
    	for(int i = 18; i >= 0; i --){
    		int v = anc[t][i];
    		if(st[v].len >= len)
    		    t = v;
    	}return t;
    }
    
    int a, b, c, d;
    
    bool check(int len){
    	int u = Find(c, len);
    	int A = ask(root[b - len + 1], 1, dfs_clock, In[u], Out[u]);
    	int B = ask(root[a - 1], 1, dfs_clock, In[u], Out[u]);
    	return A > B;
    }
    
    int main(){
    	freopen("str.in", "r", stdin);
    	freopen("str.out", "w", stdout);
    	init();
    	scanf("%d%d", &n, &m);
    	scanf("%s", str + 1);
    	for(int i = n; i >= 1; i --)
    	    Extend(str[i] - 'a'), whe[i] = last;
    
    	for(int i = 1; i <= size; i ++)
    	    DFS::add(st[i].link, i);
    	DFS::dfs(0);
    	for(int i = 1; i <= size; i ++)
    	    anc[i][0] = st[i].link;
    	for(int j = 1; 1 << j <= size; j ++)
    		for(int i = 0; i <= size; i ++)
    			anc[i][j] = anc[anc[i][j-1]][j-1];
    
    	for(int i = 1; i <= n; i ++)
    		root[i] = Insert(root[i-1], 1, dfs_clock, In[whe[i]]);
    		
    	while(m --){
    		scanf("%d%d%d%d", &a, &b, &c, &d);
    		int l = 0, r = min(b - a + 1, d - c + 1);
    		while(l < r){
    			int mid = l + (r - l + 1) / 2;
    			if(check(mid))l = mid;
    			else r = mid - 1;
    		}
    		printf("%d
    ", l);
    	}
    	
    	return 0;
    }
    

      

    给时光以生命,而不是给生命以时光。
  • 相关阅读:
    BZOJ 2069 POI2004 ZAW 堆优化Dijkstra
    浏览器大小屏适配的方法
    编译性语言&amp;解释性语言
    ubuntu 查看网卡 数据包处理 速度
    使用Newtonsoft JsonConvert反序列化Json数据到DataTable
    easyUI datagrid view扩展
    easyUI layout 中使用tabs+iframe解决请求两次方法
    oracle自定义判断数据是否为数值函数
    easyUI datagrid editor扩展dialog
    JavaScript格式化日期
  • 原文地址:https://www.cnblogs.com/Candyouth/p/5430452.html
Copyright © 2011-2022 走看看