[HEOI 2016]

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

题目可以在cogs提交

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\n", ans[i]);
	fclose(stdin);
	fclose(stdout);
    return 0;
}

D1T2 sort

暴力分50很足就没写正解QAQ。。

好吧。首先题目的瓶颈在排序

考虑如果枚举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\n", 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\n", 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\n", 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-1]

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

然后NTT

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

#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\n", ans);
	return 0;
}

D2T3 str

标解是后缀数组

我写的是后缀自动机

考虑二分答案L,

#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\n", l);
	}

	return 0;
}

  

时间: 2024-08-10 18:36:01

[HEOI 2016]的相关文章

COGS 2274. [HEOI 2016] tree

★☆   输入文件:heoi2016_tree.in   输出文件:heoi2016_tree.out   简单对比时间限制:1 s   内存限制:128 MB 这道题数据弱到炸了 . 第一次做用树刨在链上找 A了. 第二次边都没连,直接赋值找爸爸 A了.. 屠龙宝刀点击就送 #include <ctype.h> #include <cstdio> #define M 100005 void read(int &x) { x=0; bool f=0; char ch=get

COGS——C2274. [HEOI 2016] tree

http://www.cogs.pro/cogs/problem/problem.php?pid=2274 ★☆   输入文件:heoi2016_tree.in   输出文件:heoi2016_tree.out   简单对比时间限制:1 s   内存限制:128 MB 福利题? 1 #include <algorithm> 2 #include <iostream> 3 #include <cstdio> 4 5 using namespace std; 6 7 con

数据结构(并查集||树链剖分):HEOI 2016 tree

[注意事项] 为了体现增强版,题目限制和数据范围有所增强: 时间限制:1.5s 内存限制:128MB 对于15% 的数据,1<=N,Q<=1000. 对于35% 的数据,1<=N,Q<=10000. 对于50% 的数据,1<=N,Q<=100000,且数据均为官方数据. 对于100% 的数据,1<=N,Q<=1000000. 请注意常数因子对于程序运行的影响. 并查集很简单,并查集就是倒序处理,表示删除一个点的标记,删除后不会再加回来,删完后,合并当前点与其

HEOI 2016 游记

闲来无事,把这玩意儿补上. OI生涯中第一次正经的考试.挂的很惨. Day -1 不小心把机油(雾)sm惹毛了. 好像没啥别的事儿. Day 0 说好了上午直接去机房,然而临时说让我们上完前两节课再去机房,整个人都不好了……于是乎前两节课颓废了. 去机房之前成功借到了sm的外套,背上一个角落里商标上写的made in Turkey真心叼…… 在机房好像是复习了一会儿. 去机房之前默默看了看不跟我去打省选的妹子,然后11:00去食堂吃饭(良心食堂开饭这么早),由于某些原因想减肥,于是乎基本没吃.

多项式求逆 学习总结

感觉蒟蒻现在学多项式求逆貌似有些晚了 不过真的很有意思了(然而省选的时候自己还在玩泥巴什么也不会 多项式求逆是基于倍增的 假设我们知道 h(x)f(x)=1(mod x^n) 移项得 (h(x)*f(x)-1)=0(mod x^n) 两边同时求平方得 h(x)^2*f(x)^2 - 2*h(x)*f(x) +1 = 0 (mod x^2n) 设g(x)*f(x)=1(mod x^2n) 两边同时乘以g(x)可以得 h(x)^2*f(x) -2*h(x) + g(x) =0 (mod x^2n)

聊一聊前端模板与渲染那些事儿

欢迎大家收看聊一聊系列,这一套系列文章,可以帮助前端工程师们了解前端的方方面面(不仅仅是代码): https://segmentfault.com/blog/frontenddriver 作为现代应用,ajax的大量使用,使得前端工程师们日常的开发少不了拼装模板,渲染模板.我们今天就来聊聊,拼装与渲染模板的那些事儿. 如果喜欢本文请点击右侧的推荐哦,你的推荐会变为我继续更文的动力 1 页面级的渲染 在刚有web的时候,前端与后端的交互,非常直白,浏览器端发出URL,后端返回一张拼好了的HTML串

使用 Raspberry Pi 上的传感器在 Node.js 中创建一个 IoT Bluemix 应用程序

先决条件 一个IBM Bluemix 帐号,一个 Raspberry Pi 2 或 3,一个 PIR 运动传感器 适用于本文的 Github 存储库 如果您是一位精明的 Bluemix 开发人员,您可能只想看看如何在 node.js 中与 IoT 建立连接,或者只想了解如何从此 github 存储库中拉取我的代码. git clone https://github.com/nicolefinnie/iot-nodejs-tutorial 以下是实现与 IBM IoT 平台连接在一起的 4 个 R

爱奇艺、优酷、腾讯视频竞品分析报告2016(一)

1 背景 1.1 行业背景 1.1.1 移动端网民规模过半,使用时长份额超PC端 2016年1月22日,中国互联网络信息中心 (CNNIC)发布第37次<中国互联网络发展状况统计报告>,报告显示,网民的上网设备正在向手机端集中,手机成为拉动网民规模增长的主要因素.截至2015年12月,我国手机网民规模达6.20亿,有90.1%的网民通过手机上网. 图 1  2013Q1~2015Q3在线视频移动端和PC端有效使用时长份额对比 根据艾瑞网民行为监测系统iUserTracker及mUserTrac

China Operating System 电脑操作系统 2016全球互联网排名

韩梦飞沙  韩亚飞  [email protected]  yue31313  han_meng_fei_sha 电脑操作系统 China Operating System 2016全球互联网排名 ========