P9376 题解
首先考虑怎么暴力。
考虑把每个数进行 \(B\) 进制分解,然后我们惊奇的发现这两个操作就是把最低位去掉和往最低位后面插入一个数。
然后我们顺藤摸瓜,把每个数的分解扔到 Trie 树上,我们发现我们要找到一个节点,使得所有单词节点到其的距离之和最短,答案就是这个最短距离。
这里直接考虑一个 Trie 树上 dp,记所有单词节点到节点 \(i\) 的最短距离为 \(dp_i\),然后直接去转移。
然后考虑找找性质。
记 \(sz_i\) 表示以节点 \(i\) 为根的子树内单词节点数量,我们发现节点 \(i\) 的转移如下 \(dp_i = dp_{fa_i} - sz_j + (sz_1 - sz_j)\)。
又因为 \(sz_i \leq sz_{fa_i}\),所以只有当节点 \(i\) 满足 \(sz_i \times 2 > sz_1\) 进入到以 \(i\) 为根的子树转移才最优。
而我们又发现对于一个节点满足条件的子节点至多只有 \(1\) 个。
也就是说如果把最优答案在树上的转移画出来,并称其为最优路径,那么首先这一定是一条从根出发的路径,而且以这条路径所代表的数为前缀的数一定超过总数的一半。
然后有两种优化方向。
第一种是利用可持久化 Trie 树预处理,然后直接在 Trie 上利用刚刚的性质暴力 \(\log_{B} V\) 查询,缺点是对于每个 \(B\) 都要建一棵树。
第二种是因为我们只在乎最优路径上的转移,所以我们随机抽取 \(\log n\) 个节点放到 Trie 树上,显然因为这个性质类似于绝对众数的性质,因此最优的转移路径不在 Trie 上出现的可能只有 \(\frac{1}{n}\)。那么多抽取几个节点就可以基本保证一定会出现。
那么转移路径找出来了,现在问题是转移中 \(sz_i\) 怎么求?
我们有发现 \(sz_i\) 等价于 \(B\) 进制下以从根节点到节点 \(i\) 所表示的数为前缀的数的数量,而这个可以枚举这个前缀后面有几位数转变成一个连续区间上查询权值为 \([L,R]\) 的数的数量,这个可以直接主席树来搞。这么做缺点是复杂度是 \(n \log_{B}^3 V\)。
考虑到 \(\log_{2} n\) 一般远大于 \(\log_{3} n\)。
所以结合两种算法,用第一种算法解决 \(B = 2\) 的询问,用第二种算法解决其他询问。
那么就做完了。
代码极丑,慎入。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int top = 100000001;
const int maxn = 1e6+114;
struct Node{
int sz;//当前这个节点子树内有多少个单词节点
vector <pair<int,int> > edge;//边
}Trie[maxn];
struct node{
int sz;
int ls,rs;
}_01trie[maxn*30];
struct NODE{
int val;
int ls,rs;
}SGTtree[maxn*30];
int SGTroot[maxn];
int SGTtot=1;
int SGTask(int ql,int qr,int lt,int rt,int L,int R){
if(rt<ql||lt>qr){
return 0;
}
if(ql<=lt&&rt<=qr){
return SGTtree[R].val-SGTtree[L].val;
}
int mid=(lt+rt)/2;
int res=0;
res+=SGTask(ql,qr,lt,mid,SGTtree[L].ls,SGTtree[R].ls);
res+=SGTask(ql,qr,mid+1,rt,SGTtree[L].rs,SGTtree[R].rs);
return res;
}
int SGTquery(int l,int r,int cl,int cr){
return SGTask(cl,cr,1,top,SGTroot[l-1],SGTroot[r]);
}
void SGTupdate(int cur,int lst,int lt,int rt,int pos,int v){
SGTtree[cur].val=SGTtree[lst].val+v;
if(lt==rt){
return ;
}
int mid=(lt+rt)/2;
if(pos<=mid){
SGTtree[cur].rs=SGTtree[lst].rs;
SGTtree[cur].ls=++SGTtot;
SGTupdate(SGTtree[cur].ls,SGTtree[lst].ls,lt,mid,pos,v);
}
else{
SGTtree[cur].ls=SGTtree[lst].ls;
SGTtree[cur].rs=++SGTtot;
SGTupdate(SGTtree[cur].rs,SGTtree[lst].rs,mid+1,rt,pos,v);
}
}
int qpow(int a,int b){
if(b==0) return 1;
if(b==1) return min(a,top);
int res=min(qpow(a,b/2),top);
res=min(top,res*res);
if(b%2==1) res=min(res*a,top);
return min(top,res);
}
void SGTadd(int pos,int val){
SGTroot[pos]=++SGTtot;
SGTupdate(SGTroot[pos],SGTroot[pos-1],1,top,val,1);
}
int SUMQUERY(int B,int L,int R){//查询 B 进制下区间 [L,R] 所有数长度之和
int res=0;
for(int k=1;k<=30;k++){
int l=min(top,qpow(B,k-1)),r=min(top,qpow(B,k)-1);
res+=SGTquery(L,R,l,r)*k;
if(r==top) break;
}
return res;
}
int PREQUERY(int x,int B,int L,int R){//查询 B 进制下区间 [L,R] 内多少个数前缀为 x
int res=0;
for(int k=0;k<=30;k++){
int l=min(top,x*qpow(B,k)),r=min(top,(x+1)*qpow(B,k)-1);
res+=SGTquery(L,R,l,r);
if(r==top) break;
}
return res;
}
int tot=1,anser;
int _01tot=1;
int sum;
int n,m;
int flag;
stack<int> s;
int root[maxn],Sum[maxn];
void _01insert(int cur,int lst){
_01trie[cur].sz++;
s.pop();
if(s.size()==0) return ;
if(s.top()==0){
_01trie[cur].rs=_01trie[lst].rs;
_01trie[cur].ls=++_01tot;
_01trie[_01trie[cur].ls].sz+=_01trie[_01trie[lst].ls].sz;
_01insert(_01trie[cur].ls,_01trie[lst].ls);
}
else{
_01trie[cur].ls=_01trie[lst].ls;
_01trie[cur].rs=++_01tot;
_01trie[_01trie[cur].rs].sz+=_01trie[_01trie[lst].rs].sz;
_01insert(_01trie[cur].rs,_01trie[lst].rs);
}
}
void _01dfs(int l,int r,int ans,int L,int R){
if(l==0&&r==0) return ;
anser=min(anser,ans);
if(_01trie[_01trie[r].ls].sz-_01trie[_01trie[l].ls].sz>_01trie[_01trie[r].rs].sz-_01trie[_01trie[l].rs].sz){
_01dfs(_01trie[l].ls,_01trie[r].ls,ans-(_01trie[_01trie[r].ls].sz-_01trie[_01trie[l].ls].sz)+(_01trie[R].sz-_01trie[L].sz-(_01trie[_01trie[r].ls].sz-_01trie[_01trie[l].ls].sz)),L,R);
}
else{
_01dfs(_01trie[l].rs,_01trie[r].rs,ans-(_01trie[_01trie[r].rs].sz-_01trie[_01trie[l].rs].sz)+(_01trie[R].sz-_01trie[L].sz-(_01trie[_01trie[r].rs].sz-_01trie[_01trie[l].rs].sz)),L,R);
}
}
int _01query(int l,int r){
anser=INT_MAX;
_01dfs(root[l-1],root[r],Sum[r]-Sum[l-1],root[l-1],root[r]);
return anser;
}
void _01add(int x,int B,int pos){
while(x!=0){
s.push(x%B);
x/=B;
}
s.push(0);
Sum[pos]=s.size()-1+Sum[pos-1];
root[pos]=++_01tot;
_01trie[root[pos]].sz+=_01trie[root[pos-1]].sz;
_01insert(root[pos],root[pos-1]);
}
void insert(int cur,int val){
Trie[cur].sz+=val;
s.pop();
if(s.size()==0) return ;
for(pair<int,int> v:Trie[cur].edge){
if(v.second==s.top()){
insert(v.first,val);
return ;
}
}
Trie[cur].edge.push_back(make_pair(++tot,s.top()));
insert(tot,val);
}
void add(int x,int B){
while(x!=0){
s.push(x%B);
x/=B;
}
s.push(0);
sum+=s.size()-1;
insert(1,1);
}
void dfs(int cur,int ans,int PRE,int S,int B,int L,int R){
anser=min(anser,ans);
for(pair<int,int> v:Trie[cur].edge){
int nxt=PRE*B+v.second;
int g=PREQUERY(nxt,B,L,R);
if((S-g)-g>=0) continue;
dfs(v.first,ans-g+(S-g),nxt,S,B,L,R);
}
}
int query(int B,int L,int R){
anser=INT_MAX;
dfs(1,SUMQUERY(B,L,R),0,(R-L+1),B,L,R);
return anser;
}
void clear(){
for(int i=1;i<=tot;i++){
Trie[i].edge.clear();
Trie[i].sz=0;
}
sum=0;
tot=1;
}
int a[maxn];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
SGTadd(i,a[i]);
_01add(a[i],2,i);
}
while(m--){
int l,r,B;
cin>>l>>r>>B;
if(B==2){
cout<<_01query(l,r)<<"\n";
}
else{
clear();
for(int j=1;j<=22;j++){
int x=rand()%(r-l+1)+l;
add(a[x],B);
}
cout<<query(B,l,r)<<'\n';
}
}
}
/*
5 2
7 6 5 8 9
1 3 2
2 5 2
*/
P9376 题解的更多相关文章
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
- noip2016十连测题解
以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...
- BZOJ-2561-最小生成树 题解(最小割)
2561: 最小生成树(题解) Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1628 Solved: 786 传送门:http://www.lyd ...
- Codeforces Round #353 (Div. 2) ABCDE 题解 python
Problems # Name A Infinite Sequence standard input/output 1 s, 256 MB x3509 B Restoring P ...
- 哈尔滨理工大学ACM全国邀请赛(网络同步赛)题解
题目链接 提交连接:http://acm-software.hrbust.edu.cn/problemset.php?page=5 1470-1482 只做出来四道比较水的题目,还需要加强中等题的训练 ...
- 2016ACM青岛区域赛题解
A.Relic Discovery_hdu5982 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- poj1399 hoj1037 Direct Visibility 题解 (宽搜)
http://poj.org/problem?id=1399 http://acm.hit.edu.cn/hoj/problem/view?id=1037 题意: 在一个最多200*200的minec ...
- 网络流n题 题解
学会了网络流,就经常闲的没事儿刷网络流--于是乎来一发题解. 1. COGS2093 花园的守护之神 题意:给定一个带权无向图,问至少删除多少条边才能使得s-t最短路的长度变长. 用Dijkstra或 ...
- CF100965C题解..
求方程 \[ \begin{array}\\ \sum_{i=1}^n x_i & \equiv & a_1 \pmod{p} \\ \sum_{i=1}^n x_i^2 & ...
- JSOI2016R3 瞎BB题解
题意请看absi大爷的blog http://absi2011.is-programmer.com/posts/200920.html http://absi2011.is-programmer.co ...
随机推荐
- 飞桨PaddleLite架构研读
一.架构全景图 二.源码详细解读 1. Lite体系下似乎有多种 op_desc/program_desc 的定义,之间的关系是什么?这样设计的背景和好处是什么? model_parser目录下,包含 ...
- vue 安装教程(如何在node环境下搭建vue项目)
如果要配置node.js环境请查看(node.js环境在Window和Mac中配置,以及安装cnpm和配置Less环境)在终端输入命令 cnpm -v出现版本号 1.安装vue window : ...
- 【项目学习】Timeswap:第一个完全去中心化的基于 AMM 的货币市场协议
总览 Timeswap 是世界上第一个完全去中心化的基于 AMM 的货币市场协议,无需预言机或清算人即可工作. Timeswap 采用 3 变量来维持 AMM 的运作.它通过允许用户决定他们的风险状况 ...
- 🔥httpsok彻底告别SSL证书续期烦扰
httpsok彻底告别SSL证书续期烦扰 介绍 httpsok 是一个便捷的 HTTPS 证书自动续签工具,专为 Nginx .OpenResty 服务器设计.已服务众多中小企业,稳定.安全.可靠. ...
- 在uGUI正交相机中实现旋转透视效果
正常uGUI使用正交相机的话,旋转是没有透视效果的,但如果能实现较简单的透视, 对一些效果表现来说还是不错的:见下图(左为透视效果): 正常思路感觉各种麻烦. 因为uGUI使用unity的x和y方向表 ...
- windows 10安装python2.7
一.官网下载 1.百度搜索"python"或者点击这个网址下载https://www.python.org/downloads/release/python-2716/ pytho ...
- 5GC 关键技术之网络切片
目录 文章目录 目录 前文列表 网络切片的需求来自于业务对网络提出的差异化要求 基于 3 大业务场景的切片 基于切片资源访问对象的切片 网络切片的商业价值 网络切片的底层技术支撑 网络切片的粒度 网络 ...
- USRP B210 软件定义的无线网络(SDR)支撑设备
目录 文章目录 目录 蜂窝网络 蜂窝网络的组成 USRP B210 USRP B210 的功能清单与相关参数 USRP B210 的系统结构与运行原理 相关知识储备 SDR RFIC RF 发展历程 ...
- 29.4K star! 仅需几行代码快速构建机器学习 Web 应用项目,无需前端技能!
大家好,我是狂师! 今天给大家推荐一款开源的Python库:Gradio! Gradio是一个开源的Python库,用于创建机器学习和数据科学的交互式应用和演示. 项目地址: https://gith ...
- C# EF 使用sqlite 数据库出现表名出现dbo的坑
当ef使用sqlite时,正常情况映射的表名是没有dbo开头的.这个dbo是映射的sa用户,而sqlite是没有用户的.所以映射出的sql语句是查不到数据的. 我在网上找半天解决方案,都不得行.后 ...