2020-08-16 树形DP+等差数列前缀和

http://icpc.upc.edu.cn/problem.php?cid=2535&pid=1

题目描述

eobiyye给了你一个长度为n的序列ai,序列中每个元素的初始值为0。
接下来她会对这个序列进行m次操作,每次操作有4个参数l,r,s,e,表示将区间[l,r]加上一个首项为s,末项为e的等差数列。
若一次操作中l=1,r=5,s=2,e=10,则对序列中第1~5个数分别加上2,4,6,8,10。
现在Geobiyye要求你求出m次操作后序列中的每个数的值。

输入

第一行2个整数n,m,表示序列长度和操作数。
接下来m行,每行4个整数l,r,s,e,含义见题目描述。
数据保证等差数列中的每一项都是整数。

输出

由于输出数据过大,Geobiyye只想要知道最终序列每一项的异或和,即。(其中表示二进制下的异或操作,在c++中为^)

样例输入
5 2
1 5 2 10
2 4 1 1
样例输出
3
样例解释:

第一次操作加的数列:2 4 6 8 10
第二次操作加的数列:0 1 1 1 0
所有操作结束后序列每个元素值为:2 5 7 9 10。
输出异或和,就是3。

【数据范围】
对于30%的数据:n,m≤1000 。
对于50%的数据:n,m≤100000。
对于另外20%的数据:s=e。
对于100%的数据:n,m≤500000,1≤l<r≤n。
数据保证输入数据以及在任何时候序列中的数在[0,9×1018]范围内。

等差数列的前缀和

#include<bits/stdc++.h>
 
using namespace std;
const long long int N=1e7+500;
long long int out[N]={0};
int main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    register long long int n,m,l,r,ks,e1,dc,tmp=0;
    register long long int max1=0,ans=0;
    cin>>n>>m;
    register long long int i;
    for(i=1; i<=m; i++)
    {
        cin>>l>>r>>ks>>e1;
        dc=(e1-ks)/(r-l);
        out[l]=out[l]+ks;
        out[l+1]=out[l+1]+dc-ks;
        out[r+1]=out[r+1]-dc-e1;
        out[r+2]=out[r+2]+e1;
    }
    for(i=1; i<=n; i++)
    {
        out[i]=out[i-1]+out[i];
        tmp+=out[i];
        ans=ans^tmp;
    }
    cout<<ans<<endl;
    return 0;
}

http://icpc.upc.edu.cn/problem.php?cid=2535&pid=5

题目描述

请开 long long。

老爹说要用魔法打败魔法 ———— 3edc2wsx1qaz

一天,巨神 3edc2wsx1qaz 正在森林里漫步,突然,一只凶恶的双头巨人拦住了他。

双头巨人的两个头的攻击力分别为a,b。它每次可以对 3edc2wsx1qaz 造成 |a-b| 的魔法伤害。

3edc2wsx1qaz 觉得这只怪物十分难缠。

“要用魔法打败魔法!” 就在这时,老爹的话回响在 3edc2wsx1qaz 的耳边,3edc2wsx1qaz 顿时感到一股力量涌上心头,于是,他决定从它的特殊能力下手,从而打败它。

998244353年前,巨神 Rainy7 曾经教过他名为 爪巴 的 % 法,他决定用这种 % 法击败巨人。

3edc2wsx1qaz 可以使用无限次法术,第i次使用可以任选一个头,使得它的攻击力增加或减少i。(在此过程中攻击力可以是负数)

3edc2wsx1qaz 不想受到任何伤害,所以,他想知道,他至少要施几次法,才能使得a-b=0。

3edc2wsx1qaz 只用了114514-1919810 s就解决了这个问题,于是让您解决。

输入

第一行一个数T,表示数据组数。
对于每组数据,一行两个数a,b,代表双头巨人两个头的攻击力。

输出

对于每组数据,一行一个整数,表示最小次数。

样例输入

【样例1】
3
1 3
11 11
30 20
【样例2】
1
114514 1919810

样例输出

【样例1】
3
0
4
【样例2】
1900

提示

样例1解释:
对于数据1,一种操作流程如下:1+1=2,3+2=5,2+3=5
对于数据2,由于相等,所以答案为0。
对于数据3,一种操作流程如下:20+1+2+3+4=30。
在这里插入图片描述

