cf701div2

cf701div2

A

给出a,b两个整数

有两种操作, a = ⌊ a b ⌋ . a=\lfloor \frac a b \rfloor . a=ba. b = b + 1 b=b+1 b=b+1

问最少几次操作将 a a a 变成 0 0 0

显然,可以调换顺序,先让 b b b 变大,然后再一次性除到 0 0 0 是比两种操作混着来要快的

那么剩下的问题就是 b b b 要变大多少

发现最坏情况下 a = 1 0 9 , b = 1 a=10^9,b=1 a=109,b=1 时,可以在大约三十多步内完成

那么b最大就是加到b+30了,如果再大就一定不会是最优解

枚举 b b b 变大多少,然后暴力模拟即可

B

两个端点单独算,中间前缀和算

特判 l = r l=r l=r 的情况

C

给出 x , y x,y x,y ,问有多少对 a , b a,b a,b 满足以下条件

1 ≤ a ≤ x 1 \leq a \leq x 1ax , 1 ≤ b ≤ y 1 \leq b \leq y 1by , ⌊ a b ⌋ = a % b \lfloor \frac a b \rfloor = a \% b ba=a%b

假设 ⌊ a b ⌋ = a % b = k \lfloor \frac a b \rfloor = a \% b = k ba=a%b=k

于是有 a = k ∗ b + k = k ∗ ( b + 1 ) a=k*b+k=k*(b+1) a=kb+k=k(b+1)

由于 k = a % b < b k=a\%b<b k=a%b<b ,因此 a = k ∗ ( b + 1 ) > k ∗ ( k + 1 ) > k ∗ k a=k*(b+1)>k*(k+1)>k*k a=k(b+1)>k(k+1)>kk

所以 k < a 1 2 < = x 0.5 k<a^{\frac 1 2}<=x^{0.5} k<a21<=x0.5 ,考虑暴力枚举 k k k 然后计算有多少对 a , b a,b a,b 满足以下条件

1 ≤ a ≤ x 1 \leq a \leq x 1ax , 1 ≤ b ≤ y 1 \leq b \leq y 1by , a = k ∗ ( b + 1 ) a=k*(b+1) a=k(b+1) 直接统计即可

D

给出一个 n ∗ m n*m nm 的矩阵 A A A

构造一个 n ∗ m n*m nm 的矩阵 B B B 满足一下条件

1 ≤ b i , j ≤ 1 0 6 1 \leq b_{i,j} \leq 10^6 1bi,j106 , b i , j b_{i,j} bi,j a i , j a_{i,j} ai,j 的倍数 , 相邻的格子的差的绝对值是四次方数且不为 0 0 0

发现 a i , j a_{i,j} ai,j 的值域有点奇妙 ,只有一到十六

然后发现 l c m ( 1 , 2 , . . . , 16 ) = 720720 ≤ 1 0 6 lcm(1,2,...,16)=720720 \leq 10^6 lcm(1,2,...,16)=720720106

于是黑白染色,将黑色格子赋为 720720 720720 720720 ,白色格子赋为 720720 + a i , j 4 720720+a_{i,j}^4 720720+ai,j4 即可

E

给出一棵根为1,带有点权的树,每个叶子节点有相同的深度 d d d ,一开始有两个硬币在根节点处

要进行 d d d 次操作,每次先把红硬币移到它的其中一个儿子处,再把蓝硬币移到任意一个深度与红硬币相同的点处,然后可以选择交换或不交换红蓝硬币,每次操作结束后将得到两个硬币所在节点的权值差的绝对值的分数

求最大总分

显然,蓝硬币的具体位置并不重要

d p ( x ) dp(x) dp(x) 为红硬币在x时的最大总分

情况1:若不交换红蓝硬币,那么蓝硬币肯定可以贪心地取贡献最大的点,即最大值或最小值

情况2:若交换,则需要枚举红硬币的位置,蓝硬币也是尽可能贪心地取

这样就有了一个复杂度 n 2 n^2 n2 的做法

分析复杂度,情况1只需要枚举与x相连的点,是 O ( n ) O(n) O(n) 的, n 2 n^2 n2 主要来自情况2的枚举

假设情况2选择的红蓝点位置为y和z,考虑所有合法的x对y的贡献

d p [ y ] = m a x ( d p [ y ] , d p [ x ] + ∣ a [ y ] − a [ z ] ∣ ) = m a x ( d p [ y ] , m a x ( d p [ x ] + a [ y ] − a [ z ] , d p [ x ] + a [ z ] − a [ y ] ) ) dp[y]=max(dp[y],dp[x]+|a[y]-a[z]|)=max(dp[y],max(dp[x]+a[y]-a[z],dp[x]+a[z]-a[y])) dp[y]=max(dp[y],dp[x]+a[y]a[z])=max(dp[y],max(dp[x]+a[y]a[z],dp[x]+a[z]a[y]))

