洛谷 P4552 [Poetize6] IncDec Sequence


挺好的一道思维题。

分析

因为是对区间修改,多次修改肯定会超时,很容易想到差分。

那么原题的对区间修改就可以转换为下面三个操作(均在差分数组中):

1. 任选一个数+1

2. 任选一个数-1

3. 任选两个数+1和-1

进一步考虑题目的问题,让原数组一样,那么就是

a[1]的值任意,a[2]开始后面的值均为0。

再分析现有的三个操作,最多的操作数肯定是总正数之和或者总负数之和(取大的那个)

显而易见的,因为只能选一个数进行操作。

那么我们再考虑满足当前最少操作数的时候,能出现不同序列的数量,即a[1]的取值能有多少。

如果正数比负数多,那么正数执行操作3减少到0,额外的还能执行加法(加到a[1]身上),也可以不加(即选操作2)。那么不同的数量就是正数比负数多的部分再+1(可以一个都不加)。

反之负数也是如此,但是需要注意负数执行加,那么a[1]就是减,不能小于0。

正负一样多,那肯定就只有一种序列了,因为要求操作数最少。

AC代码

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;

const int N=1e5+5;
int n,a[N],pos,neg;

void solve(){
    cin>>n;
    for(int i=1,t;i<=n;++i){
        cin>>t;
        a[i]+=t;
        a[i+1]-=t;
    }
    for(int i=2;i<=n;++i)
        if(a[i]<0)neg+=-a[i];
    else pos+=a[i];

    cout<<max(pos,neg)<<endl;//最少操作次数
    if(pos>neg){//正数多
        cout<<pos-neg+1<<endl;
    }else if(pos==neg){
        cout<<1<<endl;
    }else if(pos<neg){//负数多
        if(neg-pos<=a[1])cout<<neg-pos+1<<endl;
        else cout<<a[1]+1<<endl;
    }
}

signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t=1;
    while(t--)solve();
    return 0;
}

差分都会了,接下来复习树上差分


P3258 [JLOI2014] 松鼠的新家 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)


基础LCA敲完之后发现是需要差分来维护树上节点的内容,不然一直遍历会T飞。

分析

因为树只有一个父节点所以不需要什么vis数组来只遍历一遍,只要遍历的时候不遍历父节点就行了。

void dfs(int now,int fa){
    for(auto i:e[now])if(i!=fa)dfs(i,now);
}

考虑松鼠从a1走到an(实际上是从别的地方先到a1了),我们可以发现松鼠在作为出发点的节点上并不需要吃一个糖果,所以点差分深搜完了之后最后遍历a2~an都减去1就行了(a1不能减,因为他需要被骗过来一次)。

AC代码

#include <bits/stdc++.h>
//#define int long long
#define endl '\n'
using namespace std;

const int N=3e5+5;
int n,a[N],f[N][20],lg[N],d[N],b[N];
vector<int>e[N];

void dfs(int now,int fa){
    d[now]=d[fa]+1;
    f[now][0]=fa;
    for(int i=1;i<=lg[d[now]];++i)
        f[now][i]=f[f[now][i-1]][i-1];
    for(auto i:e[now])if(i!=fa)dfs(i,now);
}
void lca_init(){
    for(int i=1;i<N;++i)
        lg[i]=log(i)/log(2)+1;
    dfs(1,0);
}
int lca(int x,int y){
    if(d[x]<d[y])swap(x,y);
    while(d[x]>d[y])x=f[x][lg[d[x]-d[y]]-1];
    if(x==y)return x;
    for(int i=lg[d[x]];i>=0;--i)
        if(f[x][i]!=f[y][i]){
            x=f[x][i];
            y=f[y][i];
        }
    return f[x][0];
}
void get(int now,int fa){
    for(auto i:e[now])if(i!=fa)get(i,now),b[now]+=b[i];
}

void solve(){
    cin>>n;
    for(int i=1;i<=n;++i)cin>>a[i];
    for(int i=1,x,y;i<=n-1;++i){
        cin>>x>>y;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    
    lca_init();

//    for(int i=2;i<=n;++i)
//        cout<<a[i-1]<<" "<<a[i]<<" lca:"<<lca(a[i],a[i-1])<<endl;
//    return;
    for(int i=2;i<=n;++i){
        int node=lca(a[i],a[i-1]);
        b[a[i-1]]++,b[a[i]]++,b[node]--,b[f[node][0]]--;
    }

    get(1,0);

    for(int i=2;i<=n;++i)b[a[i]]--;
    for(int i=1;i<=n;++i){
        cout<<b[i]<<endl;
    }
}

signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t=1;
    while(t--)solve();
    return 0;
}

绿的都会做了,接下来把紫色省选爆了

P2680 [NOIP2015 提高组] 运输计划 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)


分析

无向连通图G中有n个节点和n-1条边,则G为树。

所以这一题没说也应该知道本题是树上问题

稍后更,更个毛,做不来,明年再说

还有一道紫题,也放弃了

P1600 [NOIP2016 提高组] 天天爱跑步 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值