zoukankan      html  css  js  c++  java
  • CodeForces 1324

    第一次遇到这么水的div. 3。。。

    本来想抢D一血的,结果失败了。。然后干脆就(45mathrm{min})AK了。。。

    CodeForces比赛页面传送门

    A - Yet Another Tetris Problem

    洛谷题目页面传送门 & CodeForces题目页面传送门

    给出(n)个数,第(i)个数为(a_i)。每次操作可以选择一个(iin[1,n])并令(a_i=a_i+2),或令(forall iin[1,n],a_i=a_i-1)。问能否经过若干次操作,将(a)数组清零。本题多测。

    结论显然,能将(a)数组清零当且仅当所有数奇偶性相同。

    证明:若所有数奇偶性相同,显然可以通过若干次第(1)种操作把它们变成两两相等,再通过若干次第(2)种操作把它们都变成(0);若存在(2)个数(a_i,a_j)奇偶性不相同,则若对(a_i,a_j)做第(1)种操作,那么它们奇偶性不变,若做第(2)种操作,它们奇偶性互换,所以它们奇偶性永远不可能相同,自然也无法都变成(0)。得证。

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    void mian(){
    	int n;
    	cin>>n;
    	int x;
    	cin>>x;
    	x&=1;//a[1]的奇偶性
    	n--;
    	bool ok=true;
    	while(n--){
    		int y;
    		cin>>y;
    		ok&=x==(y&1);//奇偶性要全部相同
    	}
    	puts(ok?"YES":"NO");
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    B - Yet Another Palindrome Problem

    洛谷题目页面传送门 & CodeForces题目页面传送门

    给出(n)个数,第(i)个数为(a_i)。问是否存在长度至少为(3)的回文子序列。本题多测。

    (nin[3,5000],sum nleq5000)

    显然,任何一个回文子序列的两端相等,又因为要求长度至少为(3),所以两端的距离至少为(2)。于是存在(2)个距离至少为(2)且相等的数是存在长度至少为(3)的回文子序列的必要条件。若存在(2)个距离至少为(2)且相等的数,那么随便选择它们之间的任意一个数即可构成长度为(3)的回文子序列。于是存在(2)个距离至少为(2)且相等的数是存在长度至少为(3)的回文子序列的充分条件。综上,存在(2)个距离至少为(2)且相等的数是存在长度至少为(3)的回文子序列的充要条件。

    直接暴力枚举数对,时间复杂度(mathrm O!left(sum n^2 ight))

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=5000;
    int n;
    int a[N+1];
    void mian(){
    	cin>>n;
    	for(int i=1;i<=n;i++)cin>>a[i];
    	for(int i=1;i<=n;i++)for(int j=i+2/*距离至少为2*/;j<=n;j++)if(a[i]==a[j])/*相等*/{puts("YES");return;}
    	puts("NO");
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    C - Frog Jumps

    洛谷题目页面传送门 & CodeForces题目页面传送门

    (n+2)个格子,编号(0sim n+1)(forall iin[1,n]),格子(i)上有一个字母(a_iin{ exttt L, exttt R}),分别表示在格子(i)上只能往左、右跳。青蛙一开始站在格子(0)(a_0= exttt R)。求一个最小的(d),表示青蛙一次能跳(1sim d)格且不能出格,使得青蛙能到达格子(n+1)。保证总有符合要求的(d)。本题多测。

    首先,不难发现对于任意一个(d),能到达的格子组成前缀。证明:反证法。若不组成前缀,即存在(iin[1,n])使得(i)不能到达且(i+1)能到达。此时必存在一个(xin[0,i))使得(a_x= exttt R)(x)能到达([i+1,n+1])中任意一个格子(y)。根据青蛙跳的规则,([x+1,y])内任意一个格子都能从(x)到达,又(x<i,y>iRightarrow iin[x+1,y]),与(i)不能到达矛盾。得证。

    接下来,又双叒叕不难发现往左跳的格子不起丝毫作用,即任意一个能到达的格子都可以只往右跳来到达。证明:数学归纳法。设对于某一个(d),能到达的格子组成的前缀为([0,r](rgeq0))

    1. 格子(0)显然满足可以只往右跳来到达;

    2. (forall xin[1,r]),假设(forall iin[0,x)),格子(i)都满足可以只往右跳来到达。对于(x)的某一种到达方式,设最后一步是由(y)跳过来的,分(2)种情况:

      1. (y<x)。此时最后一步一定是往右跳。再加上(forall iin[0,x)),格子(i)都满足可以只往右跳来到达,可以得出格子(x)满足可以只往右跳来到达;
      2. (y>x)。此时在(y)的到达方式中,必有一步是从(zin[1,x))跳到(xxin[x,n+1])。根据青蛙跳的规则,([z+1,xx])内任意一个格子都能从(z)到达,又(x>z,xleq xxRightarrow xin[z+1,xx]),所以(x)能被(z)往右跳到达。再加上(zin[1,x)subsetneq[0,x))(forall iin[0,x)),格子(i)都满足可以只往右跳来到达,可以得出格子(x)满足可以只往右跳来到达。

      综上,若(forall iin[0,x)),格子(i)都满足可以只往右跳来到达,那么格子(x)满足可以只往右跳来到达。

    综上,得证。

    于是我们可以把所有(a_i= exttt R)(i)有序地抽出来组成序列(pos),特殊地,(pos_{|pos|}=n+1)。显然,(forall iin[1,|pos|),pos_i o pos_{i+1}),这种跳法最省(d)。要满足每次跳,起点和终点的距离都在(d)以内,所以答案是(d=maxlimits_{i=1}^{|pos|-1}{pos_{i+1}-pos_i})

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    const int N=200000;
    int n;
    char a[N+5];
    void mian(){
    	cin>>a+1;
    	n=strlen(a+1);
    	vector<int> pos;
    	pos.pb(0);
    	for(int i=1;i<=n;i++)if(a[i]=='R')pos.pb(i);
    	pos.pb(n+1);
    	int ans=0;
    	for(int i=0;i+1<pos.size();i++)ans=max(ans,pos[i+1]-pos[i]);//取最大距离 
    	cout<<ans<<"
    ";
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    

    D - Pair of Topics

    洛谷题目页面传送门 & CodeForces题目页面传送门

    给定(2)个长度为(n)的数组(a,b),求有多少个有序对((i,j))满足(i<j)(a_i+a_j>b_i+b_j)

    (ninleft[2,2 imes10^5 ight],a_i,b_iinleft[1,10^9 ight])

    (a_i+a_j>b_i+b_jLeftrightarrow a_i-b_i>b_j-a_j),这样左边仅关于(i),右边仅关于(j)。于是我们把(forall iin[1,n],a_i-b_i,b_i-a_i)(2n)个数一起离散化咯,然后类似BIT求逆序对那样建一个值域BIT,从后往前扫描,每扫到一个数(i),给答案贡献值域区间((-infty,a_i-b_i))上的区间计数的结果,再将(b_i-a_i)插入BIT。时间复杂度(mathrm O(nlog n))

    答案会爆int,记得开long long

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long//答案爆int
    #define pb push_back
    int lowbit(int x){return x&-x;}
    const int N=200000;
    int n;
    int a[N+1];
    int b[N+1];
    vector<int> nums;
    void discrete(){//离散化 
    	sort(nums.begin(),nums.end());
    	nums.resize(unique(nums.begin(),nums.end())-nums.begin());
    	for(int i=1;i<=n;i++)
    		b[i]=lower_bound(nums.begin(),nums.end(),-a[i])-nums.begin()+1,
    		a[i]=lower_bound(nums.begin(),nums.end(),a[i])-nums.begin()+1;
    }
    struct bitree{//BIT 
    	int sum[2*N+1];
    	void init(){memset(sum,0,sizeof(sum));}
    	void add(int x){//添加x 
    		while(x<=nums.size())sum[x]++,x+=lowbit(x);
    	}
    	int Sum(int x){//前缀计数 
    		int res=0;
    		while(x)res+=sum[x],x-=lowbit(x);
    		return res;
    	}
    }bit;
    signed main(){
    	cin>>n;
    	for(int i=1;i<=n;i++)scanf("%lld",a+i);
    	for(int i=1;i<=n;i++){
    		int x;
    		cin>>x;
    		a[i]-=x;
    		nums.pb(a[i]);//a[i]-b[i]
    		nums.pb(-a[i]);//b[i]-a[i]
    	}
    	discrete();
    	int ans=0;
    	bit.init();
    	for(int i=n;i;i--){
    		ans+=bit.Sum(a[i]-1);//贡献答案 
    		bit.add(b[i]);//添加 
    	}
    	cout<<ans;
    	return 0;
    }
    

    E - Sleeping Schedule

    洛谷题目页面传送门 & CodeForces题目页面传送门

    在Vova的世界里,一天(hmathrm h)。Vova会睡(n)次觉,每次睡刚好(1)天。第(i)次会在第(i-1)次睡觉醒来后((a_i-1)mathrm h)(a_imathrm h)后入睡,特殊地,第(0)次睡觉在第(0mathrm h)醒来。设一次睡觉是在入睡当天的第(xmathrm h)入睡,若(xin[l,r]),则称此次睡觉是好的。问最多能有多少次好的睡觉。

    (nin[1,2000],hin[3,2000],0leq lleq r<h,a_iin[1,h))

    可以说是基础DP。

    (dp_{i,j})表示考虑到第(i)次睡觉,第(i)次睡觉在当天第(jmathrm h)醒来时最多的好的睡觉次数。边界为(dp_{i,j}=egin{cases}0&j=0\- infty&j eq0end{cases})(j eq0)时状态不合法),目标为(maxlimits_{i=0}^{h-1}{dp_{n,i}}),状态转移方程为(dp_{i,j}=max!left(dp_{i-1,(j-(a_i-1))mod h},dp_{i-1,(j-a_i)mod h} ight)+[jin[l,r]])(选择在上一次睡觉醒来后((a_i-1)mathrm h)还是(a_imathrm h)后入睡)。时间复杂度(mathrm O(nh))

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int N=2000,H=2000;
    int dp[N+1][H];
    int n,h,l,r;
    int a[N+1];
    bool in(int x){return l<=x&&x<=r;}//[x in [l,r]]
    int main(){
    	cin>>n>>h>>l>>r;
    	for(int i=1;i<=n;i++)cin>>a[i];
    	for(int i=1;i<h;i++)dp[0][i]=-inf;//不合法状态 
    	for(int i=1;i<=n;i++)for(int j=0;j<h;j++)//DP 
    		dp[i][j]=max(dp[i-1][(j-a[i]+1+h)%h],dp[i-1][(j-a[i]+h)%h])+in(j);//状态转移方程 
    	cout<<*max_element(dp[n],dp[n]+h);//目标 
    	return 0;
    }
    

    F - Maximum White Subtree

    洛谷题目页面传送门 & CodeForces题目页面传送门

    给定一棵树(T=(V,E),|V|=n,|E|=n-1),节点(i)有一个权值(a_iin{0,1}),分别表示是白点、黑点。(forall iin[1,n]),找一个树上连通子图,设此图内白、黑点各有(cnt1,cnt2)个,要求最大化(cnt1-cnt2)。输出最大值。

    (ninleft[1,2 imes10^5 ight])

    先吐槽一句:为什么我总共就打过(4)场div. 3,其中(3)场的F都是树形DP???/yiw

    非常显然的树形DP+二次扫描与换根。

    首先,如果当前要求的这个点(x)是树根的话,那一切都好办了。设(dp_i)表示在以(x)为整棵树的根的情况下,在以(i)为根的子树内选连通子图,必须包含(i)(cnt1-cnt2)的最大值。目标是(dp_x),状态转移方程是(dp_i=sumlimits_{jin son_i}max(0,dp_j)+egin{cases}-1&a_i=0\1&a_i=1end{cases})(累加以每个儿子为根的子树能给(dp_i)带来的贡献的最大值,如果为负就不选)。时间复杂度(mathrm O(n))

    然而题目要求对于所有点。不妨先令(1)为根求出所有点的DP值,再一遍二次扫描与换根求出所有点的答案。时间复杂度(mathrm O(n))

    总时间复杂度(mathrm O(n)+mathrm O(n)=mathrm O(n))

    下面是AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    const int N=200000;
    int n;
    bool a[N+1];//点权
    vector<int> nei[N+1];
    int dp[N+1];
    void dfs(int x=1,int fa=0){//求出以1为根时所有点的DP值 
    	dp[x]=a[x]?1:-1;
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];
    		if(y==fa)continue;
    		dfs(y,x);
    		dp[x]+=max(0,dp[y]);
    	}
    }
    int ans[N+1];
    void dfs0(int x=1,int fa=0){//二次扫描与换根 
    	ans[x]=dp[x];//记录答案 
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i];
    		if(y==fa)continue;
    		dp[x]-=max(0,dp[y]);
    		dp[y]+=max(0,dp[x]);
    		dfs0(y,x);
    		dp[y]-=max(0,dp[x]);
    		dp[x]+=max(0,dp[y]);
    	}
    }
    int main(){
    	cin>>n;
    	for(int i=1;i<=n;i++)cin>>a[i];
    	for(int i=1;i<n;i++){
    		int x,y;
    		cin>>x>>y;
    		nei[x].pb(y);nei[y].pb(x);
    	}
    	dfs();
    	dfs0();
    	for(int i=1;i<=n;i++)cout<<ans[i]<<" ";
    	return 0;
    }
    
  • 相关阅读:
    mysql 之 union 分类: database 测试 2014-02-12 11:59 218人阅读 评论(0) 收藏
    MySql模糊查询like通配符使用详细介绍 分类: database 测试 2014-02-12 10:19 6829人阅读 评论(1) 收藏
    sed(查找替换) 与awk(提取字段) 分类: ubuntu 测试 2014-02-11 12:08 4074人阅读 评论(0) 收藏
    Linux命令行uniq 分类: ubuntu 测试 2014-02-10 17:52 341人阅读 评论(0) 收藏
    n&1判断奇偶 分类: python基础学习 测试 2014-02-10 15:41 636人阅读 评论(0) 收藏
    vue-cli安装
    input事件
    Edusohu搭建
    Ngxin代理服务基本概述
    五种IO模型---转载
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/CodeForces-1324.html
Copyright © 2011-2022 走看看