由于学校要求,过来打 USACO。

由于上次已经打到白金了,所以继续。

然后还是 AK 了。

感觉题意很迷惑,所以都翻译一下。

Hungry Cow

Bessie 很饿,每天晚饭如果有干草就会吃 \(1\) 份,没有就不吃,初始没有干草。

每天早上 Farmer John 会给它送若干干草,设第 \(k\) 天送 \(a_k\) 份干草,初始时 \(a_k=0\),表示该天不送干草。

\(q\) 次操作,每次给出 \(x,y\),表示将 \(a_x\) 改成 \(y\),请将在此时 Bessie 有干草吃的日期编号求和并输出。对 \(10^9+7\) 取模。

操作间互不独立。

\(1\le q\le10^5\),\(1\le x\le10^{14}\),\(0\le y\le10^9\)。

注意到这个过程是一个“前时间”对“后时间”的贡献,考虑兔队线段树。

维护区间内有干草吃的编号数 \(cnt\)、编号和 \(ans\),以及对区间外的贡献 \(more\)。

单点修改,全局查询。

考虑计算左侧区间对当前节点带来 \(c\) 的前缀贡献时,区间内三个值的值。

当填满左儿子空位时,就不用向左递归;否则,只用向左递归,右儿子结果和 \(c=0\) 时的右儿子结果一致。

总复杂度 \(O(q\log^2v)\)。

核心代码:

const ullt Mod=1e9+7,inv2=5e8+4;
typedef ConstMod::mod_ullt<Mod>modint;
typedef std::vector<modint>modvec;
inline modint sum(ullt l,ullt r){return modint(r-l)*(l+r-1)*inv2;}
struct Seg{
Seg*L,*R;ullt id,len,cnt,more;modint ans;
Seg(ullt l,ullt r):L(NULL),R(NULL),id(l),len(r-l),cnt(0),more(0),ans(){}
voi chg(ullt p,ullt v){
if(len==1){
if(v)cnt=1,more=v-1,ans=id;else cnt=more=0,ans=0;
return;
}
if(p<(len>>1)){
if(!L)L=new Seg(id,id+(len>>1));
L->chg(p,v);
}
else{
if(!R)R=new Seg(id+(len>>1),id+len);
R->chg(p-(len>>1),v);
}
ans=L?L->ans:0,cnt=L?L->cnt:0;
ullt Lv=L?L->more:0;
auto g=R?R->cal(Lv):std::pair<modint,std::pair<ullt,ullt> >
{sum(id+(len>>1),id+std::min(len,Lv+(len>>1))),
{std::min(len-(len>>1),Lv),Lv-std::min(len-(len>>1),Lv)}};
ans+=g.first,cnt+=g.second.first,more=g.second.second;
}
std::pair<modint,std::pair<ullt,ullt> >cal(ullt c){
if(!c)return{ans,{cnt,more}};
if(c+cnt>=len)return{sum(id,id+len),{len,c+cnt-len+more}};
// printf("%llu %llu\n",id,id+len);
auto g1=L?L->cal(c):std::pair<modint,std::pair<ullt,ullt> >
{sum(id,id+std::min(len>>1,c)),{std::min(len>>1,c),c-std::min(len>>1,c)}};
ullt Lv=g1.second.second;
if(Lv==(L?L->more:0))return{ans-(L?L->ans:0)+g1.first,{g1.second.first+cnt-(L?L->cnt:0),more}};
auto g2=R?R->cal(Lv):std::pair<modint,std::pair<ullt,ullt> >
{sum(id+(len>>1),id+std::min(len,Lv+(len>>1))),
{std::min(len-(len>>1),Lv),Lv-std::min(len-(len>>1),Lv)}};
return{g1.first+g2.first,{g1.second.first+g2.second.first,g2.second.second}};
}
};

Problem Setting

Farmer John 出了 \(n\) 道题,聘了 \(m\) 个验题人来品鉴难度。

难度只有简单(E)和困难(H)两种。

Farmer John 将从中选出若干道(至少一道),并以一定顺序排列,使得前一道题目中任意一个觉得此题困难的验题人也觉得后面一道题目困难。

