当前位置: 首页 > ops >正文

nflsoi 8.2 题解

C.#15563 ab串字典序 / AT_abc202_d aab aba baa

题意

请你求出由 AAAaBBBb 组成的长度为 A+BA+BA+B 的字符串中,按字典序排列后的第 KKK 个字符串。

1≤A,B≤301\le A,B\le 301A,B30

思路

最多有 2602^{60}260 种字符串,肯定不能一个一个枚举了。我们考虑在 1∼A+B1\sim A+B1A+B 位中填入恰当的 ab 使字典序恰为 KKK

dfs 决策一位填 a 还是填 b:我们发现填 b 会使字符串的字典序变大!

对于前面 t−1t-1t1 位全部相同,还有 A0A_0A0aB0B_0B0b 可以填。t∼A+Bt\sim A+BtA+B 位字符串 a (A0-1)×a (B0-1)×bb A0×a (B0-1)×b 这两个字符串,字典序会相差“A0−1A_0-1A01aB0B_0B0b 全排的不同方案数”。

因此填 b 升高那么多字典序,这个差值可以 dp 计数算出来:设 fi,jf_{i,j}fi,j 表示 iiiajjjb 全排的不同方案数,有转移:
fi,j←fi−1,j(i>0)fi,j←fi,j−1(j>0)\begin{matrix} f_{i,j}\leftarrow f_{i-1,j}(i>0)\\ f_{i,j}\leftarrow f_{i,j-1}(j>0) \end{matrix}fi,jfi1,j(i>0)fi,jfi,j1(j>0)

初始化 f0,0=1f_{0,0}=1f0,0=1

如果出现 a 或者 b 用完的情况,后面全部填其他字母即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=33;
ll a,b,t;
string lh[2][N];
ll f[N][N];
string dfs(ll rest,ll tota,ll totb)
{if(tota==0)return lh[1][totb];if(totb==0)return lh[0][tota];if(rest>f[tota-1][totb])return 'b'+dfs(rest-f[tota-1][totb],tota,totb-1);return 'a'+dfs(rest,tota-1,totb);//放b可以升高字典序 
}
int main()
{scanf("%lld%lld%lld",&a,&b,&t);f[0][0]=1;for(int i=0;i<=a;i++){for(int j=0;j<=b;j++){if(i)f[i][j]+=f[i-1][j];if(j)f[i][j]+=f[i][j-1];}}for(int i=1;i<=a;i++)lh[0][i]=lh[0][i-1]+'a';for(int i=1;i<=b;i++)lh[1][i]=lh[1][i-1]+'b';cout<<dfs(t,a,b);return 0;
}

D.#15528 复合函数查询 / AT_abc196_e Filters

题目传送门

感觉这题挺无脑的,是计算答案的上下界,然后搞一个加法的 tagtagtag,最后对询问的 x+tagx+tagx+tag 然后限制在上下界之间——简洁优美!

机房好多人做出来,好厉害。不过我暴力都有 85pts 的。。。

E.#4940 数对数字和 / AT_arc158_c All Pair Digit Sums

题意

对于正整数 xxx,记其各位数字之和为 f(x)f(x)f(x)。例如,f(158)=1+5+8=14f(158) = 1 + 5 + 8 = 14f(158)=1+5+8=14f(2023)=2+0+2+3=7f(2023) = 2 + 0 + 2 + 3 = 7f(2023)=2+0+2+3=7f(1)=1f(1) = 1f(1)=1

给定一个正整数序列 a=(a1,a2,…,aN)a = (a_1, a_2,\ldots, a_N)a=(a1,a2,,aN),请计算 ∑i=1N∑j=1Nf(ai+aj)\displaystyle\sum_{i=1}^N\sum_{j=1}^N f(a_i + a_j)i=1Nj=1Nf(ai+aj) 的值。

1≤N≤2×1051 \leq N \leq 2 \times 10^51N2×1051≤ai<10151 \leq a_i < 10^{15}1ai<1015