解析

假设双方的血量分别为a和b(a<b),由于减法相当于对另一个进行加法运算,所以我们只用加法。首先,我们对a进行加法直到大于等于b,如果等于b,结果就是进行加法的次数,如果大于b,则分两种情况,如果超出的数目为偶数,则我们可以把多出来的部分除以2从a减去加到b上,这样两边就相同了,如果为奇数,继续加,直至差值为偶数为止。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
typedef long long ll;
 
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(a>b)
            swap(a,b);
        int i;
        for(i=1; a<b||(a>b&&(a-b)%2==1); i++)
            a+=i;
        cout<<i-1<<endl;
    }
    return 0;
}

http://icpc.upc.edu.cn/problem.php?cid=2535&pid=6

题目描述

请开 long long。

若众人皆丧气,则我谒见谏言。———— Rainy7

在浩瀚无垠的宇宙中,分布着许许多多的文明,它们之间经常起冲突。

一次,一个文明派出大批星舰进攻地球,地球人被打得落荒而逃。

此时在地球上,巨神 Rainy7 天天吊打集训队,已经感到厌烦了,于是,Rainy7 便去吊打外星人了。

Rainy7 使用魔法,将这n艘星舰封锁住了。然而,这些星舰都有护盾保护,Rainy7 要想方设法破除它们。

星舰的封锁关系可以看成一棵树。一开始, Rainy7 可以任选一艘星舰,然后直接解除它的护盾。紧接着,Rainy7 可以进行n次操作,每次选择一艘未被护盾保护的星舰,然后直接摧毁它,并且获得它被摧毁前所在的全都是未被摧毁的星舰的连通块的大小的魔法值。一艘星舰被摧毁后,与它直接相连且未被摧毁的星舰的护盾会全部被解除。

Rainy7 想知道,自己最多能获得多少魔法值。她只用了114514-1919810s就解决了问题,于是把问题抛给了您。
在这里插入图片描述

如图所示,加粗的星舰表示已经被摧毁。若选择2号星舰,将会获得4点魔法值,并且解除3,5,6的护盾。若选择9号星舰,将会获得3点魔法值,并且解除7,8的护盾。

输入

第一行一个数n,表示星舰数量。
接下来n-1行,每行两个数u,v,表示星舰的相连关系。

输出

一行一个数,表示最多能获得的魔法值。

样例输入

【样例1】
9
1 2
2 3
2 5
2 6
1 4
4 9
9 7
9 8
【样例2】
5
1 2
1 3
2 4
2 5

样例输出

【样例1】
36
【样例2】
14

提示

在这里插入图片描述

解析

树形DP,先求出1点的魔法值,后经过状态转移求出其他点的魔法值,求最大值即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<ll>w[300500];
int vis[300500]={0};
int vis1[300500]={0};
ll size1[300500]={0};
ll ans[300500]={0};
ll n;
ll dfs(ll k)
{
    if(size1[k]!=0)
        return size1[k];
    size1[k]=1;
    vis[k]=1;
    for(ll i=0;i<w[k].size();i++)
    {
        if(!vis[w[k][i]])
        {
            size1[k]+=dfs(w[k][i]);
        }
    }
    return size1[k];
}
ll dfs1(ll k)
{
    vis1[k]=1;
    for(ll i=0;i<w[k].size();i++)
    {
        if(!vis1[w[k][i]])
        {
            ll x=w[k][i];
            ans[x]=ans[k]+(n-size1[x]-1)-(size1[x]-1);
            dfs1(x);
        }
    }
}
int main()
{
    scanf("%lld",&n);
    ll x,y;
    for(ll i=1;i<=n-1;i++)
    {
        scanf("%lld%lld",&x,&y);
        w[x].push_back(y);
        w[y].push_back(x);
    }
    dfs(1);
    for(ll i=1;i<=n;i++)
    {
        ans[1]+=size1[i];
    }
    dfs1(1);
    ll max1=0;
    for(ll i=1;i<=n;i++)
    {
        max1=max(max1,ans[i]);
    }
    cout<<max1<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值