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 1≤a≤x , 1 ≤ b ≤ y 1 \leq b \leq y 1≤b≤y , ⌊ 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=k∗b+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)>k∗k
所以 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 1≤a≤x , 1 ≤ b ≤ y 1 \leq b \leq y 1≤b≤y , a = k ∗ ( b + 1 ) a=k*(b+1) a=k∗(b+1) 直接统计即可
D
给出一个 n ∗ m n*m n∗m 的矩阵 A A A
构造一个 n ∗ m n*m n∗m 的矩阵 B B B 满足一下条件
1 ≤ b i , j ≤ 1 0 6 1 \leq b_{i,j} \leq 10^6 1≤bi,j≤106 , 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)=720720≤106
于是黑白染色,将黑色格子赋为 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=bi−∑j=1i−1aj
那么从左向右推到 i i i 时,若有 ∑ j = 1 i − 1 a j = 0 \sum _{j=1} ^{i-1} a_j=0 ∑j=1i−1aj=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′=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)
转移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′[j−s[i]]=g[j−s[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′=sum′−g[−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′=sum∗2−g[−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;
}