zoukankan      html  css  js  c++  java
  • Codeforces Round #384 Div.2

    A:Vladik and flights

    题目大意:给定一个长度为n的01串和a,b两个位置,如果一个位置和另一个位置上的数相同,那么这两个位置之间相互到达的代价是0,否则代价是这两个位置的距离,问从a到b最少需要多少代价。

    思路:答案非0即1,0的情况就是这两个位置上的数相同,1就代表一定同时存在0和1,那么就把这两个点分别移到0,1交界的位置,那么答案就是1。

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define maxn 100005
    const int inf=1e9;
    
    int n,st,ed,ans;
    char s[maxn];
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    	return x*f;
    }
    
    inline void print(int x){
    	if (x>=10) print(x/10);putchar(x%10+'0');
    }
    
    int main(){
    	n=read(),st=read(),ed=read();
    	scanf("%s",s+1);
    	puts(s[st]==s[ed]?"0":"1");
    	return 0;
    }
    

    B:Chloe and the sequence

    题目大意:给定一个序列的构造方式,第一个序列是[1],第二个序列是[1,2,1],第三个序列是[1,2,1,3,1,2,1],然后第n个序列就是把第n-1个序列翻折后再在最中间加一个数n,然后问第n个序列中第k个数是多少。

    思路:设len为第n个序列的长度(显然len=(1<<n)-1),mid为最中间数的位置,然后如果k>mid,显然有a[k]=a[len-k+1](满足对称性),然后如果k<mid,那么就可以把序列折半看成是一个子问题,k==mid就输出这是第几个序列就好了。

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    int n;
    long long len,k;
    
    inline long long read(){
    	long long x=0,f=1;char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    	return x*f;
    }
    
    inline void print(int x){
    	if (x>=10) print(x/10);putchar(x%10+'0');
    }
    
    int main(){
    	n=read(),k=read();
    	len=(1ll<<n)-1;
    	for (int i=n;i;i--){
    		if (k==len/2+1){printf("%d
    ",i);return 0;}
    		if (k>len/2+1) k=len-k+1;
    		len>>=1;
    	}
    	return 0;
    }
    

    C:Vladik and fractions

    题目大意:给定n,找到三个互不相同的整数使得2/n=1/a+1/b+1/c。

    思路:2/n=1/n+1/(n+1)+1/(n*(n+1)),注意互不相同要特判n=1的情况。

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    int n,m;
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    	return x*f;
    }
    
    inline void print(int x){
    	if (x>=10) print(x/10);putchar(x%10+'0');
    }
    
    int main(){
    	n=read();
    	if (n==1){puts("-1");return 0;}
    	else{printf("%d %d %d
    ",n,n+1,n*(n+1));}
    	return 0;
    }
    

    D:Chloe and pleasant prizes

    题目大意:一颗以1为根的树,给定每个点的点权,找到两颗没有交集的子树并使它们的权值和尽量大,如果找不到输出-1。

    思路:首先找不到的情况就是一条链的情况(当然1必须是该链的一个端点),然后答案直接就tree dp就好了,和tree dp求树最长链基本一毛一样。

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define maxn 200005
    const long long inf=1e18;
    
    int n,m,maxdeg,tot;
    long long ans;
    
    int now[maxn],pre[maxn*2],son[maxn*2],deg[maxn];
    long long sum[maxn];
    bool vis[maxn];
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    	return x*f;
    }
    
    inline void print(int x){
    	if (x>=10) print(x/10);putchar(x%10+'0');
    }
    
    void add(int a,int b){
    	son[++tot]=b;
    	pre[tot]=now[a];
    	now[a]=tot;
    }
    
    void link(int a,int b){
    	add(a,b),add(b,a);
    }
    
    long long tree_dp(int x,int fa){
    	long long mx=-inf,tx=-inf;
    	for (int p=now[x];p;p=pre[p])
    		if (son[p]!=fa){
    			long long tmp=tree_dp(son[p],x);
    			if (tmp>mx) tx=mx,mx=tmp;
    			else tx=max(tx,tmp);
    			ans=max(ans,tx+mx);
    			sum[x]+=sum[son[p]];
    		}
    	return max(mx,sum[x]);
    }
    
    void dfs(int x,int fa){
    	vis[x]=1;
    	for (int p=now[x];p;p=pre[p]) if (son[p]!=fa){dfs(son[p],x);break;}
    }
    
    bool check(){
    	dfs(1,0);
    	for (int i=1;i<=n;i++) if (!vis[i]) return 1;
    	return 0;
    }
    
    int main(){
    	n=read(); ans=-inf;
    	for (int i=1;i<=n;i++) sum[i]=read();
    	for (int i=1,a,b;i<n;i++) a=read(),b=read(),link(a,b);
    	if (!check()){puts("Impossible"); return 0;}
    	tree_dp(1,0);
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    E:Vladik and cards

    题目大意:在给定的只由1到8组成的序列中,找到一个子序列,使得其满足以下条件:1.任意一个数字出现的次数之间不能相差超过1,也就是说每个数字要么出现x次,要么出现x+1次。2.如果子序列中第一次出现某个数字,就必须全部出现完,也就是说相同的数字在子序列中一定是一条线段而不能断开。在以上基础上要求子序列长度最长。

    思路:状压dp很显然,关键是二分比较难想,因为一个数字要么出现x次,要么出现x+1次,那么我们就二分这个x(显然具有单调性),然后考虑如何去check就好了。

    因为答案肯定是形如x*y+(8-y)*(x+1),这个y就是有多少个数字出现x次,然后就可以令状态f[i][S]表示当前状态为S,当前位于第i个时的y的值,然后枚举当前选哪个数字转移即可。

    #include<cmath>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define maxn 1005
    #define maxs (1<<8)
    const int inf=1e9;
    
    int n,ans=-inf;
    int a[maxn],cnt[9];
    int f[maxn][maxs];
    
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
    	for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    	return x*f;
    }
    
    vector<int> v[9];
    
    void clear(){memset(cnt,0,sizeof(cnt)),memset(f,-1,sizeof(f));}
    
    bool check(int mid){
    	clear();f[1][0]=0;
    	for (int i=1;i<=n;i++){
    		for (int S=0;S<maxs;S++){
    			if (f[i][S]==-1) continue;
    			for (int j=1;j<=8;j++)
    				if (!(S&(1<<(j-1)))){
    					int x=cnt[j]+mid;
    					if (x>v[j].size()) continue;
    					f[v[j][x-1]+1][S|(1<<(j-1))]=max(f[v[j][x-1]+1][S|(1<<(j-1))],f[i][S]);
    					if (++x>v[j].size()) continue;
    					f[v[j][x-1]+1][S|(1<<(j-1))]=max(f[v[j][x-1]+1][S|(1<<(j-1))],f[i][S]+1);
    				}
    		}
    		cnt[a[i]]++;
    	}
    	int tt=-1;
    	for (int i=1;i<=n+1;i++) tt=max(tt,f[i][maxs-1]);
    	return tt==-1?0:(ans=max(ans,tt*(mid+1)+(8-tt)*mid),1);
    }
    
    int main(){
    	n=read();
    	for (int i=1;i<=n;i++) a[i]=read(),v[a[i]].push_back(i);
    	int l=1,r=n;
    	while (l<=r){
    		int mid=(l+r)>>1;
    		if (check(mid)) l=mid+1;
    		else r=mid-1;
    	}
    	if (ans==-inf){
    		ans=0;
    		for (int i=1;i<=8;i++) if (v[i].size()) ans++;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

      

  • 相关阅读:
    Codevs 2296 仪仗队 2008年省队选拔赛山东
    Codevs 1535 封锁阳光大学
    Codevs 1069 关押罪犯 2010年NOIP全国联赛提高组
    Codevs 1218 疫情控制 2012年NOIP全国联赛提高组
    Codevs 1684 垃圾陷阱
    洛谷 P1108 低价购买
    Vijos P1325桐桐的糖果计划
    Codevs 3289 花匠 2013年NOIP全国联赛提高组
    Codevs 2611 观光旅游(floyed最小环)
    C语言基础之彩色版C语言(内含linux)
  • 原文地址:https://www.cnblogs.com/DUXT/p/6187900.html
Copyright © 2011-2022 走看看