思路

感觉没法从 O(n2)O(n^2)O(n2) 暴力去优化,数字和和数字本身没什么特别的关联。于是考虑把 ai+aja_i+a_jai+aj 拆开。

根据小学奥数知识,如果两个数加法过程中,没有发生进位,结果的数字和就是两数数字和相加。否则每进一次位数字和就 −9-99

比如 58+4758+4758+478+78+78+7 进一次位,1+5+41+5+41+5+4 进一次位;f(105)=6=f(58)+f(47)−2×9f(105)=6=f(58)+f(47)-2\times 9f(105)=6=f(58)+f(47)2×9

那么答案变成:
∑i=1N∑j=1Nf(ai)+f(aj)−9z(ai,aj)\sum_{i=1}^N\sum_{j=1}^N f(a_i)+f(a_j)-9z(a_i,a_j)i=1Nj=1Nf(ai)+f(aj)9z(ai,aj)

=2n∑i=1nf(ai)−9∑i=1N∑j=1Nz(ai,aj)=2n\sum_{i=1}^n f(a_i)-9\sum_{i=1}^N\sum_{j=1}^N z(a_i,a_j)=2ni=1nf(ai)9i=1Nj=1Nz(ai,aj)

其中 z(x,y)z(x,y)z(x,y) 表示 x+yx+yx+y 过程中发生进位的次数。

减号左边 O(15n)O(15n)O(15n) 内随便算,问题还是在进位次数怎么算。

  • 两个 nnn 位数相加,最多发生 nnn 次进位;
  • 1≤ai<10151 \leq a_i < 10^{15}1ai<1015

aia_iai 再大顶天就 151515 位,我们对于所有数对我们可以分别考虑 1∼151\sim15115 位上是否发生进位。

如果我们对每个数按位拆开,不仅还是 O(n2)O(n^2)O(n2),而且还要考虑低位会不会进个 111 过来。

对于枚举的第 ttt 位,我们不妨记录每个数的末 ttt 位:记 bt,ib_{t,i}bt,i 表示 aia_iai 的末 ttt 位,存在 aja_jajaia_iai 相加可以在 ttt 进位,当且仅当 bt,j+bt,i≥10t+1b_{t,j}+b_{t,i}\ge 10^{t+1}bt,j+bt,i10t+1,即 jjj 要满足 bt,j≥10t+1−bt,ib_{t,j}\ge 10^{t+1}-b_{t,i}bt,j10t+1bt,i。我们对 btb_tbt 数组排序,容易用二分 lower_bound 计算合法的 jjj 的个数。