回答有多少种选出来并排列的方案,对 \(10^9+7\) 取模。

\(1\le n\le10^5\),\(1\le m\le20\)。

考虑把 E 视作 \(0\),H 视作 \(1\),就是相邻项为子集包含。

考虑把状态相同的数一起考虑,假设出现了 \(t\) 次,选择其的方案数即为

\[G_t=\sum_{k>0}k!\binom tk=\sum_{k>0}t^{\underline k}=t!\sum_{k<t}\frac1{k!}
\]

可以 \(O(n)\) 预处理。

假设选的最后一个集合为 \(S\) 的方案数为 \(f_S\),集合为 \(S\) 的元素有 \(a_S\) 个,则

\[f_S=G_{a_S}(1+\sum_{T\subsetneq S}f_T)
\]

直接子集枚举是 \(O(3^m)\) 的,不优,考虑更优做法。

注意到这个东西是一个半半在线卷积的形式,直接按 \(|S|\) 递增序进行 FMT 即可。其实就是高维前缀和。

总复杂度 \(O(nm+m^22^m)\),可以通过。

听说有 \(O(nm+m2^m)\) 做法,不懂哦,可能因为这里是子集和的形式吧。

核心代码:

const ullt Mod=1e9+7;
typedef ConstMod::mod_ullt<Mod>modint;
typedef std::vector<modint>modvec;
chr C[20][100005];uint Cnt[1u<<20|1],nLim;
modint P[100005],Q[100005],G[100005],Dp[21][1u<<20|1],User[1u<<20|1];
voi FWT(modint*x,bol op){
for(uint i=1;i<nLim;i<<=1)for(uint j=0;j<nLim;j+=i<<1)for(uint k=0;k<i;k++)
op?x[i+j+k]-=x[j+k]:x[i+j+k]+=x[j+k];
}
int main()
{
#ifdef MYEE
freopen("QAQ.in","r",stdin);
// freopen("QAQ.out","w",stdout);
#endif
uint n,m;scanf("%u%u",&n,&m);
for(uint i=0;i<m;i++)scanf("%s",C[i]);
for(uint i=0;i<n;i++){
uint v=0;
for(uint j=0;j<m;j++)if(C[j][i]=='H')v|=1u<<j;
Cnt[v]++;
}
P[0]=1;for(uint i=1;i<=n;i++)P[i]=P[i-1]*i;
Q[n]=P[n].inv();for(uint i=n;i;i--)Q[i-1]=Q[i]*i;
for(uint i=0;i<n;i++)G[i+1]=G[i]+Q[i];
for(uint i=0;i<=n;i++)G[i]*=P[i];
modint ans;
nLim=1u<<m;
for(uint j=0;j<=m;j++){
for(uint k=0;k<(1u<<m);k++)if(__builtin_popcount(k)==j){
Dp[j][k]=1;
for(uint t=0;t<j;t++)Dp[j][k]+=Dp[t][k];
ans+=Dp[j][k]*=G[Cnt[k]];
}
FWT(Dp[j],0);
}
ans.println();
return 0;
}

Watching Cowflix

Bessie 喜欢在 Cowflix 上看节目,并且喜欢在农场里的不同地方看。

Farmer John 的农场可以被描述成一颗 \(n\) 个节点的树,并且 Bessie 只可能在树上的一些指定的节点处看节目。每个节点是否要看节目将在初始时给定;保证至少在一个节点处会看节目。

不幸的是,Cowflix 为了避免奶牛们使用公用账号,采取了一个新的会员策略:

Bessie 将多次付款,每次选择树上任意一个大小为 \(d\) 的联通块,为其支付 \(d+k\) 的代价,才能够在这些位置看节目。

换言之,Bessie 将选取若干联通块 \(c_1,c_2,\dots,c_C\),支付 \(\sum_{i=1}^C(|c_i|+k)\) 的代价,才可以在这些联通块的各个节点处看节目;即,被指定的节点必须被某个联通块包含,不被指定的节点不必被包含

