由于学校要求,过来打 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. cs231n__4.2 神经网络 Neural networks

    CS231n 学习笔记 4.2 神经网络 Neural networks 之前我们已经使用了 很多线性分类函数 现在我们不用单变换的: 我们首先有线性层,然后有这个非线性计算,继而在顶层再加入另一个线 ...

  2. TortoiseGit (小乌龟安装配置及使用)

    TortoiseGit 使用教程 (图文详解) 第一步 下载Git:下载地址:https://gitforwindows.org 第二步 下载TortoiseGit 以及 汉化包 (安装教程这里就不详 ...

  3. 金融科技 DevOps 的最佳实践

    随着软件技术的发展,越来越多的企业已经开始意识到 DevOps 文化的重要价值.DevOps 能够消除改变公司业务开展方式,并以更快的速度实现交付,同时创建迭代反馈循环以实现持续改进.而对于金融科技( ...

  4. 【机器学习】李宏毅——Domain Adaptation(领域自适应)

    在前面介绍的模型中,一般我们都会假设训练资料和测试资料符合相同的分布,这样模型才能够有较好的效果.而如果训练资料和测试资料是来自于不同的分布,这样就会让模型在测试集上的效果很差,这种问题称为Domai ...

  5. docker搭建Elasticsearch、Kibana、Logstash 同步mysql数据到ES

    一.前言 在数据量大的企业级实践中,Elasticsearch显得非常常见,特别是数据表超过千万级后,无论怎么优化,还是有点力不从心!使用中,最首先的问题就是怎么把千万级数据同步到Elasticsea ...

  6. 【ASP.NET Core】按用户角色授权

    上次老周和大伙伴们分享了有关按用户Level授权的技巧,本文咱们聊聊以用户角色来授权的事. 按用户角色授权其实更好弄,毕竟这个功能是内部集成的,多数场景下我们不需要扩展,不用自己写处理代码.从功能语义 ...

  7. Longhorn+K8S+KubeSphere云端数据管理,实战 Sentry PostgreSQL 数据卷增量快照/备份与还原

    云端实验环境配置 VKE K8S Cluster Vultr 托管集群 https://vultr.com/ 3 个 worker 节点,kubectl get nodes. k8s-paas-71a ...

  8. 判断一个对象是否是数组的n个方法,typeOf不能判断引用类型对象

  9. elementui表格自定义格式实现原理???

    <html> <head> <title>学习</title> <meta charset="utf-8"> <m ...

  10. excel空格处理

    private String StringTrim(String str){ return str.replaceAll("[\\s\\u00A0]+","") ...