文章目录
前言
2021南京,号称今年最卷的icpc区域赛。
上周末训练赛和队友打了一下,争取多补几道题。
比赛链接:https://codeforces.com/gym/103470
题目一览
签到题:A,C,M,H
铜牌题:D
银牌题:J
金牌题:I ,G
我不能做的题:B,F,K,L
本场差不多是4.5题铜,5.5题银,7题金(4题铜5题银的概率不大,约等于5铜,6银,7金)
A.Oops, It’s Yesterday Twice More(签到)
题意
不知道,队友写的
思路
不知道,队友写的
M. Windblume Festival(签到)
题意
给一个长度为n整数序列 a [ ] a[] a[] , 每次操作可以选择两个下标 i , j i,j i,j ( i < j i < j i<j,环形) , 移除 a j a_j aj , 并且令 a i = a i − a j a_i = a_i - a_j ai=ai−aj 。
最后会剩下一个元素,要求最大化这个元素。 ( 1 < = n < = 1 0 6 1<=n<=10^6 1<=n<=106)
input
5
4
1 -3 2 -4
11
91 66 73 71 32 83 72 79 84 33 93
12
91 66 73 71 32 83 72 79 84 33 33 93
13
91 66 73 71 32 83 72 79 84 33 33 33 93
1
0
output
10
713
746
779
0
Note
For the first sample test case follow the strategy shown below, where the underlined integers are the integers held by the players selected in each turn.
{1–,−3,2,−4}(select x=4) →→ {−3,2,−5} (select x=2x) →→ {−3,7} (select x=2) →→ {10}}.
思路
这是一个环形的结构,所以我们可以“自行地”决定一个数的贡献是正的还是负的。并且一定会有一个正的贡献,并且n>1时必定有一个负的贡献。
所以,答案就是 a n s = m a x − m i n + s u m ( r e s ) ans = max - min + sum(res) ans=max−min+sum(res)。
C. Klee in Solitary Confinement(思维+前缀和)
题意
给一个长度为n整数序列 a [ ] a[] a[] , 给一个正整数 k k k 。
可以选择一段区间 [ l , r ] [l,r] [l,r] , 将其中所以元素加 k k k 。
要求最大化出现次数最多的数的数量。
( 1 ≤ n ≤ 1 0 6 , − 1 0 6 ≤ k ≤ 1 0 6 , − 1 0 6 ≤ a i ≤ 1 0 6 ) (1≤n≤10^6, −10^6≤k≤10^6 ,−10^6≤a_i≤10^6) (1≤n≤106,−106≤k≤106,−106≤ai≤106)
Examples
input
5 2
2 2 4 4 4
output
5
input
7 1
3 2 3 2 2 2 3
output
6
input
7 1
2 3 2 3 2 3 3
output
5
input
9 -100
-1 -2 1 2 -1 -2 1 -2 1
output
3
思路
不难发现,对于每个数字 i i i,我们只需要考虑 i i i 和 i + k i+k i+k 的相对位置关系,可以忽略其他所有的数字。
所以我们可以遍历一遍数组,用 v e c t o r vector vector将每个 i i i 和 i + k i+k i+k分在一个组里并保留相对位置。
对每个组,令 a i = 1 a_i = 1 ai=1 , a i + k = − 1 a_i+_k = -1 ai+k=−1,目标就是最大化区间找出区间和最大的区间 [ l , r ] [l,r] [l,r]。
意思就是对于区间 [ l , r ] [l,r] [l,r] , 我们能令最多的 a i a_i ai变成 a i + k a_i+_k ai+k 的同时,令最少的 a i + k a_i+_k ai+k被改变。
实现上,我们维护一个前缀和 sum[i] 和最小前缀和 sum_min[i] 即可。
代码
#include<cstdio>
#include<iostream>
#include<vector>
using namespace std;
#define int long long
const int BASE=1e6;
const int MAXN=4e6+20;
int n,k,a,sum[MAXN/4],minn,maxn,summin[MAXN/4],cnt[MAXN],flag,ans,tot;
vector<int> s[MAXN];
void solve(int num) {
tot=s[num].size();
if(tot==0) return;
ans=max(ans,cnt[num]+s[num][0]);
sum[1]=s[num][0];
summin[1]=min(sum[0],sum[1]);
for(int i=2;i<=tot;++i) {
sum[i]=sum[i-1]+s[num][i-1];
summin[i]=min(summin[i-1],sum[i]);
ans=max(ans,sum[i]-summin[i-1]+cnt[num]);
//cout<<i<<" "<<sum[i]<<" "<<summin[i-1]<<'\n';
}
for(int i=1;i<=tot;++i) sum[i]=summin[i]=0;
}
signed main()
{
scanf("%lld%lld",&n,&k);
flag=1;
if(k<0) flag=-1,k=-k;
minn=5e6,maxn=-1;
for(int i=1;i<=n;++i) {
scanf("%lld",&a);
a=a*flag+BASE;
minn=min(minn,a);
maxn=max(maxn,a);
++cnt[a];
ans=max(ans,cnt[a]);
s[a].push_back(-1);
s[a+k].push_back(1);
}
if(k!=0)
{
for(int i=minn+k;i<=maxn;++i)
solve(i);
}
cout<<ans;
return 0;
}
H. Crystalfly(树形dp)
题意
给一棵 n n n 个点带点权的树 ( 1 ≤ n ≤ 1 0 5 1≤n≤10^5 1≤n≤105 , 1 ≤ a i ≤ 1 0 9 1≤a_i≤10^9 1≤ai≤109)
从 1 号点出发,每个点的点权可以吃一次,要求最大化吃到的点权和。
每个点还有另一个值 t i t_i ti , 当你走到这个点的相邻节点时,这个点会被" d i s t u r b disturb disturb " , t i t_i ti 开始倒计时,如果在倒计时结束前还没能吃到这个点,那这个点的点权会消失(注意,可以经过,只是吃不到点权)。 其中 1 < = t i < = 3 1<=t_i<=3 1<=ti<=3。
输入多组样例,每组样例第一行 n n n,第二行点权,第三行 t i t_i ti,然后是树边
input
2
5
1 10 100 1000 10000
1 2 1 1 1
1 2
1 3
2 4
2 5
5
1 10 100 1000 10000
1 3 1 1 1
1 2
1 3
2 4
2 5
output
10101
10111
思路
1 < = t i < = 3 1<=t_i<=3 1<=ti<=3是一个很关键的信息。
t i = 1 或 2 t_i = 1或2 ti=1或2不影响,对于每个父节点我们只能选一个孩子,该吃不到的点还是吃不到。
t i = 3 t_i = 3 ti=3 时 , 我们可以考虑先走一个孩子节点,再回到父节点,再走另外一个孩子节点。
如图,当 2号点有 t i = 3 t_i = 3 ti=3 时,我们可以先绕到3号点,再走2号。
这样做,我们可以同时吃到1号的两个孩子2号和3号,
但要注意,3号的孩子5号和6号(橙色框)必定吃不到点权。
所以,当我们考虑“绕”这种策略时,要考虑到孙子节点;而不考虑“绕”这种策略时,孙子节点是不需要考虑的,
这影响到了我们dp方程的设置。
考虑 d p [ i ] dp[i] dp[i] 代表 i 的子树的最优解,不包括 i i i 。
“不包括 i i i ”,那么不考虑“绕”这种策略,我们就可以直接将子节点的dp值直接合并到父节点。
所以,不考虑“绕”的策略为 : d p [ i ] = ∑ v = 1 m d p [ v ] + m a x ( a [ v ] ) dp[i] = \sum_{v=1}^m dp[v] + max(a[v]) dp[i]=∑v=1mdp[v]+max(a[v]) 。
考虑“绕”这种策略时,假设要绕的点是 w w w , w w w的孩子是 w s w_s ws 。 那么就是 减掉 d p [ w ] dp[w] dp[w], 加上 ∑ w s = 1 m d p [ w s ] \sum_{w_s=1}^m dp[w_s] ∑ws=1mdp[ws] ,再加上 a [ w ] a[w] a[w] 。
即 d p [ i ] = m a x ( d p [ i ] , ∑ v = 1 m d p [ v ] + a [ v ] − d p [ w ] + ∑ w s = 1 m d p [ w s ] + a [ w ] dp[i] = max(dp[i] , \sum_{v=1}^m dp[v] + a[v] - dp[w] +\sum_{w_s=1}^m dp[w_s]+a[w] dp[i]=max(dp[i],∑v=1mdp[v]+a[v]−dp[w]+∑ws=1mdp[ws]+a[w]) .
(其中, v v v必须保证 t v = 3 t_v = 3 tv=3)
我们发现这样处理的话每次不仅得遍历儿子还得遍历孙子,因此考虑将孙子的dp值存起来
令 s u m [ u ] = ∑ v = 1 m d p [ v ] sum[u] = \sum_{v=1}^m dp[v] sum[u]=∑v=1mdp[v],
那么两种策略分别为:
- d p [ i ] = s u m [ i ] + m a x ( a [ v ] ) dp[i] = sum[i]+max(a[v]) dp[i]=sum[i]+max(a[v])
- d p [ i ] = m a x ( d p [ i ] , s u m [ i ] + a [ v ] + m a x ( − d p [ w ] + s u m [ w ] + a [ w ] ) ) dp[i] = max(dp[i],sum[i]+a[v] + max(-dp[w]+sum[w]+a[w])) dp[i]=max(dp[i],sum[i]+a[v]+max(−dp[w]+sum[w]+a[w]))
策略2中的走法是: i i i 先到 w w w , w w w 回到 i i i, i i i 再到 v v v 。
由转移方程可以看出 , − d p [ w ] + s u m [ w ] + a [ w ] -dp[w]+sum[w]+a[w] −dp[w]+sum[w]+a[w] 应该取最大的 , v v v 必须满足 t v = 3 t_v = 3 tv=3。
yysy ,这题作为最后一道签到题来讲思路上还是挺绕的,但场上确实过了那么多人…
如果在不太卷的赛站这大概会是铜牌题?
代码
思路挺绕,但写起来不麻烦
#include<bits/stdc++.h>
using namespace std;
#define rep(i,x,y) for(int i=x;i<=y;i++)
#define int long long
const int N = 1e6+100;
vector<int> edge[N];
int a[N],ti[N];
int n;
int dp[N],sum[N];
void dfs(int u,int f){
int max_av = 0;
for(auto v:edge[u]){
if(v==f) continue;
dfs(v,u);
sum[u] += dp[v];
max_av = max(max_av,a[v]);
}
dp[u] = sum[u] + max_av;
if(edge[u].size()<=1) return;
// 预处理出-dp[w]+sum[w]+a[w] 最大的两项 , 因为w和v可能重复
// mx[0] 最大的 -dp[w]+sum[w]+a[w]
// mx[1] 第二大的 -dp[w]+sum[w]+a[w]
pair<int,int> mx[2];
for(auto w:edge[u]){
if(w==f) continue;
int now = -dp[w]+sum[w]+a[w];
if(now>mx[0].first){
swap(mx[0],mx[1]);
mx[0] = make_pair(now,w);
}
else if(now>mx[1].first){
mx[1] = make_pair(now,w);
}
}
for(auto v:edge[u]){
if(v==f) continue;
if(ti[v]==3){
if(v!=mx[0].second)
dp[u]= max(dp[u],sum[u]+a[v]+mx[0].first);
else
dp[u] = max(dp[u],sum[u]+a[v]+mx[1].first);
}
}
}
signed main(){
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--){
cin>>n;
rep(i,1,n) cin>>a[i],edge[i].clear();
rep(i,1,n) cin>>ti[i];
rep(i,1,n-1){
int x,y;
cin>>x>>y;
edge[x].push_back(y);
edge[y].push_back(x);
}
rep(i,1,n){
dp[i] = sum[i] = 0;
}
dfs(1,0);
cout<<dp[1]+a[1]<<endl;
}
}
D.Paimon Sorting(思维+数据结构)
题意
派蒙(没错就是派蒙)发明了一种排序方法,如下图:
给你一个长度为 n n n 的正整数序列 a [ ] a[] a[] , 问 :对于 a [ ] a[] a[] 的每个前缀,执行上述的排序方法,各需要排序几次?
( 1 < = n < = 1 0 5 , 1 < = a i < = n 1<=n<=10^5,1<=a_i<=n 1<=n<=105,1<=ai<=n , 多组样例保证 n n n 不大于 1 0 6 10^6 106)
思路
首先派蒙的排序方法很简单,可以抄下来写对拍找规律。
不难发现,第 i i i 次迭代的时候,我们会将序列里最大的元素移动到第 i i i 位(废话,但很关键)
那么,既然题目要求依次输出每个前缀的答案
我们不如就将这些元素一个一个插入到序列中,看看 a n s i ans_i ansi 与 a n s i + 1 ans_{i+1} ansi+1有没有关系。
设 maxx 为前 i − 1 i-1 i−1 个元素里的最大值,当我们求第 i i i 个前缀时:
(1) 若 a i < m a x x a_i < maxx ai<maxx :
“第 i i i 次迭代的时候,我们会将序列里最大的元素移动到第 i i i 位” , 那么,前i-1次迭代时, a i a_i ai 一定不会被移动(对着派蒙的代码看一下就知道了)。第 i i i 次迭代时,显然,交换次数 = 有多少个比 a i a_i ai大的数(去重)。
因此 , a n s i = a n s i − 1 ans_i = ans_{i-1} ansi=ansi−1 + 比 a i a_i ai大的数的个数(去重)。
这个可以拿权值线段树维护一下
(2)若 a i = m a x x a_i = maxx ai=maxx:
因为相等不交换,因此有 a n s i = a n s i − 1 ans_i = ans_{i-1} ansi=ansi−1
(3)若 a i > m a x x a_i > maxx ai>maxx:
a n s i ans_i ansi = a n s i − 1 ans_{i-1} ansi−1 + 最大的数第二次出现后一共有几个数(暂时不知道怎么证明, 过两天抓个人讨论下)。
这个有点难弄,但这道题有个很好的地方在于可以打表帮助推导。
cjb :
所以还是那个很关键的地方-------“第 i i i 次迭代的时候,我们会将序列里最大的元素移动到第 i i i 位” ,然后打表就完事儿了.jpg。
这个可以维护两个 c n t cnt cnt, 也可以 l o w e r b o u n d ( ) lowerbound() lowerbound()一下。
代码
(记得开long long, 记得开long long, 记得开long long, 记得开long long, 记得开long long)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e6+100;
int tr[N];
void insert(int now,int l,int r,int loc,int val){
if(l==r){
tr[now] += val;
tr[now] = min(1ll,tr[now]);
tr[now] = max(tr[now],0ll);
return;
}
int mid = (l+r)>>1;
if(loc<=mid)
insert(now<<1,l,mid,loc,val);
else
insert(now<<1|1,mid+1,r,loc,val);
tr[now] = tr[now<<1]+tr[now<<1|1];
}
int query(int now,int l,int r,int ql,int qr){
if(l>=ql&&r<=qr){
return tr[now];
}
int sum = 0;
int mid = (l+r)>>1;
if(ql<=mid)
sum += query(now<<1,l,mid,ql,qr);
if(qr>=mid+1)
sum += query(now<<1|1,mid+1,r,ql,qr);
return sum;
}
int a[N];
vector<int> ans;
signed main(){
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--){
int n;
cin>>n;
int maxx = -1;
int last = 0;
int tmp = 0,cnt = 0;
for(int i=1;i<=n;i++){
cin>>a[i];
if(i==1){
ans.push_back(last);
maxx = a[i],cnt = 1;
}
else{
if(a[i]==maxx){
ans.push_back(last);
cnt++;
if(cnt>=2) tmp++;
}
else if(a[i]<maxx){
last = last+ query(1,1,n,a[i]+1,n);
ans.push_back(last);
if(cnt>=2) tmp++;
}
else{
last = last+2+tmp;
ans.push_back(last);
maxx = a[i];
tmp = 0;
cnt = 1;
}
}
insert(1,1,n,a[i],1);
}
for(int i=1;i<=n;i++){
insert(1,1,n,a[i],-1);
cout<<ans[i-1]<<" \n"[i==n];
}
ans.clear();
}
}
J. Xingqiu’s Joke(数学,数论)
题意
多次询问 ( 1 < = t < = 1 0 4 1<=t<=10^4 1<=t<=104)
给你两个正整数 a a a, b b b ( 1 < = a , b < = 1 0 9 1<=a,b<=10^9 1<=a,b<=109) , 每次操作可以选择以下三个操作中的一种:
-
a a a b b b 同时加一
-
a a a b b b 同时减一
-
a a a b b b 同时除以他们的一个质公因数
要求用最少的操作数,让 a a a, b b b中的一个变成 1 1 1。
思路
发现了一个特别重要的东西 ,
a a a b b b 同时加一 , a a a b b b 同时减一 , 差值不变。
a a a b b b 同时除以他们的一个质公因数 , 差值变为原来的“该质公因数”分之一。
所以,我们假设最终的状态是 ξ = ( 1 , x ) \xi = (1,x) ξ=(1,x) , 令 d = ∣ b − a ∣ − ∣ x − 1 ∣ d = |b-a| - |x-1| d=∣b−a∣−∣x−1∣
将 d d d质因数分解 , 得 d = p 1 k 1 + p 2 k 2 + . . . + p n k n d = p_1^{k_1} + p_2^{k_2} + ... + p_n^{k_n} d=p1k1+p2k2+...+pnkn
那么, ( a , b ) (a,b) (a,b)必然是通过除以 k 1 k_1 k1 次 p 1 p_1 p1, k 2 k_2 k2 次 p 2 p_2 p2, k n k_n kn 次 p n p_n pn 得到 ξ \xi ξ 的。
然后简单了,一个 1 0 9 10^9 109以内的数,它的质因数总个数不会超过 30 30 30 个 (废话*2) , 所以我们可以考虑暴力地状态转移。
f ( a , b ) f(a,b) f(a,b)表示 a , b a,b a,b达到终态所需的最小步数 , 那么 f ( a , b ) f(a,b) f(a,b)可以转移到 f ( a / k , b / k ) , f ( a + 1 , b + 1 ) , f ( a − 1 , b − 1 ) f(a/k,b/k) ,f(a+1,b+1) , f(a-1,b-1) f(a/k,b/k),f(a+1,b+1),f(a−1,b−1)
有点麻烦,改一下,设 f ( a , d ) f(a,d) f(a,d) 表示 a , d = ∣ a − b ∣ a, d = |a-b| a,d=∣a−b∣达到终态所需的最小步数 , f ( a , d ) f(a,d) f(a,d) 可以从 f ( ⌊ a p ⌋ , d p ) f(\lfloor\frac ap\rfloor,\frac dp) f(⌊pa⌋,pd) , f ( ⌈ a p ⌉ , d p ) f(\lceil\frac ap\rceil,\frac dp) f(⌈pa⌉,pd) 转移。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+100;
vector<int> prime;
vector<int> p;
map<pair<int,int>,int> mp;
bool vis[N];
void pre(){
vis[1] = 1;
for(int i=2;i<=500000;i++){
if(!vis[i]){
prime.push_back(i);
for(int j=i*2;j<=500000;j+=i)
vis[j] = true;
}
}
}
void get(int d){
for(int i=0;i<prime.size();i++){
int now = prime[i];
if(now>d) break;
if(d%now==0){
p.push_back(now);
while(d%now==0) d/=now;
}
}
if(d>1) p.push_back(d);
}
int solve(int a,int d){
pair<int,int> pp = make_pair(a,d);
if(a==1) return 0;
else if(mp[pp]) return mp[pp];
int res = a-1;
for(auto v:p){
if(d%v==0){
int x = solve(a/v, d/v)+a%v; // 减完除
int y = solve(a/v+1,d/v)+v-a%v; // 加完除
res = min({res,1+x,1+y});
}
}
mp[pp] = res;
return res;
}
signed main(){
ios::sync_with_stdio(false);
pre();
int t;
cin>>t;
while(t--){
int a,b;
cin>>a>>b;
mp.clear(),p.clear();
if(a>b) swap(a,b);
if(a==1){
cout<<0<<endl;
continue;
}
else if(b-a==1){
cout<<a-1<<endl;
continue;
}
else if(a==b){
cout<<1<<endl;
continue;
}
get(b-a);
cout<<solve(a,b-a)<<endl;
}
return 0;
}
I. Cloud Retainer’s Game(数学+思维)
题意
给一个二维的平面坐标系,下边界在 y = 0 y = 0 y=0 , 上边界在 y = H y = H y=H 。
有一个小球在 ( 0 , 0 ) (0,0) (0,0) 点处被释放, 它初始的速度向量是 v ⃗ = ( 1 , 1 ) \vec v = (1,1) v=(1,1) 。
平面内有 m m m 个硬币 ( x i , y i ) (x_i,y_i) (xi,yi)和 n n n 个障碍 ( x i ′ , y i ′ ) (x'_i,y'_i) (xi′,yi′) 。
当小球吃到硬币时,你的积分 + 1 。 当小球撞到障碍或上下边界时 , 会被反弹 (速度向量的 y y y 分量取反 , x x x 分量不变)
你可以去除任意多个障碍,问能得到的积分最大值是多少?
输入多组样例 t t t, 每个样例第一行是 H H H , 然后是 n n n个障碍,然后是 m m m个小球。
其中 2 ≤ H ≤ 1 0 9 , 1 ≤ n , m ≤ 1 0 5 , 1 ≤ x i ≤ 1 0 9 , 1 ≤ y i < H 2≤H≤10^9 , 1≤n,m≤10^5, 1≤x_i≤10^9 , 1≤y_i<H 2≤H≤109,1≤n,m≤105,1≤xi≤109,1≤yi<H , 多组样例保证 ( n + m ) < = 5 ∗ 1 0 5 (n+m) <= 5*10^5 (n+m)<=5∗105。
input
2
4
3
1 1
2 2
6 2
4
3 1
3 3
5 1
7 3
3
1
4 2
3
1 1
6 2
9 1
output
3
3
思路
官方解答:
考虑到速度向量只能有两种情况 v ⃗ = ( 1 , 1 ) \vec v = (1,1) v=(1,1) 、 v ⃗ = ( 1 , − 1 ) \vec v = (1,-1) v=(1,−1)
我们会发现一个点只能从两条路径转移过来,因此我们希望找到一个用于描述路径信息的"偏移量",同时满足:
1.在不遇到障碍的情况下, 偏移量不变
2.一个偏移量能指定描述一条折线
然后,不难发现, 在无障碍情况下,有:
正向直线:
y = x y = x y=x
y = x − 2 ∗ H y = x-2*H y=x−2∗H
y = x − 4 ∗ H y = x-4*H y=x−4∗H
…
反向直线
y = 2 ∗ H − x y = 2*H - x y=2∗H−x
y = 4 ∗ H − x y = 4*H - x y=4∗H−x
y = 6 ∗ H − x y = 6*H - x y=6∗H−x
…
每次增加 2 ∗ H 2*H 2∗H
那么, 当我们碰到障碍时,其实是改变了小球相对于 2*H 的偏移量:
如图, 当我们撞到一个位于 ( 4 , 4 ) (4,4) (4,4) 的障碍时 , 偏移量会比原来$ -4$
所以,我们可以找到这样一个偏移量 k:
向右下移动时 : k = ( x + y ) m o d 2 ∗ H k = (x+y) \mod 2*H k=(x+y)mod2∗H
向右上移动时: k = ( 2 ∗ H + x − y ) m o d 2 ∗ H k = (2*H + x - y) \mod 2*H k=(2∗H+x−y)mod2∗H
然后我们从最右上的点依次转移到最左下的点就好了, 显然答案就是偏移量 k = 0 k = 0 k=0时的解。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
map<int,int> dp;
struct Tuple{
int x;
int y;
bool val;
bool operator<(Tuple B) const{
if(x!=B.x) return x>B.x;
else return y>B.y;
}
};
vector<Tuple> v;
int n,m;
int H;
signed main(){
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--){
cin>>H;
cin>>n;
for(int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
v.push_back({x,y, false});
}
cin>>m;
for(int i=1;i<=m;i++){
int x,y;
cin>>x>>y;
v.push_back({x,y,true});
}
sort(v.begin(),v.end());
for(auto now:v){
int x = now.x,y = now.y;bool val = now.val;
if(val){
dp[(x+y)%(2*H)] += 1;
dp[(2*H+x-y)%(2*H)] +=1;
}
else{
dp[(x+y)%(2*H)] = max(dp[(x+y)%(2*H)],dp[(2*H+x-y)%(2*H)]);
dp[(2*H+x-y)%(2*H)] = max(dp[(x+y)%(2*H)],dp[(2*H+x-y)%(2*H)]);
}
}
cout<<dp[0]<<endl;
dp.clear();
v.clear();
}
return 0;
}