Bessie 觉得这个策略的代价太昂贵了,考虑是否要改在 Mooloo 上看节目。为了帮助其决策,你应当告诉之 \(k\) 取遍 \(1\sim n\) 时看节目的最小总代价。

\(1\le n\le2\times10^5\)。

先考虑 \(n\le5000\) 时怎么做。

我们对每个 \(k\) 考虑怎么暴力求答案。

考虑 dp,\(f_{p,0/1}\) 表示在 \(p\) 的子树中,当前根节点是否被选入某个联通块的最小代价;一个联通块额外的 \(k\) 的代价将在其根节点向父亲做贡献时计算。

\[f_{p,0}=\begin{cases}+\infty&p\text{ 必须被选入某个联通块}\\\sum_s\min\{f_{s,0},f_{s,1}+k\}&\text{otherwise.}\end{cases}
\]
\[f_{p,1}=1+\sum_s\min\{f_{s,0},f_{s,1}\}
\]

最后 \(\min\{f_{p,0},f_{p,1}+k\}\) 即为答案。

这样我们就得到一个 \(O(n)\) dp 的方法,对每个 \(k\) 暴力做一次,复杂度 \(O(n^2)\)。

(不过想在 USACO 的老爷机上过 \(5000\) 还是比较困难的,可能要加一些常数优化)

接下来考虑怎么优化。

一种想法是 slope trick,但是这个东西非常没有前途,常数也不小。

考虑另一种方法:优化暴力!

注意到,\(k=n\) 时答案仍不超过 \(2n\),我们的答案不会很大;同时,容易发现答案关于 \(k\) 具有凸性

因此,我们的答案是一个关于 \(k\) 的凸壳

我们考虑把凸壳上的每一段相同的线段一起处理,其为等差数列

我们来证明一下凸壳上斜率不同的线段数目;显然斜率均为正整数

假设斜率为 \(k\) 的线段有 \(a_k\) 条,则

\[\sum_kka_k\le2n
\]

于是 \(a_k>0\) 的 \(k\) 是 \(O(\sqrt n)\) 级别的!

考虑直接二分该等差数列在何处结束,复杂度为 \(O(n\sqrt n\log n)\) 的。

这种东西显然过不去,考虑优化一下复杂度。

我们把二分换掉,改成先倍增,倍增到不可行后再二分

这样,对斜率为 \(k\) 的线段,我们的查询轮数为 \(O(\log a_k)\) 的。

我们考虑分析一下 \(O(\sum_{a_k>0}\log a_k)\) 的级别。

我们把 \(\log a_k\) 描述为 \(\sum_t[2^t\le a_k]\),则即

\[O(\sum_{a_k>0}\log a_k)=O(\sum_{a_k>0}\sum_t[2^t\le a_k])=O(\sum_t\sum_{a_k\ge2^t}1)
\]

由于

\[\sum_{a_k\ge w}k\le\sum_{a_k\ge w}k\lfloor a_k/w\rfloor\le\frac1w\sum_kka_k\le\frac{2n}{w}
\]

因此满足 \(a_k\ge w\) 的 \(k\) 是 \(O(\sqrt{n/w})\) 级别的。

因此

\[O(\sum_t\sum_{a_k\ge2^t}1)=O(\sum_t\sqrt{\frac{n}{2^t}})=O(\sqrt n)
\]

因此只用做 \(O(\sqrt n)\) 轮查询。

总复杂度即为 \(O(n\sqrt n)\)。

这个东西如果实现的常数不够优秀,在 USACO 的老爷机上会 T!L!E!

因此考虑一个常数优化技巧:我们每轮 dp 进行 dfs 的常数太大了,且内存访问不连续,考虑预先进行一遍 dfs,把每个节点重标号一下,使得父亲标号小于当前标号,这样每次 dp 只用进行一轮循环即可了。

实践表明,这样的常数大约是原来的 \(1/6\) 左右,可以轻松通过。

核心代码:

