• CF #627(div.3)


    A. Yet Another Tetris Problem

    题意: 给定一个数组,每次可以给其中一个元素+2 ,问可否经过若干次操作使所有元素的值一样

    分析: 找到最大的元素,判断与其它元素的差值是否是二的整倍数即可

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        
        int t,n,a[101];
        cin>>t;
        while(t--)
        {
        	cin>>n; int m=0,flag=0;
        	for(int i=0;i<n;i++) cin>>a[i],m=max(m,a[i]);
        	for(int i=0;i<n;i++)
        		if((m-a[i])%2) {flag=1;break;}
    		if(flag) cout<<"NO
    ";
    		else cout<<"YES
    ";
    	}
    }
    

    B. Yet Another Palindrome Problem

    题意: 给定一个字符串,问是否存在长度为3的回文子序列

    分析: 暴力枚举回文子序列两端,若存在 (s[l]==s[r]~and~r-l>1) 即表示有解

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 5e3+10;
    int a[N];
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	cin.tie(nullptr);
    	
    	int t,n;
    	cin>>t;
    	while(t--)
    	{
    		cin>>n; int flag=0;
    		for(int i=0;i<n;i++) cin>>a[i];
    		for(int i=0;i<n&&!flag;i++)
    		   for(int j=i+2;j<n;j++)
    		       if(a[i]==a[j])
    		       {
    		          flag=1;break;
    			   }
    		if(flag) cout<<"YES
    ";
    		else cout<<"NO
    "; 
    	}
    }
    

    C. Frog Jumps

    题意: 起点为 0 ,终点为 n+1 ,在区间 [1,n] 上每个点都有字母 'L' 或 'R' ,代表往左或者往右,当你跳到 [1,n] 上的点时,下一次起跳的方向需要对应点上的字母,每次跳跃的范围在 [1,k] ,现在要你使得从起点能跳到终点的情况下 k 的值尽量小,求这个最小值

    分析: 最小的值肯定是 [0,n+1] 上连续的 'L' 的最大值,不然是无法跳过这段距离的

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    string s;
    int main()
    {
    	ios::sync_with_stdio(false);
    	cin.tie(nullptr);
    	
    	int t;cin>>t;
    	while(t--)
    	{
    		cin>>s; int n=s.length(),last=-1,ans=0;
    		for(int i=0;i<n;i++)
    		    if(s[i]=='R')
    		    {
    		    	ans=max(ans,i-last);
    		    	last=i;
    			}
    		ans=max(ans,n-last);
    		cout<<ans<<'
    ';
    	}
    }
    

    D. Pair of Topics

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

    分析: 移下项就变成了 (a_i-b_i>-(a_j-b_j)) ,那么令 (c_i=a_i-b_i) ,即找到所有的 ((i,j)~i<j) 满足(c_i>-c_j) ,给数组 c 排个序,对于每个 (c_i) ,二分找到满足条件的位置就可以了

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 2E5+10;
    int s[N];
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	cin.tie(nullptr);
    	
        int n;
    	cin>>n;
    	for(int i=0;i<n;i++) cin>>s[i];
    	for(int i=0,x;i<n;i++) cin>>x,s[i]-=x;
    	sort(s,s+n);
    	long long ans=0;
    	for(int i=0;i<n;i++)
    	{
    		int xb=upper_bound(s,s+n,-s[i])-s; //找到第一个大于等于 -s[i] 的位置
    		xb=max(i+1,xb); //满足 i<j 的条件
    		ans+=(long long)n-xb;
    	}
    	cout<<ans;
    } 
    

    E. Sleeping Schedule

    题意: 给出长度为 n 的睡觉数组 a,(a_i) 表示第 i 次睡觉的时长,Vova 从 0 时开始睡觉(按照数组a顺序,醒了又继续睡),若他某次醒来的时刻在模 h 意义下处于区间 [l,r] 内,那么这一次的睡眠是 good 的,Vova 可以控制每次睡觉的时间减小 1 小时,问 n 次睡眠中至多有几次是 good 的

    分析: 因为每次睡觉至多减小一小时,所以可以给数组 a 做前缀和 sum,这样若前 i 次睡眠一共减小 j 次,那第 i 次睡眠后醒来的时刻就是 (sum[i]-j) ,设 dp[i][j] 表示前 i 次睡眠,减少操作为 j 次的情况下 good 最多的次数,则转移方程不难想

    //a[i]是前缀和
    for(int i=1;i<=n;i++)
            for(int j=0;j<i;j++)
            {
                // 1.前i组睡眠j+1次操作,使得第i次睡眠醒来处于good区间
            	if((a[i]-j-1)%h>=l&&(a[i]-j-1)%h<=r)
                    dp[i][j+1]=max(dp[i][j+1],dp[i-1][j]+1);
                // 2.前i组睡眠j次操作,使得第i次睡眠醒来处于good区间
            	if((a[i]-j)%h>=l&&(a[i]-j)%h<=r)
            	    dp[i][j]=max(dp[i][j],dp[i-1][j]+1);
            	// 3.前i组睡眠j次操作,第i次睡眠醒来不处于good区间
            	dp[i][j]=max(dp[i][j],max(dp[i-1][j],dp[i-1][j-1]));
            	// 4.前i组睡眠j+1次操作,第i次睡眠醒来不处于good区间
            	dp[i][j+1]=max(dp[i][j+1],dp[i-1][j]);
    		}
    //写法不唯一,符合逻辑就可以了
    

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 2000+5;
    int a[N],dp[N][N];
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	cin.tie(nullptr);
    
    	int n,h,l,r; cin>>n>>h>>l>>r;
        for(int i=1;i<=n;i++) cin>>a[i];
        for(int i=2;i<=n;i++) a[i]+=a[i-1];
        
        for(int i=1;i<=n;i++)
            for(int j=0;j<=i;j++)
            {
            	if((a[i]-j-1)%h>=l&&(a[i]-j-1)%h<=r)
                    dp[i][j+1]=max(dp[i][j+1],dp[i-1][j]+1);
            	if((a[i]-j)%h>=l&&(a[i]-j)%h<=r)
            	    dp[i][j]=max(dp[i][j],dp[i-1][j]+1);
            	dp[i][j]=max(dp[i][j],max(dp[i-1][j],dp[i-1][j-1]));
            	dp[i][j+1]=max(dp[i][j+1],dp[i-1][j]);
    		}
        
        int ans=0;
        for(int i=0;i<=n;i++) ans=max(ans,dp[n][i]);
        cout<<ans; 
    } 
    

    F. Maximum White Subtree

    题意: 给定一棵树,每棵树属性或白或黑,设一个节点 u 所在连通块的权值为 (cnt_w-cnt_b) , 即连通块内白点减去黑点的数量,现在你可以干预这个连通块(即本来 u 的连通块就是整棵树,你可以截断使得最后的连通块只要包含节点 u 就可以了),求每个节点的最大权值

    分析: 图是一棵树,由题意,对于树上的节点,对其权值产生影响的除了它本身,就是子孙节点和祖先节点,那么可以两次dfs,第一次dfs1求出 子孙节点的影响,第二次dfs2再在dfs1的基础上求出并累加祖先节点的影响;

    设 dp[u] 表示节点 u 的最大权值

    //子孙节点的影响
    void dfs1(int u,int pre)
    {
    	if(a[u]) dp[u]++; //初始化权值,节点属性为白+1,黑-1
    	else dp[u]--;
    	for(auto v:e[u])
    	{
    		if(v==pre) continue;
    		dfs1(v,u);
    		if(dp[v]>0) dp[u]+=dp[v]; 
    		//对于节点 u ,它只累加子节点中权值大于零的,这样才会使得 u 的权值尽量大
    	}
    }
    
    
    //祖先节点影响
    void dfs2(int u,int pre)
    {
    	for(auto v:e[u])
    	{
    		if(v==pre) continue;
    		if(dp[v]>=0&&dp[u]>dp[v]) dp[v]=dp[u];  
    		//如果子节点v的权值大于等于零,则证明dfs1中u的权值肯定已经包括了v,此时若u的权值大于v,则更新v
    		else if(dp[v]<0&&dp[u]>0) dp[v]+=dp[u]; 
    		//否则若v的权值小于零,则dfs1中u的权值不包括v,若u的权值大于零,则v累加u的权值(为了使v的权值尽量大)
    		dfs2(v,u);
    	}
    }
    

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 2E5+10;
    int a[N],n,dp[N];
    vector<int>e[N];
    
    void dfs1(int u,int pre)
    {
    	if(a[u]) dp[u]++;
    	else dp[u]--;
    	for(auto v:e[u])
    	{
    		if(v==pre) continue;
    		dfs1(v,u);
    		if(dp[v]>0) dp[u]+=dp[v];
    	}
    }
    
    void dfs2(int u,int pre)
    {
    	for(auto v:e[u])
    	{
    		if(v==pre) continue;
    		if(dp[v]>=0&&dp[u]>dp[v]) dp[v]=dp[u];
    		else if(dp[v]<0&&dp[u]>0) dp[v]+=dp[u]; 
    		dfs2(v,u);
    	}
    }
    
    int main()
    {
    	ios::sync_with_stdio(false);
    	cin.tie(nullptr);
    	
    	cin>>n;
    	for(int i=1;i<=n;i++) cin>>a[i];
    	for(int i=1;i<n;i++)
    	{
    		int u,v; cin>>u>>v;
    		e[u].push_back(v);
    		e[v].push_back(u);
    	}
    	dfs1(1,0);
    	dfs2(1,0);
    	int ans=0;
    	for(int i=1;i<=n;i++) cout<<dp[i]<<' ';
    } 
    
  • 相关阅读:
    函数
    字符串格式化
    集合
    习题02
    int/str/list/tuple/dict必会
    元组/字典
    列表方法
    练习题(format、expandtabs、片层)
    字符串方法
    JMM
  • 原文地址:https://www.cnblogs.com/17134h/p/12838926.html
Copyright © 2020-2023  润新知