z的选择显然只会是与x相连的点中的最大最小值,那么只要维护所有 dp[x]+a[z] 和dp[x]-a[z] 的最大值即可

注意到x可能是y上面一层的任意点,所以要从上往下一层层dp,不能dfs了,分层或者bfs都是可以的

这样就可以 O ( n ) O(n) O(n) 做了(感觉从下往上推可能会更简单很多,但懒得写了)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1500005;
vector <int> adj[N],vc[N];
ll dp[N],g1[N],g2[N],mn[N],mx[N],l[N],r[N],fa[N],a[N],n;
void dfs(int x,int pre,int dep)
{
    vc[dep].push_back(x);
    mn[dep]=min(mn[dep],a[x]);
    mx[dep]=max(mx[dep],a[x]);
    for (auto y:adj[x])
    if (y!=pre)
    {
        fa[y]=x;
        l[x]=min(l[x],a[y]);
        r[x]=max(r[x],a[y]);
        dfs(y,x,dep+1);
    }
}
void solve()
{
    scanf("%d",&n);
    for (int i=0;i<=n;++i)
        fa[i]=dp[i]=0,mn[i]=l[i]=1LL<<60,mx[i]=r[i]=g1[i]=g2[i]=-(1LL<<60),adj[i].clear(),vc[i].clear();
    for (int i=2,x;i<=n;++i) scanf("%d",&x),adj[i].push_back(x),adj[x].push_back(i);
    for (int i=2;i<=n;++i) scanf("%d",&a[i]);
    dfs(1,0,0);
    dp[1]=0;g1[0]=0-l[1];g2[0]=0+r[1];
    for (int dep=1;dep<=n;++dep)
    {
        for (auto y:vc[dep])
        {
            dp[y]=max(dp[y],max(dp[fa[y]]+a[y]-mn[dep],dp[fa[y]]+mx[dep]-a[y]));///不交换
            dp[y]=max(dp[y],max(g1[dep-1]+a[y],g2[dep-1]-a[y]));///交换
            g1[dep]=max(g1[dep],dp[y]-l[y]);
            g2[dep]=max(g2[dep],dp[y]+r[y]);
            ///维护max(dp[x]-a[z])和max(dp[x]+a[z])
        }
    }
    //for (int i=1;i<=n;++i) cout<<dp[i]<<' ';cout<<endl;
    ll ans=0;
    for (int i=1;i<=n;++i) ans=max(ans,dp[i]);
    printf("%lld\n",ans);
}
int main()
{
    int T;cin>>T;while (T--) solve();
    return 0;
}

F

给出数列 b b b 问有多少个满足以下条件的数列 a a a

对于 ∀ i ∈ [ 1 , n ] \forall i \in [1,n] i[1,n] ,有 b i = a i b_i=a_i bi=ai 或者 b i = ∑ j = 1 i a j b_i=\sum _{j=1} ^{i} a_j bi=j=1iaj

等价于 ∀ i ∈ [ 1 , n ] \forall i \in [1,n] i[1,n] ,有 a i = b i a_i=b_i ai=bi 或者 a i = b i − ∑ j = 1 i − 1 a j a_i=b_i-\sum _{j=1} ^{i-1} a_j ai=bij=1i1aj

那么从左向右推到 i i i 时,若有 ∑ j = 1 i − 1 a j = 0 \sum _{j=1} ^{i-1} a_j=0 j=1i1aj=0 , a i a_i ai 就只有一种取值,若不为 0 0 0 ,就有两种

考虑dp,令 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示满足 ∑ k = 1 i a k = j \sum _{k=1} ^{i} a_k=j k=1iak=j 的方案数

转移1: d p [ i ] [ j ] − > d p [ i + 1 ] [ j + b [ i + 1 ] ] dp[i][j]->dp[i+1][j+b[i+1]] dp[i][j]>dp[i+1][j+b[i+1]]

转移2: d p [ i ] [ j ] − > d p [ i + 1 ] [ b [ i + 1 ] ] dp[i][j]-> dp[i+1][b[i+1]] dp[i][j]>dp[i+1][b[i+1]] (仅在 j ≠ 0 j\neq0 j=0 时转移)

每次都判能不能转移太麻烦,考虑先都转移上,再把贡献减掉

转移1: d p [ i ] [ j ] − > d p [ i + 1 ] [ j + b [ i + 1 ] ] dp[i][j]->dp[i+1][j+b[i+1]] dp[i][j]>dp[i+1][j+b[i+1]]