ans=ans*2*n;
ll ws=1;
for(int t=1;t<=15;t++)
{ws*=10;//10^{t+1}sort(b[t]+1,b[t]+n+1);for(int i=1;i<=n;i++){ll minus=n-(lower_bound(b[t]+1,b[t]+n+1,ws-b[t][i])-b[t])+1;//查大于等于10^{t+1}-b[t][i]的个数minusans-=9*minus;//ai+aj过程中第t为进位次数 }
}

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2e5+9;
ll n,a[N];
ll b[17][N];
ll cal(ll x)
{ll ret=0;while(x){ret+=x%10;x/=10;}return ret;
}
int main()
{scanf("%lld",&n);ll ans=0;for(int i=1;i<=n;i++){scanf("%lld",&a[i]);ans+=cal(a[i]);ll x=a[i],ws=1;for(int t=1;t<=15;t++){ws*=10;b[t][i]=x%ws;}}ans=ans*2*n;ll ws=1;for(int t=1;t<=15;t++){ws*=10;sort(b[t]+1,b[t]+n+1);for(int i=1;i<=n;i++){ll minus=n-(lower_bound(b[t]+1,b[t]+n+1,ws-b[t][i])-b[t])+1;//查大于等于10^{t+1}-b[t][i]的个数,b[j]单调 ans-=9*minus;//ai+aj过程中第t为进位次数 }}printf("%lld",ans);return 0;
}

F.#4548 树上游戏 / 洛谷 P10641 BZOJ3252 攻略

题意

给定一个有 nnn 个结点的树,树有点权且点权为正整数。现选取 kkk 条从根结点出发到叶子结点的简单路径,求这些路径的并集上所有结点的点权之和的最大值。

1≤n≤2×1051\leq n\leq 2\times 10^51n2×1051≤wi≤231−11\leq w_i\leq 2^{31}-11wi2311

思路

这就很树剖啊。

我们定义重儿子为链长更长的,然后把树给剖开成一条条链——因为走过的贡献不重复算。这样剖肯定能把树剖完。

然后我们把所有链长搞下来从大到小排序,取前 kkk 大即可。

多测记得清空。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=1e5+9;
int T,n,k;
ll a[N];
vector<int>G[N];
bool nolt[N];
int big[N];
ll lng[N];
void dfs(ll u,ll fa)//长剖 
{for(auto v:G[u]){if(v==fa)continue;dfs(v,u);if(lng[v]>lng[big[u]])big[u]=v;}lng[u]=lng[big[u]]+a[u];
}
int tot;
ll b[N];
bool cmp(ll x,ll y)
{return x>y;
}
void clean()
{tot=0;for(int i=1;i<=n;i++){nolt[i]=big[i]=lng[i]=0;G[i].clear(); }
}
int main()
{scanf("%d",&T);int tick=0;while(T--){tick++;clean();scanf("%d%d",&n,&k);for(int i=1;i<=n;i++)scanf("%lld",&a[i]);for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);G[u].push_back(v);G[v].push_back(u);}dfs(1,0);/*	cout<<"?剖:\n";for(ll u=1;u<=n;u++)cout<<big[u]<<" "<<lng[u]<<endl;*/for(int i=1;i<=n;i++)nolt[big[i]]=1;for(int i=1;i<=n;i++)if(!nolt[i])b[++tot]=lng[i];sort(b+1,b+tot+1,cmp);ll ans=0;for(int i=1;i<=min(tot,k);i++)ans+=b[i];printf("Case #%d: %lld\n",tick,ans);clean();}return 0;
}
http://www.xdnf.cn/news/17019.html

相关文章:

  • Druid与JdbcTemplate基本使用
  • vscode 关闭自动更新
  • 从达梦到 StarRocks:国产数据库实时入仓实践
  • Memcached 缓存详解及常见问题解决方案
  • P1002 [NOIP 2002 普及组] 过河卒
  • 06 基于sklearn的机械学习-欠拟合、过拟合、正则化、逻辑回归、k-means算法
  • 【RH124知识点问答题】第8章 监控和管理 Linux 进程
  • 关于解决WinRiver项目动态XmlElement的序列化与反序列化的问题
  • 2.1 vue组件
  • EXCEL删除数据透视表
  • HTTP各个版本对比
  • 网络资源模板--基于Android Studio 实现的消消乐游戏
  • 【机器学习】(算法优化二)提升算法之:AdaBoost与随机梯度
  • 37. line-height: 1.2 与 line-height: 120% 的区别
  • Redis真的是单线程的吗?
  • 【Unity3D实例-功能-镜头】第三人称视觉
  • 四、Linux 的实用操作
  • 【目标检测基础】——yolo学习
  • Servlet 相关笔记整理
  • Java 的 APT(Annotation Processing Tool)机制详解
  • 力扣 hot100 Day65
  • 基于Matlab实现LDA算法
  • 数据结构——单向链表部分操作及valgrind安装
  • 单片机裸机程序设计架构
  • webm 读取解析
  • 各种信号分解、模态分解方法合集【MATLAB实现】
  • 网络相关命令
  • TorchDynamo源码解析:从字节码拦截到性能优化的设计与实践
  • 复合机器人抓取精度怎么测量?
  • 8.4 打卡 DAY 33: 第一个神经网络 - MLP的构建与训练