std::vector<uint>Way[200005];
chr C[200005];
uint Dp[2][200005];
uint Dfn[200005],Fath[200005],cnt;
bol Op[200005];
voi dfs0(uint p,uint f){
Fath[Dfn[p]=cnt]=~f?Dfn[f]:-1u,Op[cnt++]=C[p]=='1';
for(auto s:Way[p])if(s!=f)dfs0(s,p);
}
std::map<uint,uint>M;
uint find(uint v){
if(M.count(v))return M[v];
for(uint i=0;i<cnt;i++)Dp[0][i]=0,Dp[1][i]=1;
for(uint i=cnt-1;i;i--){
if(Op[i]){
Dp[0][Fath[i]]+=Dp[1][i]+v;
Dp[1][Fath[i]]+=Dp[1][i];
}
else{
Dp[0][Fath[i]]+=std::min(Dp[0][i],Dp[1][i]+v);
Dp[1][Fath[i]]+=std::min(Dp[0][i],Dp[1][i]);
}
}
if(Op[0])Dp[0][0]=1e9;
return M[v]=std::min(Dp[0][0],Dp[1][0]+v);
}
uint Ans[200005];
int main()
{
#ifdef MYEE
freopen("QAQ.in","r",stdin);
// freopen("QAQ.out","w",stdout);
#endif
uint n;scanf("%u%s",&n,C);
for(uint i=1,u,v;i<n;i++)scanf("%u%u",&u,&v),Way[--u].push_back(--v),Way[v].push_back(u);
// printf("%u\n",find(1));
dfs0(0,-1);
for(uint l=1;l<=n;){
Ans[l]=find(l);
if(l==n)break;
Ans[l+1]=find(l+1);
if(Ans[l+1]==Ans[l]+1){
for(uint j=l+2;j<=n;j++)
Ans[j]=Ans[j-1]+1;
break;
}
uint len=2;
while(l+len<=n){
if(find(l+len)-Ans[l]!=(Ans[l+1]-Ans[l])*len)
break;
len<<=1;
}
uint p=l+(len>>1);
while(len>1){
uint mid=p+(len>>=1);
if(mid<=n&&find(mid)-Ans[l]==(Ans[l+1]-Ans[l])*(mid-l))p=mid;
}
for(uint j=l+2;j<=p;j++)Ans[j]=Ans[j-1]*2-Ans[j-2];
l=p+1;
}
for(uint i=1;i<=n;i++)printf("%u\n",Ans[i]);
return 0;
}

USACO2023Feb游记的更多相关文章

  1. 【20161203-20161208】清华集训2016滚粗记&&酱油记&&游记

    先挖坑(这个blog怎么变成游记专用了--) 已更完 #include <cstdio> using namespace std; int main(){ puts("转载请注明 ...

  2. 【20160722-20160728】NOI2016滚粗记&&酱油记&&游记

    先挖坑 #include <cstdio> using namespace std; int main(){ puts("转载请注明出处:http://www.cnblogs.c ...

  3. NOIp2016 游记

    DAY -2 不要问我为什么现在就开了一篇博客. 本来想起个NOIp2016爆零记或者NOIp2016退役记之类的,但是感觉现在不能乱立flag了.所以就叫游记算了. 前几场模拟赛崩了一场又一场,RP ...

  4. NOIP2016游记

    只是游记而已.流水账. Day0:忘了. Day1:看完T1,本以为T2一如既往很简单,结果看了半天完全没有思路.然后看了一眼T3,期望,NOIP什么时候要考期望了,于是接着看T2.一开始我推的限制条 ...

  5. CTSC2016&&APIO2016滚粗记&&酱油记&&游记<del>(持续更新)</del>

    挖一波坑 #include <cstdio> using namespace std; int main(){ puts("转载请注明出处:http://www.cnblogs. ...

  6. 游记——noip2016

    2016.11.18 (day 0) 呆在家. 悠闲地呆在家.. 明后天可能出现的错误: 1)没打freopen.打了ctime: 2)对拍程序忘记怎么写了...忘记随机化种子怎么写了: 3)不知道厕 ...

  7. 【NOIP 2015 & SDOI 2016 Round1 & CTSC 2016 & SDOI2016 Round2】游记

    我第一次写游记,,,, 正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪里?正文在哪 ...

  8. ZJOI2016二试+游记

    ...excited.... 一场打回原形爽哦. T1莫名爆到了10分,T2T3均没交,一个小时过后就没再拿任何分数,perfectly狗带了... 总之没有给自己充足的时间去敲暴力,ZJOI啊..拿 ...

  9. NOIP2015游记——一次开心又失望的旅行

    啊,一年一度的NOIP终于是结束了 以前的大神都有写自己的感受 然而我居然给忘了!!!! 吓得我赶紧来写一份游记 Day.-INF--出发前一个星期 机智的我选择了停课 就是为了OIER这伟大而又光荣 ...

  10. CTSC2017 && APIO2017 && THUSC2017 游记

    一去北京就是近20天,还是回来写写游记吧. 5.6 坐飞机到天津转动车到北京. 5.7 在天坛公园逛了一圈就去报到了. 下午试机,好像没发生什么. 5.8 CTSC一试 T1签到,开个桶打个标记就好了 ...

