【习题总结】差分问题

  差分,即序列中相邻两数之差,本文章讲解几个经典例题,帮助各位理解差分在算法竞赛中的应用,若遇到新的差分思维题,在此更新。
例题1:堆积木问题
题目来源:
https://www.luogu.com.cn/problem/P5019
题意:
  给定一个序列 a [ n ] a[n] a[n],每次操作可使区间 [ l , r ] [l,r] [l,r]内各个数字减一,问最少操作数。
思路:
  两个坑之间的相对"深度"差决定此区间的操作数,以第一个坑的深度作为基准值,即可得到绝对"深度",故而考虑差分。

	for(int i=2;i<=n;i++)     if(a[i]>a[i-1]) ans+=a[i]-a[i-1];
	cout<<ans+a[1];

  类似地:
https://codeforces.com/contest/1700/problem/C
此题的操作数也是差分相加减,修改前缀,加上基准值即可。

例题2:差分空间问题
题目来源:
https://codeforces.com/contest/1705/problem/D
题意:
  给定01序列s,当 s [ i − 1 ] ≠ s [ i + 1 ] s[i-1]\neq s[i+1] s[i1]=s[i+1]时, s [ i ] s[i] s[i]取反,问s变成t的最少操作数。
思路:
  直接在数组中找规律难度较大,又因为 s [ i ] s[i] s[i]取反后两边差分值会变化,所以可以在差分数组中找规律。我们用数组构造差分空间,(注意,由于是01序列,差分是以模2意义下的),不难发现我们每次取反在差分数组中即为交换0,1。所以比较差分数组中0和1的个数及距离即可。
  code:

    #include<bits/stdc++.h>
    #define fi first
    #define se second
    #define pb push_back
    #define LL long long
    #define PII pair<int, int> 
    #define PDD pair<double,double>
    using namespace std;
    typedef long long readtype;
    const int maxn = 1e5 + 5;
    const int inf = INT_MAX;
    const LL mod = 1e9 + 7;
    const double eps=1e-6;
     
    LL T;
     
    inline readtype read(){
    	readtype X=0; bool flag=1; char ch=getchar();
    	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
    	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
    	if(flag) return X;
    	return ~(X-1);
    }
     
    inline int double_cmp(double a,double b)
    {
        if(fabs(a-b)<eps) return 0;
        if(a<b) return -1;
        return 1;
    }
     
    int main(){ 
    	T = read();
    	while(T--)
    	{
    		int n = read();
    		int cnt_a=0,cnt_b=0;
    		string s1,s2;
    		cin>>s1;
    		cin>>s2;
    		s1='$' +s1;
    		s2='$' +s2;
    		if(s1[1]!=s2[1] || s1[n]!=s2[n])
    		{
    			cout<<-1<<endl;
    			continue;
    		}
    		vector<int> a(n+1,0);
    		vector<int> b(n+1,0);
    		vector<int> diff_a(n+1,0);
    		vector<int> diff_b(n+1,0);
    		for(int i=1;i<=n;i++)
    		{
    			a[i]=s1[i]-'0';
    			diff_a[i]=a[i]^a[i-1];
    			b[i]=s2[i]-'0';
    			diff_b[i]=b[i]^b[i-1];
    			if(i!=1)
    			{
    				cnt_a+=diff_a[i];
    				cnt_b+=diff_b[i];
    			}
    		}
    		if(cnt_a==cnt_b)
    		{
    			LL res=0;
    			int i=2,j=2;
    			while(i<=n && j<=n)
    			{
    				if(diff_b[j] && diff_a[i])
    				{
    					res+=abs(i-j);
    					i++;
    					j++;
    				}
    				else if(diff_b[j] && !diff_a[i]) i++;
    				else if(!diff_b[j] && diff_a[i]) j++;
    				else
    				{
    					i++;
    					j++;
    				}
    			}
    			cout<<res<<endl;
    		}
    		else
    		{
    			cout<<-1<<endl;
    		}
    	}
    	return 0;
    }

例题3:差分空间问题
题目来源:https://ac.nowcoder.com/acm/contest/38727/J
题意:
  给定一个012的环,若顺时针方向两数之差在模3的意义下等于1,则减数可加上1,问能否使得该环中各个数相等。
思路:
  模拟的时候可以发现,各个数之间的差值比较重要,而难以找到规律。故而构造差分数组,在差分数组中找规律。推导过程略,各位可以自行推导,注意把握原数组变化时对应的差分数组如何变化。最终结论就是差分数组中1的个数要不少于2,而0可以忽略掉。
  

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define LL long long
#define PII pair<int, int> 
#define PDD pair<double,double>
using namespace std;
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const LL mod = 1e9 + 7;
int T;

inline int read(){
	int X=0; bool flag=1; char ch=getchar();
	while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
	while(ch>='0'&&ch<='9') {X=(X<<1)+(X<<3)+ch-'0'; ch=getchar();}
	if(flag) return X;
	return ~(X-1);
}

 
int main(){ 
	T = read();
	while(T--)
	{
		int n = read();
		vector<int> a(n+1);
		for(int i = 1; i <= n; i++)
			a[i]=read();
        vector<int> b;
        for(int i=2;i<=n;i++)
            b.pb((a[i]-a[i-1]+3)%3);
        b.pb((a[1]-a[n]+3)%3);
        int cnta=0,cntb=0;
        for(auto num:b)
        {
            if(num==1)
                cnta++;
            else if(num==2)
                cntb++;
        }
        if(cnta>=cntb)
            puts("Yes");
        else    
            puts("No");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值