转移2: d p [ i ] [ j ] − > d p [ i + 1 ] [ b [ i + 1 ] ] dp[i][j]-> dp[i+1][b[i+1]] dp[i][j]>dp[i+1][b[i+1]]

转移3: − d p [ i ] [ 0 ] − > d p [ i + 1 ] [ b [ i + 1 ] ] -dp[i][0]->dp[i+1][b[i+1]] dp[i][0]>dp[i+1][b[i+1]]

发现转移1是全体右移,转移2是把全部的dp值的和赋给 d p [ i + 1 ] [ b [ i + 1 ] ] dp[i+1][b[i+1]] dp[i+1][b[i+1]] ,转移3也是单点修改

维护一个偏移量 p p p ,再维护一个全体dp值的和 s u m sum sum 即可

g ( j + p ) = d p ( i ) ( j ) , g ′ ( j + p ′ ) = d p ( i + 1 ) ( j ) g(j+p)=dp(i)(j),g'(j+p')=dp(i+1)(j) g(j+p)=dp(i)(j),g(j+p)=dp(i+1)(j)

转移1: p ′ = p − b [ i + 1 ] , g ′ [ j + p ] = g ′ [ j + b [ i + 1 ] + p ′ ] = d p [ i + 1 ] [ j + b [ i + 1 ] ] = d p [ i ] [ j ] = g ( j + p ) p'=p-b[i+1],g'[j+p]=g'[j+b[i+1]+p']=dp[i+1][j+b[i+1]]=dp[i][j]=g(j+p) p=pb[i+1],g[j+p]=g[j+b[i+1]+p]=dp[i+1][j+b[i+1]]=dp[i][j]=g(j+p)

转移2: s u m − > g ′ [ b [ i + 1 ] + p ′ ] , s u m − > s u m ′ sum->g'[b[i+1]+p'],sum->sum' sum>g[b[i+1]+p],sum>sum

转移3: − g [ 0 + p ] − > g ′ [ b [ i + 1 ] + p ′ ] , − g [ 0 + p ] − > s u m ′ -g[0+p]->g'[b[i+1]+p'],-g[0+p]->sum' g[0+p]>g[b[i+1]+p],g[0+p]>sum

将b的前缀和记作s,发现 p ′ = − s [ i + 1 ] , p = − s [ i ] p'=-s[i+1],p=-s[i] p=s[i+1],p=s[i] ,将转移化简得

转移1: g ′ [ j − s [ i ] ] = g [ j − s [ i ] ] , s u m ′ = s u m g'[j-s[i]]=g[j-s[i]],sum'=sum g[js[i]]=g[js[i]],sum=sum

转移2: g ′ [ − s [ i ] ] = g ′ [ − s [ i ] ] + s u m , s u m ′ = s u m ′ + s u m g'[-s[i]]=g'[-s[i]]+sum,sum'=sum'+sum g[s[i]]=g[s[i]]+sum,sum=sum+sum

转移3: g ′ [ − s [ i ] ] = g ′ [ − s [ i ] ] − g [ − s [ i ] ] , s u m ′ = s u m ′ − g [ − s [ i ] ] g'[-s[i]]=g'[-s[i]]-g[-s[i]],sum'=sum'-g[-s[i]] g[s[i]]=g[s[i]]g[s[i]],sum=sumg[s[i]]

于是有 g ′ [ − s [ i ] ] = g [ − s [ i ] ] − g [ − s [ i ] ] + s u m = s u m , s u m ′ = s u m ∗ 2 − g [ − s [ i ] ] g'[-s[i]]=g[-s[i]]-g[-s[i]]+sum=sum,sum'=sum*2-g[-s[i]] g[s[i]]=g[s[i]]g[s[i]]+sum=sum,sum=sum2g[s[i]]

最后得到的sum即为所求

第二维可以用map存

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MOD = 1e9+7;
const int N = 1500005;
ll a[N],s[N],n,sum;
map <ll,ll> mp;
void solve()
{
    cin>>n;
    for (int i=1;i<=n;++i) cin>>a[i],s[i]=s[i-1]+a[i];
    mp.clear();
    mp[0]=sum=1;
    for (int i=1;i<n;++i)
    {
        ll t=mp[-s[i]];
        mp[-s[i]]=sum;
        sum=(sum*2-t)%MOD;
    }
    ll ans=(sum%MOD+MOD)%MOD;
    cout<<ans<<endl;
    return;
}
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int T=1;cin>>T;
    for (int cas=1;cas<=T;++cas) solve();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值