zoukankan      html  css  js  c++  java
  • Codeforces Round #721 (Div. 2)

    B. Palindrome Game

    题目描述

    点此看题

    解法

    考试时候推规律没推出来,直接上了个 (O(n^2)) 暴力 (dp)

    回文串是很好做的,那不是回文怎么办。有一个开天辟地的结论是:( t Alice) 不会输。

    证明这个结论是很简单的,考虑 (2) 操作的本质其实是跳过一次操作

    那么如果 ( t Alice) 选了 (1) 操作输了,那么他可以选择 (2) 操作,就逼迫 ( t Bob) 接受了输的状态,另一种情况 ( t Alice) 也不会输。

    所以博弈游戏要考虑有没有反悔机制!

    根据这个大结论的指导,我们可以给出 ( t Alice) 具体的不败策略:

    • 如果还有较多的非对称 (0),那么 ( t Alice) 选择跳过操作,那么 ( t Bob) 只能拿走这些非对称 (0),要不然会增加更多的非对称 (0)
    • 如果非对称 (0) 只有一个,那么 ( t Alice) 看是否有对称 (0),如果有的话他必赚 (2) 元或者 (1) 元。如果没有的话那么 ( t Alice) 还是跳过操作可以赚 (1) 元。

    不难发现平局仅出现在 (11001) 之类的情况,也就是 ( t Alice) 选择非对称 (0) 之后只能赚 (1) 元。

    D. MEX Tree

    题目描述

    点此看题

    给定一棵 (n) 个点的树,从 (0) 开始编号,统计路径上点集 (mex)(0leq ileq n) 的路径个数。

    (2leq nleq 2cdot 10^5)

    解法

    这个东西差分的话会好做一些,具体来说设 (ans[i])(mexgeq i) 的路径条数,那么 (mex)(i) 的路径条数是:

    [ans[i]-ans[i+1] ]

    那么怎么统计 (ans[i]) 呢?我们可以强制点 ([0,i-1]) 出现在路径中,那么就一定满足路径的 (mexgeq i)

    稍微想一下就知道这些点构成了一条链的形状,所以我们可以维护链的左端点和右端点,然后每次只需要新加入点 (i) 就可以强制 ([0,i]) 出现在路径中了。考虑 (i) 合法的位置有三种:左端点的子树内;右端点的子树内;链上。如果不在合法的位置直接跳出循环即可。

    然后就是讨论一下就行了,注意一定要单独考虑一个是另一个祖先的情况,要不然会被搞昏。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 200005;
    #define int long long
    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 T,n,tot,Ind,siz[M],ans[M],f[M];
    int l[M],r[M],a[M],dep[M],fa[M][20];
    struct edge
    {
    	int v,next;
    }e[2*M];
    void dfs(int u,int p)
    {
    	dep[u]=dep[p]+1;fa[u][0]=p;
    	for(int i=1;i<20;i++)
    		fa[u][i]=fa[fa[u][i-1]][i-1];
    	l[u]=++Ind;siz[u]=1;
    	for(int i=f[u];i;i=e[i].next)
    	{
    		int v=e[i].v;
    		if(v==p) continue;
    		dfs(v,u);
    		siz[u]+=siz[v]; 
    	}
    	r[u]=Ind;
    }
    int get(int u,int v)
    {
    	for(int i=19;i>=0;i--)
    		if(dep[fa[u][i]]>dep[v])
    			u=fa[u][i];
    	return u;
    }
    int in(int x,int y)
    {
    	return l[x]<=l[y] && l[y]<=r[x]; 
    }
    signed main()
    {
    	T=read();
    	while(T--)
    	{
    		n=read();Ind=tot=0;
    		for(int i=0;i<=n+1;i++)
    			a[i]=ans[i]=f[i]=0;
    		for(int i=1;i<n;i++)
    		{
    			int u=read(),v=read();
    			e[++tot]=edge{v,f[u]},f[u]=tot;
    			e[++tot]=edge{u,f[v]},f[v]=tot;
    		}
    		dfs(0,0);
    		ans[0]=n*(n-1)/2;
    		for(int i=f[0],s=1;i;i=e[i].next)
    		{
    			int v=e[i].v;
    			ans[1]+=s*siz[v];s+=siz[v];
    		}
    		int x=0,y=0;a[0]=1;
    		for(int i=1;i<n;i++)
    		{
    			if(a[i])//this node has already appeared
    			{
    				ans[i+1]=ans[i];//none effect
    				continue;
    			}
    			int rx=in(x,y),ry=in(y,x),t1=get(y,x),t2=get(x,y);
    			if((x==y) || (rx && in(x,i) && !in(t1,i)) || (!rx && in(x,i)))
    			{
    				int t=i;
    				while(t!=x) a[t]=1,t=fa[t][0];
    				x=i;
    			}
    			else if((ry && in(y,i) && !in(t2,i)) || (!ry && in(y,i)))
    			{
    				int t=i;
    				while(t!=y) a[t]=1,t=fa[t][0];
    				y=i;
    			}
    			else break;//the answer is always 0
    			t1=get(y,x);t2=get(x,y);
    			if(in(x,y)) ans[i+1]=(n-siz[t1])*siz[y];
    			else if(in(y,x)) ans[i+1]=(n-siz[t2])*siz[x];
    			else ans[i+1]=siz[x]*siz[y];
    		}
    		for(int i=0;i<=n;i++)
    			printf("%lld ",ans[i]-ans[i+1]);
    		puts("");
    	}
    }
    

    E. Partition Game

    题目描述

    点此看题

    解法

    最后一道反而是最简单的一道

    因为 (k) 很小所以可以分层 (dp),设 (dp[i]) 表示划分到 (i) 的最小代价,可以写出转移:

    [dp[i]=min(dp'[j-1]+cost(j,i)) ]

    发现就是代价比较难算,多半是用数据结构维护了。考虑 (i-1) 移动到 (i) 的过程,设和 (i) 同颜色的前驱位置是 (x),那么这种颜色在 (jin (x,i]) 的贡献都是不变的,在 (jin[1,x]) 的贡献都会增加 (i-x),所以用线段树维护即可,时间复杂度 (O(nklog n))

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int M = 35005;
    const int inf = 0x3f3f3f3f;
    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,k,a[M],b[M],s[4*M],tag[4*M],dp[105][M];
    void down(int i)
    {
    	if(!tag[i]) return ;
    	s[i<<1]+=tag[i];
    	s[i<<1|1]+=tag[i];
    	tag[i<<1]+=tag[i];
    	tag[i<<1|1]+=tag[i];
    	tag[i]=0; 
    }
    void add(int i,int l,int r,int L,int R,int x)
    {
    	if(L>r || l>R) return ;
    	if(L<=l && r<=R)
    	{
    		tag[i]+=x;s[i]+=x;
    		return ;
    	}
    	int mid=(l+r)>>1;down(i);
    	add(i<<1,l,mid,L,R,x);
    	add(i<<1|1,mid+1,r,L,R,x);
    	s[i]=min(s[i<<1],s[i<<1|1]);
    }
    int ask(int i,int l,int r,int L,int R)
    {
    	if(L>r || l>R) return inf;
    	if(L<=l && r<=R) return s[i];
    	int mid=(l+r)>>1;down(i);
    	return min(ask(i<<1,l,mid,L,R),ask(i<<1|1,mid+1,r,L,R));
    }
    signed main()
    {
    	n=read();k=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read();
    	memset(dp,0x3f,sizeof dp);
    	dp[0][0]=0;
    	for(int l=1;l<=k;l++)
    	{
    		memset(s,0,sizeof s);
    		memset(tag,0,sizeof tag);
    		memset(b,0,sizeof b);
    		for(int i=1;i<=n;i++)
    		{
    			add(1,0,n,i-1,i-1,dp[l-1][i-1]);
    			if(b[a[i]]) add(1,0,n,0,b[a[i]]-1,i-b[a[i]]);
    			dp[l][i]=ask(1,0,n,0,i-1);
    			b[a[i]]=i;
    		}
    	}
    	printf("%d
    ",dp[k][n]);
    }
    
  • 相关阅读:
    网络爬虫基础练习
    综合练习:词频统计
    Hadoop综合大作业
    理解MapReduce
    熟悉常用的HBase操作
    第三章、熟悉常用的HDFS操作
    爬虫大作业
    数据结构化与保存
    使用正则表达式,取得点击次数,函数抽离
    爬取校园新闻首页的新闻
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14798544.html
Copyright © 2011-2022 走看看