zoukankan      html  css  js  c++  java
  • codeforces 几道题目

    BZOJ挂了....明天就要出发去GDKOI了....不能弃疗. 于是在cf水了几道题, 写写详(jian)细(dan)题解, 攒攒RP, 希望GDKOI能好好发挥....... 

    620E. New Year Tree

    题目大意:

    N个结点的树, 结点1为根, 要支持2种操作(M个操作):

    1.将以v为根的子树所有节点的颜色为c

    2.询问以v为根的子树中不同颜色个数

    N,M<=4*10^5, 1<=c<=60

    题解:

    处理出dfs序, 线段树维护.

    1,2操作都对应线段树的一段区间(子树在dfs序中连续), 线段树结点压位记录当前区间的结点包含哪些颜色.

    时间复杂度O(M log N * max(c))

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    typedef long long ll;
    
    const int maxn = 400009;
    
    int N, Q, c[maxn];
    int Id[maxn], Left[maxn], Right[maxn], dfn;
    
    struct edge {
    	int t;
    	edge* n;
    } E[maxn << 1], *Pt = E, *H[maxn];
    
    inline void AddEdge(int u, int v) {
    	Pt->t = v, Pt->n = H[u], H[u] = Pt++;
    }
    
    void DFS(int x, int fa = -1) {
    	Id[++dfn] = x;
    	Left[x] = dfn;
    	for(edge* e = H[x]; e; e = e->n)
    		if(e->t != fa) DFS(e->t, x);
    	Right[x] = dfn;
    }
    
    struct Node {
    	Node *lc, *rc;
    	ll v;
    	int mk;
    	inline void pd() {
    		if(~mk) {
    			lc->mk = mk;
    			rc->mk = mk;
    			v = 1LL << mk;
    			mk = -1;
    		}
    	}
    	inline void upd() {
    		if(lc && rc)
    			v = lc->v | rc->v;
    		if(~mk)
    			v = 1LL << mk;
    	}
    } pool[maxn << 1], *pt = pool, *Root;
    
    int L, R, Val;
    
    void Build(Node* t, int l, int r) {
    	int m = (l + r) >> 1;
    	t->mk = -1;
    	if(l != r) {
    		Build(t->lc = pt++, l, m);
    		Build(t->rc = pt++, m + 1, r);
    		t->upd();
    	} else
    		t->v = 1LL << c[Id[m]];
    }
    
    void Modify(Node* t, int l, int r) {
    	if(L <= l && r <= R) {
    		t->mk = Val;
    	} else {
    		int m = (l + r) >> 1;
    		t->pd();
    		L <= m ? Modify(t->lc, l, m) : t->lc->upd();
    		m < R ? Modify(t->rc, m + 1, r) : t->rc->upd();
    	}
    	t->upd();
    }
    
    ll Query(Node* t, int l, int r) {
    	if(L <= l && r <= R) return t->v;
    	int m = (l + r) >> 1;
    	t->pd();
    	t->lc->upd(), t->rc->upd();
    	return (L <= m ? Query(t->lc, l, m) : 0) | (m < R ? Query(t->rc, m + 1, r) : 0);
    }
    
    int calc(ll n) {
    	int ret = 0;
    	for(; n; n -= n & -n) ret++;
    	return ret;
    }
    
    void Work() {
    	DFS(dfn = 0);
    	Build(Root = pt++, 1, N);
    	int t, x;
    	while(Q--) {
    		scanf("%d%d", &t, &x), x--;
    			L = Left[x], R = Right[x];
    		if(t == 1) {
    			scanf("%d", &Val), Val--; 
    			Modify(Root, 1, N);
    		} else
    			printf("%d
    ", calc(Query(Root, 1, N)));
    	}
    }
    
    void Init() {
    	int u, v;
    	scanf("%d%d", &N, &Q);
    	for(int i = 0; i < N; i++)
    		scanf("%d", c + i), c[i]--;
    	for(int i = 1; i < N; i++) {
    		scanf("%d%d", &u, &v);
    		u--, v--;
    		AddEdge(u, v);
    		AddEdge(v, u);
    	}
    }
    
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("test.in", "r", stdin);
    	freopen("test.out", "w", stdout);
    #endif
    	
    	Init();
    	Work();
    		
    	return 0;
    }
    

    609E. Minimum spanning tree for each edge

    题目大意:

    N个结点, M条无向边, 第i条边连接ui, vi, 边权为wi, 依次输出包含第i条边的最小生成树.

    1<=N,M<=2*10^5, 1<=wi<=10^9.

    题解:

    先跑出任意一个MST(设权值和为tot), 然后建树.

    树链剖分, RMQ维护边权最大值(不需要线段树).

    对于每条边ui, vi, wi, 求出ui~vi路径上的最大边权maxv, 对于这条边的答案就是tot - maxv + wi.

    时间复杂度O(N log N + M log N)

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    typedef long long ll;
    
    const int maxn = 200009;
    
    int N, M;
    ll tot, ans[maxn];
    
    struct e {
    	int u, v, w, Id;
    	bool operator < (const e &t) const {
    		return w < t.w;
    	}
    } Edge[maxn];
    
    struct edge {
    	int t, w;
    	edge* n;
    } E[maxn << 1], *pt = E, *H[maxn];
    
    inline void AddEdge(int u, int v, int w) {
    	pt->t = v, pt->w = w, pt->n = H[u], H[u] = pt++;
    }
    
    int par[maxn];
    int Find(int x) {
    	return x == par[x] ? x : par[x] = Find(par[x]);
    }
    
    int RMQ[20][maxn], w[maxn];
    int top[maxn], fa[maxn], Id[maxn], dep[maxn], sz[maxn], ch[maxn];
    int Top, dfn;
    
    void dfs(int x) {
    	sz[x] = 1, ch[x] = -1;
    	for(edge* e = H[x]; e; e = e->n) if(e->t != fa[x]) {
    		dep[e->t] = dep[x] + 1;
    		fa[e->t] = x;
    		w[e->t] = e->w;
    		dfs(e->t);
    		sz[x] += sz[e->t];
    		if(!~ch[x] || sz[ch[x]] < sz[e->t])
    			ch[x] = e->t;
    	}
    }
    
    void DFS(int x) {
    	top[x] = Top;
    	Id[x] = dfn++;
    	if(~ch[x]) DFS(ch[x]);
    	for(edge* e = H[x]; e; e = e->n)
    		if(e->t != fa[x] && e->t != ch[x]) DFS(Top = e->t);
    }
    
    void Init_Query() {
    	for(int i = 0; i < N; i++)
    		RMQ[0][Id[i]] = w[i];
    	for(int i = 1; (1 << i) <= N; i++)
    		for(int j = 0; j + (1 << i) <= N; j++)
    			RMQ[i][j] = max(RMQ[i - 1][j], RMQ[i - 1][j + (1 << (i - 1))]);
    }
    
    int Qmax(int l, int r) {
    	int Log = log2(r - l + 1);
    	return max(RMQ[Log][l], RMQ[Log][r - (1 << Log) + 1]);
    }
    
    int Query(int x, int y) {
    	int ret = 0;
    	for(; top[x] != top[y]; x = fa[top[x]]) {
    		if(dep[top[x]] < dep[top[y]]) swap(x, y);
    		ret = max(ret, Qmax(Id[top[x]], Id[x]));
    	}
    	if(x == y) return ret;
    	if(dep[x] < dep[y]) swap(x, y);
    	return max(ret, Qmax(Id[y] + 1, Id[x]));
    }
    
    void Work() {
    	tot = 0;
    	sort(Edge, Edge + M);
    	for(int i = 0; i < N; i++) par[i] = i;
    	for(int i = 0; i < M; i++) {
    		int u = Find(Edge[i].u), v = Find(Edge[i].v);
    		if(u != v) {
    			par[u] = v;
    			tot += Edge[i].w;
    			AddEdge(Edge[i].u, Edge[i].v, Edge[i].w);
    			AddEdge(Edge[i].v, Edge[i].u, Edge[i].w);
    			Edge[i].Id += M;
    		}
    	}
    	
    	fa[0] = -1, w[0] = dep[0] = 0;
    	dfs(0);
    	DFS(Top = dfn = 0);
    	Init_Query();
    	
    	for(int i = 0; i < M; i++) if(Edge[i].Id < M) {
    		ans[Edge[i].Id] = tot - Query(Edge[i].u, Edge[i].v) + Edge[i].w;
    	}
    	else
    		ans[Edge[i].Id - M] = tot;
    	for(int i = 0; i < M; i++)
    		cout << ans[i] << "
    ";
    }
    
    void Init() {
    	scanf("%d%d", &N, &M);
    	for(int i = 0; i < M; i++) {
    		scanf("%d%d%d", &Edge[i].u, &Edge[i].v, &Edge[i].w);
    		Edge[i].u--, Edge[i].v--;
    		Edge[i].Id = i;
    	}
    }
    
    int main() {
    	
    	Init();
    	Work();
    	
    	return 0;
    }
    

      

    600D. Area of Two Circles' Intersection

    题目大意:

    给2个圆, 求它们交的面积.

    -10^9<=x,y<=10^9, 1<=r<=10^9

    题解:

    模板题.

    先判掉相离和相切的情况, 然后利用余弦定理与扇形和三角形面积公式解决.

    时间复杂度O(1)

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    typedef long double ld;
    
    void Work() {
    	int x0, x1, y0, y1, r0, r1;
    	scanf("%d%d%d%d%d%d", &x0, &y0, &r0, &x1, &y1, &r1);
    	ld d = sqrt((ld) (x0 - x1) * (x0 - x1) + (ld) (y0 - y1) * (y0 - y1));
    	if(r0 + r1 <= d) {
    		puts("0");
    		return;
    	}
    	if(abs(r0 - r1) >= d) {
    		printf("%.10lf
    ", (double) ld(acos(-1.0)) * min(r0, r1) * min(r0, r1));
    		return;
    	}
    	ld a0 = acos((ld(r0) * r0 + ld(d) * d - ld(r1) * r1) / (d * r0 * 2)); // angle_0
    	ld a1 = acos((ld(r1) * r1 + ld(d) * d - ld(r0) * r0) / (d * r1 * 2)); // angle_1
    
    	printf("%.10lf
    ", (double) (ld(r0) * r0 * (a0 - sin(a0) * cos(a0)) + ld(r1) * r1 * (a1 - sin(a1) * cos(a1))));
    }
    
    int main() {
    	
    	Work();
    	
    	return 0;
    }
    

     

    592D. Super M

    题目大意:

    给棵N个结点的树, 你需要走到其中的M个点, 起始位置任选. 求M个点全都走过的最短路径, 多种方案则选择起始位置的结点字典序小的.

    1<=m<=n<=123456

    题解:

    去掉一些没用的结点与边, 使得保留下来的树的叶子结点全部为需要到达的点.

    记留下来的边总长为x, 起点与终点距离为y, 通过观察(?)可以发现答案为2*x-y(画个图感受一下, 易证).

    所以要最大化y.那么就是在新树中找出1条直径, 并且某一端点字典序最小(多条直径时).

    对于树的直径, 可以使用经典的BFS/DFS做法.

    时间复杂度O(N)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    const int maxn = 123460;
    
    int n, m;
    bool F[maxn];
    
    struct edge {
    	int t, f;
    	edge* n;
    } E[maxn << 1], *pt = E, *H[maxn];
    
    inline void AddEdge(int u, int v) {
    	pt->t = v, pt->f = 1, pt->n = H[u], H[u] = pt++;
    }
    
    int X, Y, d[maxn], ans;
    
    void dfs(int x, int fa) {
    	d[x] = F[x];
    	for(edge* e = H[x]; e; e = e->n) if(e->t != fa) {
    		dfs(e->t, x);
    		if(!d[e->t]) {
    			e->f = 0;
    		} else
    			d[x] += d[e->t];
    	}
    }
    
    void dfs(int x, int dist, int fa) {
    	d[x] = dist;
    	for(edge* e = H[x]; e; e = e->n)
    		if(e->f && e->t != fa) dfs(e->t, dist + 1, x);
    }
    
    void calc(int x, int fa = -1) {
    	for(edge* e = H[x]; e; e = e->n)
    		if(e->f && e->t != fa) calc(e->t, x), ans += 2;
    }
    
    void Work() {
    	int mx, Id;
    	for(int i = 0; i < n; i++) if(F[i]) {
    		dfs(i, -1);
    		break;
    	}
    	for(int i = 0; i < n; i++) if(F[i]) {
    		dfs(i, 0, -1);
    		break;
    	}
    	mx = 0;
    	for(int i = 0; i < n; i++) if(F[i]) {
    		mx = max(mx, d[i]);
    		if(mx == d[i]) X = i;
    	}
    	dfs(X, 0, -1);
    	mx = 0;
    	for(int i = 0; i < n; i++) if(F[i]) {
    		mx = max(mx, d[i]);
    		if(mx == d[i]) Y = i;
    	}
    	ans = -d[Y], Id = min(X, Y);
    	for(int i = 0; i < n; i++)
    		if(F[i] && d[i] == mx) Id = min(Id, i);
    	dfs(Y, 0, -1);
    	for(int i = 0; i < n; i++)
    		if(F[i] && d[i] == mx) Id = min(Id, i);
    	calc(Y);
    	printf("%d
    %d
    ", ++Id, ans);
    }
    
    void Init() {
    	scanf("%d%d", &n, &m);
    	int u, v;
    	for(int i = 1; i < n; i++) {
    		scanf("%d%d", &u, &v);
    		u--, v--;
    		AddEdge(u, v);
    		AddEdge(v, u);
    	}
    	memset(F, 0, sizeof F);
    	for(int i = 0; i < m; i++) {
    		scanf("%d", &v);
    		F[--v] = true;
    	}
    }
    
    int main() {
    	
    	Init();
    	Work();
    	
    	return 0;
    }
    

    596D. Wilbur and Trees

    题目大意:

    1条线上有N棵位置为xi,高为H的树. 要把它们全部砍下来, 每次随机选择最左边或者最右边的树砍下来. 每棵树倒向左边的概率为p, 假如2棵树之间的距离严格小于H, 那么1棵树向另一棵树方向倒会撞倒另1棵树, 求最后树覆盖线的期望长度. 1<=N<=2000, 1<=H<=10^8, -10^8<=xi<=10^8, 0<=p<=1

    题解:

    期望dp. 先对树的位置排序.

    dp(l, r, ls, rs)表示第l~r棵树没倒, ls,rs分别是第l-1棵树和第r+1棵树的倒向(0左1右).

    枚举第l棵树倒下或者第r棵树倒下(概率均为0.5), 再枚举倒下的方向(概率:左边p, 右边(1-p)), 以此来状态转移.

    时间复杂度O(N^2)

    #include<cstdio>
    #include<cassert>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    const int maxn = 2009;
    
    bool vis[maxn][maxn][2][2];
    double dp[maxn][maxn][2][2], p;
    int N, H, x[maxn], L[maxn], R[maxn];
    
    double Dp(int l, int r, int ls,int rs) {
    	if(l > r) return 0;
    	double &t = dp[l][r][ls][rs];
    	if(vis[l][r][ls][rs]) return t;
    	vis[l][r][ls][rs] = true;
    	
    	int _l = x[l - 1] + (ls ? H : 0), _L = max(l, L[r]);
    	int _r = x[r + 1] - (rs ? 0 : H), _R = min(r, R[l]);
    	t = p * (min(x[l] - _l, H) + Dp(l + 1, r, 0, rs)) // left -> left
    	  + (1 - p) * (min(_r - x[r], H) + Dp(l, r - 1, ls, 1)); // right -> right
    	t += (1 - p) * (x[_R] - x[l] + min(_r - x[_R], H) + Dp(_R + 1, r, 1, rs)) // left -> right
    	   + p * (x[r] - x[_L] + min(x[_L] - _l, H) + Dp(l, _L - 1, ls, 0)); // right -> left
    	
    	return t *= 0.5;
    }
    
    void Init() {
    	scanf("%d%d%lf", &N, &H, &p);
    	x[0] = -500000000;
    	x[N + 1] = 500000000;
    	for(int i = 1; i <= N; i++) scanf("%d", x + i);
    	sort(x + 1, x + N + 1);
    	memset(vis, 0, sizeof vis);
    	
    	L[0] = 1;
    	for(int i = 1; i <= N; i++)
    		L[i] = (x[i - 1] + H > x[i] ? L[i - 1] : i);
    	R[N + 1] = N;
    	for(int i = N; i >= 1; i--)
    		R[i] = (x[i] + H > x[i + 1] ? R[i + 1] : i);
    }
    
    int main() {
    	
    	Init();
    	printf("%.10lf
    ", Dp(1, N, 0, 1));
    	
    	return 0;
    }
    

    写完啦~~

  • 相关阅读:
    【资源共享】JNI 课题
    Firefly自动售货机解决方案
    【资源共享】Android开发技巧整理
    【资源共享】《Rockchip IO-Domain 开发指南 V1.0》
    【人脸识别+硬件】Firefly推出可商业化的人脸识别方案
    【技术案例】双目摄像头数据采集
    windows环境常用网络命令测试和分析(51cto实验01~02)
    利用三层交换机实现VLAN间路由配置
    c++11
    归并排序
  • 原文地址:https://www.cnblogs.com/JSZX11556/p/5199528.html
Copyright © 2011-2022 走看看