随机推荐

  1. 二阶段目标检测网络-FPN 详解

    论文背景 引言(Introduction) 特征金字塔网络 FPN FPN网络建立 Anchor锚框生成规则 实验 代码解读 参考资料 本篇文章是论文阅读笔记和网络理解心得总结而来,部分资料和图参考论 ...

  2. MySQL简介、下载、密码修改及基本使用

    目录 存取数据的演变史 数据库软件应用史 数据库的本质 数据库的分类 MySQL简介 MySQL基本使用 系统服务的制作 密码相关操作 SQL与NoSQL 数据库重要概念 基本SQL语句 存取数据的演 ...

  3. 使用 BenchmarkDotNet 比较指定容量的 List 的性能

    我们之前提到 List 是 .NET 中常用的数据结构,其在存储大量数据时,如果能够指定它的初始化容量,就会有性能提升.这个优化的方法并不是很明显,因此本文将使用 BenchmarkDotNet 库, ...

  4. go语言的切片特性

    概述: 在使用切片进行赋值的时候,产生新的数组的指针指向原来的数组,只要修改新数组中的元素时,原来数组的元素也会改变. 测试代码: func TestSliceShareMemory(t *testi ...

  5. 【转载】SQL SERVER 表变量与临时表的优缺点

    什么情况下使用表变量?什么情况下使用临时表? -- 表变量: DECLARE @tb table(id int identity(1,1), name varchar(100)) INSERT @tb ...

  6. 一文教会你mock(Mockito和PowerMock双剑合璧)

    作者:京东物流 杨建民 1.什么是Mock Mock有模仿.伪造的含义.Mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法.mock工具使用 ...

  7. Java进阶篇——设计模式

    设计模式 一.代理模式 使用代理类对真实对象进行代理,包括真实对象方法的调用.功能的扩展等.访问的时候也只能访问到代理对象,既保护了真实对象同时可以在原始对象上进行扩展.类似于中介在卖家和买家之间的角 ...

  8. 广工Anyview【DC02PE97】解析

    前言 由于是出成绩后一段时间写的,已经有点遗忘当时遇到的情况,同时该代码不是最优解,需要精简代码的同学可以想想办法解决奇偶长度和有时候头结点不为空的问题,这样就可以极大程度上解决我这个代码的冗余. 题 ...

  9. JavaScript 检查(Linting)工具的比较

    一个好的检查(linting)工具可以确保一个项目遵循编码规范. 让我们来看看四种流行替代方案的特性和优缺点:JSLint , JSHint , JSCS 和 ESLint . JSLint JSLi ...

  10. 【学习笔记】XR872 GUI Littlevgl 8.0 移植(文件系统)

    不得不提 在移植的过程中,发现 LVGL 的文件操作接口并不十分完善,在我看来, LVGL 的文件操作接口,应该更多的是为了 LVGL 内部接口方便读取资源文件而设立的,例如读取图像文件,加载字库文件 ...