
日记
上午
切了一题
CF2027D1 The Endspeaker (Easy Version)
还是那句话:既然出现在dp专题里,那肯定就是dp咯
我们设 表示在 时能否获胜。我们发现,正着推好像不大行,那么,正难则反。
我们考虑从 ,反着推。那么有明显的转移: 。但是,枚举状态就有 的复杂度了,转移还有 。合起来就是 。这不给你T飞起来。
我们考虑怎么 转移。我们发现, 可以用二维后缀和维护。那么最外面的循环枚举 (本该如此),然后里面是 的转移,等它转移完了,我们再 维护一下二维后缀和,这样就达到的 转移的效果,总复杂度 ,就可以了。
中午 & 下午
打了一下午游戏,在打游戏的时候尝试切一道线段树,但是失败了。
晚上
今天晚上又有 arc
又有 codeforces
的,本来都不想来,但是还是来学校打了。顺便把之前没写完的日记补了。
CF2027D The Endspeaker solution
我们先来看 easy version
。这一看就很有dp的感觉啊,于是我们就设 表示当 ,剩的第一个元素下标为 的时候的最小代价。初始化时 。然后我们就可以考虑如何转移:
- 第一种操作时,我们从 开始找到第一个位置 使得 ,然后就可以转移: 。
- 第二种操作时,有显然的转移: 。
然后怎么统计答案呢?我们想:要把 数组去完,那么答案肯定是某个 ( 我们还不知道),然后,我们又发现,其实这个 是多少无关紧要,那么我们就有答案: 。
这是 easy version
啊,现在我们来看 easy version
的 hard version
( yzp
名言)
我们看 hard version
它 hard
在哪里。我们看:它不仅让我们求最小的代价是多少,他还让我们求有多少种方案能够达到最小的代价。我们发现,对于 ,都可以从 转移: 。在简单版中不这么做是因为答案不会更优还浪费时间复杂度,而现在要统计方案数了,那么就要考虑一下了。而我们看要更新 这个区间,那么我们就想到线段树。
因为实现实在是太美所以就放一下代码:
CPP
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
#include<bits/extc++.h>
#define int long long
#define inf 0x7f7f7f7f7f7f7f7f
#define pii pair<int,int>
#define mkp make_pair
using namespace std;
const int maxn = 3e5 + 5;
const int mod = 1e9 + 7;
void operator+=(pii &x,pii &y)//这里就类似统计答案
{
if(x.first != y.first)//如果方案更优就更新方案
{
if(x.first > y.first)
x = y;
}//这里一定一定要打大括号
else//如果方案相同就更新方案数
x.second = (x.second + y.second) % mod;
}
int n,m;
int a[maxn],b[maxn];
int pre[maxn];
struct Rukkhadevata//又好用码量又大的树:大慈树
{
int l,r;
pii val;
}tree[maxn << 2];
void push_down(int rt)
{
if (tree[rt].val.first == inf)
return;
tree[rt << 1].val += tree[rt].val;
tree[rt << 1 | 1].val += tree[rt].val;
tree[rt].val = mkp(inf,0);
}//lazy下放
//为何直接拿val当lazy:因为不用维护区间查询,所以对于非叶子节点的val没有意义,就拿来当lazy
void build(int l,int r,int rt)
{
tree[rt].l = l;
tree[rt].r = r;
tree[rt].val = mkp(inf,0);
if (l == r)
{
if (l == 1)
tree[rt].val = mkp(0,1);
return;
}
int mid = l + r >> 1;
build(l,mid,rt << 1);
build(mid + 1,r,rt << 1 | 1);
}
void upd(int ql,int qr,pii x,int rt)
{
int l = tree[rt].l;
int r = tree[rt].r;
if (ql <= l && r <= qr)
{
tree[rt].val += x;
return;
}
push_down(rt);
int mid = l + r >> 1;
if (ql <= mid)
upd(ql,qr,x,rt << 1);
if (qr > mid)
upd(ql,qr,x,rt << 1 | 1);
}
pii que(int x,int rt)
{
int l = tree[rt].l;
int r = tree[rt].r;
if (l == r)
return tree[rt].val;
push_down(rt);
int mid = l + r >> 1;
if (x <= mid)
return que(x,rt << 1);
else
return que(x,rt << 1 | 1);
}
void solve()
{
scanf("%lld%lld",&n,&m);
for (int i = 1; i <= n; i++)
{
scanf("%lld",a + i);
pre[i] = pre[i - 1] + a[i];
}
for (int j = 1; j <= m; j++)
scanf("%lld",b + j);
build(1,n,1);
pii ans = mkp(inf,0);
for (int j = 1; j <= m; j++)
{
pii tmp1 = mkp(inf,0);
for (int i = 1; i <= n; i++)
{
int pos = upper_bound(pre + i,pre + n + 1,b[j] + pre[i - 1]) - pre;//找到c
if (i < pos)
{
pii tmp2 = que(i,1);
tmp2.first += m - j;
if (pos == n + 1)
{
tmp1 += tmp2;
pos --;
}
if (i < pos)
upd(i + 1,pos,tmp2,1);//转移
}
}
ans += tmp1;//记录答案
}
if (ans.first != inf)
printf("%lld %lld\n",ans.first,ans.second);
else
puts("-1");
}
signed main()
{
int t;
scanf("%lld",&t);
while (t--)
solve();
return 0;
}