zoukankan      html  css  js  c++  java
  • [CLYZ2017]day7

    采蘑菇

    image

    solution

    30分

    \(dfs\)利用类似尺取的方式算出每个点到其他点的颜色数.

    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define M 2005
    #define N 300005
    #define min(a,b) a<b?a:b
    #define max(a,b) a>b?a:b
    using namespace std;
    typedef long long ll;
    struct graph{
    	int nxt,to;
    }e[N<<1];
    ll ans,sum;
    int c[N],g[N],t[N],tot[N],n,cnt;
    bool v[M];
    inline int read(){
    	int ret=0;char c=getchar();
    	while(!isdigit(c))
    		c=getchar();
    	while(isdigit(c)){
    		ret=(ret<<1)+(ret<<3)+c-'0';
    		c=getchar();
    	}
    	return ret;
    }
    inline void addedge(int x,int y){
    	e[++cnt].nxt=g[x];g[x]=cnt;e[cnt].to=y;
    }
    inline void dfs(int u){
    	if(!tot[c[u]]) ++sum;
    	++tot[c[u]];ans+=sum;
    	for(int i=g[u];i;i=e[i].nxt)
    		if(!v[e[i].to]){
    			v[e[i].to]=true;
    			dfs(e[i].to);
    		}
    	if(!(--tot[c[u]])) --sum;
    }
    inline void Aireen(){
    	n=read();
    	for(int i=1;i<=n;++i)
    		c[i]=read();
    	for(int i=1,j,k;i<n;++i){
    		j=read();k=read();
    		addedge(j,k);addedge(k,j);
    	}
    	if(n<=2000){
    		for(int i=1;i<=n;++i){
    			memset(v,0,sizeof(v));
    			ans=0;v[i]=true;dfs(i);
    			printf("%lld\n",ans);
    		}
    		return;
    	}
    }
    int main(){
    	freopen("mushroom.in","r",stdin);
    	freopen("mushroom.out","w",stdout);
    	Aireen();
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    序列游戏

    image

    solution

    100分

    先认识一个性质,会被删的一段数的大小趋势如下图:

    \(f[i][j][k]\)表示对于区间\([i,j]\),\(j\)与后面的\(k\)个数会在一段中被删除,可以出现峰的最大得分.
    \(g[i][j][k]\)表示对于区间\([i,j]\),\(j\)与后面的\(k\)个数会在一段中被删除,不可以出现峰的最大得分.

    \(f[i][j][k]=max\begin{cases}f[i][j-1][0]+v[k+1]&\\f[i][q][k+1]+f[q+1][j-1][0]&a_q=a_j+1,q<j\\g[i][q][k+1]+f[q+1][j-1][0]&a_q=a_j-1,q<j\end{cases}\)

    即从直接和后面\(k\)个数合并,以及删去\([q+1,j-1]\)后合并.

    \(g[i][j][k]=max\begin{cases}f[i][j-1][0]+v[k+1]&\\g[i][q][k+1]+f[q+1][j-1][0]&a_q=a_j-1,q<j\end{cases}\)

    由于不需要全删除,\(dp[i]\)表示前\(i\)个数的最大价值.

    \(dp[i]=max\begin{cases}dp[i-1]\\dp[j]+f[j+1][i][0]&j\in[0,i)\end{cases}\)

    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define K 20
    #define N 160
    #define M 460
    #define max(a,b) a>b?a:b
    using namespace std;
    typedef long long ll;
    int f[N][N][N],g[N][N][N],dp[N];
    int a[M][K],b[N],t[M],v[N],p[M],n,m;
    inline void Aireen(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)
    		scanf("%d",&v[i]);
    	for(int i=1,k;i<=n;++i){
    		scanf("%d",&b[i]);
    		p[++m]=b[i]-1;p[++m]=b[i];p[++m]=b[i]+1;
    	}
    	sort(p+1,p+1+m);
    	m=unique(p+1,p+m+1)-p-1;
    	for(int i=1;i<=n;++i){
    		b[i]=lower_bound(p+1,p+1+m,b[i])-p;
    	}
    	for(int i=1;i<=n;++i)
    		a[b[i]][++t[b[i]]]=i;
    	for(int i=1;i<=n;++i)
    		for(int k=0;i+k<=n;++k)
    			f[i][i][k]=g[i][i][k]=v[k+1];
    	for(int q=1;q<=n;++q)
    		for(int i=1,j,x,y;i+q<=n;++i){
    			j=i+q;x=b[j]+1;y=b[j]-1;
    			for(int k=0;j+k<=n;++k){
    				f[i][j][k]=f[i][j-1][0]+v[k+1];
    				g[i][j][k]=f[i][j-1][0]+v[k+1];
    				for(int l=t[x];a[x][l]>=i;--l)
    					if(a[x][l]<j){
    						f[i][j][k]=max(f[i][j][k],f[i][a[x][l]][k+1]+f[a[x][l]+1][j-1][0]);
    					}
    				for(int l=t[y];a[y][l]>=i;--l)
    					if(a[y][l]<j){
    						f[i][j][k]=max(f[i][j][k],g[i][a[y][l]][k+1]+f[a[y][l]+1][j-1][0]);
    						g[i][j][k]=max(g[i][j][k],g[i][a[y][l]][k+1]+f[a[y][l]+1][j-1][0]);
    					}
    			}
    		}
    	for(int i=1;i<=n;++i){
    		dp[i]=dp[i-1];
    		for(int j=0;j<i;++j)
    			dp[i]=max(dp[i],dp[j]+f[j+1][i][0]);
    	}
    	printf("%d\n",dp[n]);
    }
    int main(){
    	freopen("sequence.in","r",stdin);
    	freopen("sequence.out","w",stdout);
    	Aireen();
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    

    石子游戏

    image

    solution

    100分

    约定层数和最后一层同奇偶的层为奇数层,不同的为偶数层.

    显然偶数层的石子数不影响必胜态,因为后手移动偶数层的,先手可以通过将其移到下一层(偶数层)或移除的方式恢复必胜态.

    所以只需求奇数层的石子的异或和.

    求方案数也就是求有多少种移动方案可以使得奇数层异或和为\(0\),分类讨论枚举即可.

    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<stack>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 65536
    using namespace std;
    typedef long long ll;
    int a[N],k,t,sum,cnt;
    inline int read(){
    	int ret=0;char c=getchar();
    	while(!isdigit(c))
    		c=getchar();
    	while(isdigit(c)){
    		ret=(ret<<1)+(ret<<3)+c-'0';
    		c=getchar();
    	}
    	return ret;
    }
    inline bool chk(int i,int j){
    	return (sum^a[j])>a[j]&&(sum^a[j])-a[j]<=a[i];
    }
    inline void Aireen(){
    	scanf("%d",&t);;
    	while(t--){
    		scanf("%d",&k);
    		for(int i=1;i<(1<<k);++i)
    				scanf("%d",&a[i]);
    		cnt=sum=0;
    		for(int i=k;i>0;i-=2)
    			for(int j=(1<<i-1);j<(1<<i);++j)
    				sum^=a[j];
    		if(!sum){
    			puts("0");continue;
    		}
    		for(int i=k;i>0;i-=2)
    			for(int j=(1<<i-1);j<(1<<i);++j)
    				if((sum^a[j])<a[j]){
    					if(i==k) ++cnt;
    					else cnt+=2;
    				}
    		for(int i=k-1;i>0;i-=2)
    			for(int j=(1<<i-1);j<(1<<i);++j){
    				if(chk(j,j<<1)) ++cnt;
    				if(chk(j,j<<1|1)) ++cnt;
    			}
    		printf("%d\n",cnt);
    	}
    }
    int main(){
    	freopen("stone.in","r",stdin);
    	freopen("stone.out","w",stdout);
    	Aireen();
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    我的大学学习之路
    拉勾上的一道题目
    python中文处理之encode/decode函数
    几个容易出错的css盒子模型细节
    洗牌算法shuffle
    判断正整数是否对称
    一种快速求fibonacci第n个数的算法
    利用正则表达式作为string.split seprator
    docker部分命令
    idea上传项目到GitHub
  • 原文地址:https://www.cnblogs.com/AireenYe/p/CLYZ2017day7.html
Copyright © 2011-2022 走看看