• Codeforces Round #749


    还我Rating

    我已经暴怒了,神(^{ t TM})前六道都是构造题,我真的受够了!!!

    再也不拿大号打 ( t Div1+Div2) 了,下次我打 ( t Div1) 直接杀穿,吊打小 ( t T) 做梦不是问题。

    ( t RNM),退钱!!!

    F. Defender of Childhood Dreams

    题目描述

    点此看题

    (n) 个点,对于 (a<b) 都有一条有向边 ((a,b)),现在要对边染色,问最少的颜色数使得不存在一条长度为 (k) 的边同色路径,并给出构造方案。

    (2leq k<nleq 1000)

    解法

    我们递归的构造,首先我们把 (1sim n) 排成一个序列,然后划分成 (k) 个长度相等的连续段(或者尽可能相近),那么段间的边都用一种颜色 (x),然后递归下去的子段都不用颜色 (x) 了。构造方案合法,因为最长的同色路径是跨段的 (k-1),并且使用的颜色数为 (lceillog _k n ceil)

    那么我们只需要证明颜色数的下界就是 (lceillog_k n ceil) 即可,这等价于证明颜色数 (c) 能承受的最大点数是 (k^c)

    直接上归纳法,(0) 个颜色能承受的最大点数是 (1),假设 (c-1) 种颜色能够承受的最大点数是 (k^{c-1})

    我们取颜色 (c),对还未染色的原图任意染色,那么得到集合 (s_0,s_1,s_2..s_{k-1})(s_i) 表示从它开始存在长度为 (i)(c) 同色路径,那么每个集合内部是没有 (c) 边的,可以知道最大点数是 (k^{c-1}),因为一共有 (k) 个这样的集合,所以最大点数是 (kcdot k^{c-1}=k^c)

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 1005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k,a[M][M];
    signed main()
    {
    	n=read();k=read();
    	for(int i=0;i<n;i++)
    		for(int j=i+1;j<n;j++)
    		{
    			int u=i,v=j;
    			while(u!=v)
    			{
    				u/=k,v/=k;
    				a[i][j]++;
    			}
    			m=max(m,a[i][j]);
    		}
    	printf("%d
    ",m);
    	for(int i=0;i<n;i++)
    		for(int j=i+1;j<n;j++)
    			printf("%d ",a[i][j]);
    }
    

    G. Omkar and Time Travel

    题目描述

    点此看题

    (n) 个传送门 ((a_i,b_i)),如果你走到 (b_i) 并且这个传送门处于开放状态,你会立刻传送到 (a_i),当前传送门关闭,并且所有 (a_i<a_j) 的传送门 (j) 都会立即变为开放状态。

    给定 (t) 个数表示一个集合,问让这个集合所有的传送门变成关闭状态的最小传送次数,答案模 (1e9+7)

    (1leq nleq 2 imes 10^5,1leq a_i<b_ileq 2n),保证 (a_i,b_i) 互不相同。

    解法

    本来我想设计状态表示点亮一段前缀的花费,但是不方便求答案而且难以转移。

    询问是给定了一个集合,这提示你要把贡献放在单点上,可以设 (dp[i]) 表示点亮 (i) 的花费。

    那么现在还剩下两个问题:哪些点需要贡献?(dp) 如何转移?对于第一个问题,首先在集合中的点是要贡献的,再者若 (a_i<a_j,b_i<b_j) 并且 (j) 在集合中,(i) 是要产生贡献的,因为点亮 (j) 在逻辑上需要 (i) 被点亮

    第二个问题如何转移?首先考虑按什么顺序,因为点亮这个点需要消除一段后缀,在逻辑上需要这段后缀被点亮,如果满足 (a_i<a_j,b_j<b_i) 那么点亮 (i) 时就需要 (j) 被点亮,那么 (dp_i=sum dp_j+1)

    为什么我上文一再强调“在逻辑上”,因为这题是按照 (a_i) 的偏序关系做的,而不是按照时间顺序做的。

    总结

    考虑答案的形式对于 (dp) 也很重要,这题答案显然是对若干个单点提出了要求,所以我们要求单点的贡献,算贡献是把所有点独立开来,你可以在思想上让其他点为这个点而服务。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    const int M = 400005;
    const int MOD = 1e9+7;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,ans,a[M],b[M],p[M],dp[M],f[M],vis[M],suf[M];
    int cmp(int x,int y) {return a[x]<a[y];}
    int lowbit(int x)
    {
    	return x&(-x);
    }
    void ins(int x,int c)
    {
    	for(int i=x;i<=2*n;i+=lowbit(i))
    		f[i]=(f[i]+c)%MOD;
    }
    int ask(int x)
    {
    	int res=0;
    	for(int i=x;i>=1;i-=lowbit(i))
    		res=(res+f[i])%MOD;
    	return res;
    }
    signed main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read(),b[i]=read(),p[i]=i;
    	m=read();
    	for(int i=1;i<=m;i++)
    		vis[read()]=1;
    	//
    	sort(p+1,p+1+n,cmp);
    	for(int i=n;i>=1;i--)
    	{
    		if(vis[p[i]]) suf[i]=b[p[i]];
    		suf[i]=max(suf[i],suf[i+1]);
    	}
    	for(int i=1;i<=n;i++)
    		if(suf[i]>b[p[i]]) vis[p[i]]=1;
    	//
    	for(int i=n;i>=1;i--)
    	{
    		dp[i]=ask(b[p[i]])+1;
    		ins(b[p[i]],dp[i]);
    		if(vis[p[i]]) ans=(ans+dp[i])%MOD;
    	}
    	printf("%d
    ",ans);
    }
    

    H. Omkar and Tours

    题目描述

    点此看题

    有一棵 (n) 个点的树,每个点有一个快乐度 (a_i),每条边有一个容量 (c_i) 花费 (t_i)

    (q) 次询问,每个有一个车辆 (v) 的车队从 (x) 出发,他们只能经过 (c_igeq v) 的边。定义路径的花费为路径上所有边花费的最大值,求能到达点的最大快乐值 和 所有到最快乐点的简单路径花费的最大值。

    (2leq nleq 2 imes 10^5,1leq qleq 2 imes 10^5)

    解法

    本来自己想了一个辣鸡 (O(nlog^2 n)) 的重口味做法,不足为外人道也。

    可以把所有询问离线,那么动态维护连通块就可以解决 (cgeq v) 的限制。

    如果所有点的快乐值两两不同的话,我们可以记录连通块内最大的快乐值和这个点,然后直接求到这个点树上路径的最大值即可,变成了一个倍增板子。

    回到原始问题上来,因为存在多条路径,但是感性理解一下这些路径是有很大重复的。这里想提出名为最值放缩的技巧:第一种情况是可以把某些不优的情况混入最大值去求,可以去掉多余的判断;第二种情况是可以对一个元素多次求最大值,只要最后把所有元素都考虑到即可。

    本题适用第二种情况,一个关键的 ( t observation) 是:我们取一个最快乐点 (a),求出任意 (m)(a) 的路径花费最大值,和 (x)(a) 的花费最大值,那么 (x) 到任意 (m) 的最大值就是这两者取 (max)

    你可以把它理解成我们先走到 (a),然后再走到任意点 (m),花费最大值是不变的。

    那么可以维护 (best[u]) 表示以 (u) 为根的连通块中两两最快乐点间的路径最大花费,那么在合并的时候如果遇到最大快乐值相等的情况,(best[u]=max(best[u],best[v],w(a,b))),其中 (a,b) 分别表示 (u,v) 中任意的最快乐点。

    所以只需要打一个倍增即可,时间复杂度 (O(nlog n))

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int M = 200005;
    #define pii pair<int,int>
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,a[M],id[M],fa[M],p[M][20],mx[M][20];
    int tot,dep[M],f[M],hp[M],ct[M],bst[M];
    struct edge
    {
    	int v,c,next;
    }e[2*M];
    struct node
    {
    	int u,v,x;
    	bool operator < (const node &b) const
    	{
    		return x>b.x;
    	}
    }s[M],q[M];
    int find(int x)
    {
    	if(fa[x]!=x) fa[x]=find(fa[x]);
    	return fa[x];
    }
    void dfs(int u,int fa)
    {
    	p[u][0]=fa;
    	dep[u]=dep[fa]+1;
    	for(int i=1;i<20;i++)
    	{
    		int t=p[u][i-1];
    		p[u][i]=p[t][i-1];
    		mx[u][i]=max(mx[u][i-1],mx[t][i-1]);
    	}
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v,c=e[i].c;
    		if(v==fa) continue;
    		mx[v][0]=c;
    		dfs(v,u);
    	}
    }
    int lca(int u,int v)
    {
    	int r=0;
    	if(dep[u]<dep[v]) swap(u,v);
    	for(int i=19;i>=0;i--)
    		if(dep[p[u][i]]>=dep[v])
    		{
    			r=max(r,mx[u][i]);
    			u=p[u][i];
    		}
    	if(u==v) return r;
    	for(int i=19;i>=0;i--)
    		if(p[u][i]^p[v][i])
    		{
    			r=max(r,mx[u][i]);
    			r=max(r,mx[v][i]);
    			u=p[u][i];v=p[v][i];
    		}
    	r=max(r,mx[u][0]);
    	r=max(r,mx[v][0]);
    	return r;
    }
    void add(int u,int v)
    {
    	u=find(u);v=find(v);
    	fa[v]=u;
    	if(a[u]>a[v]) return ;
    	if(a[u]<a[v])
    	{
    		a[u]=a[v];
    		id[u]=id[v];
    		bst[u]=bst[v];
    		return ;
    	}
    	bst[u]=max(bst[u],bst[v]);
    	bst[u]=max(bst[u],lca(id[u],id[v]));
    }
    signed main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read(),id[i]=fa[i]=i;
    	for(int i=1;i<n;i++)
    	{
    		s[i].u=read();s[i].v=read();
    		s[i].x=read();
    		int u=s[i].u,v=s[i].v,c=read();
    		e[++tot]=edge{v,c,f[u]},f[u]=tot;
    		e[++tot]=edge{u,c,f[v]},f[v]=tot;
    	}
    	for(int i=1;i<=m;i++)
    	{
    		q[i].x=read();
    		q[i].u=read();
    		q[i].v=i;
    	}
    	dfs(1,0);
    	sort(s+1,s+n);
    	sort(q+1,q+1+m);
    	for(int i=1,j=1;i<=m;i++)//process each query
    	{
    		while(j<n && s[j].x>=q[i].x)
    		{
    			add(s[j].u,s[j].v);
    			j++;
    		}
    		int rt=find(q[i].u);
    		hp[q[i].v]=a[rt];
    		ct[q[i].v]=max(bst[rt],lca(id[rt],q[i].u));
    	}
    	for(int i=1;i<=m;i++)
    		printf("%d %d
    ",hp[i],ct[i]);
    }
    
  • 相关阅读:
    Could not update ICEauthority file /var/lib/gdm/.ICEauthority
    反爬虫中技术点的定义
    反爬虫一些问题
    反爬虫准备
    题解「BZOJ4621 Tc605」
    题解「ZJOI2019 语言」
    题解「清华集训2012 序列操作」
    题解「CF1174F Ehab and the Big Finale」
    题解「CF516D Drazil and Morning Exercise」
    题解「HNOI2018 寻宝游戏」
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15430766.html
Copyright © 2020-2023  润新知