Problem A 树状数组

  给出下列$C++$代码:

  

  设区间加操作$modify(l,r)$为调用两次$update(r,1)$和$update(l-1,-1)$

  设$f(l,r)$表示在初始$cnt[i]$全部是$0$的情况下进行$modify(l,r)$操作后,cnt数组中含有非$0$元素的个数。

  给出$T$组询问,输出$\sum\limits_{i=1}^{n} \sum\limits_{j=i}^{n} f(i,j)$的值。

  对于 $100\%$的数据满足,$T\leq 10^4 , n \leq 10^{18}$

Solution :

  上来就做如此恶心的数位$DP$,不过也好,复习了一下数位$DP$。

   观察到$f(l,r)$答案的构成是$l$的二进制个数+$r$的二进制个数 - $l,r$二进制表示形成字符串的lcp的$1$的个数。

   对$n$二进制拆分,从高位到低位依次考虑,设$f[i][op1][op2][j]$当前考虑到第$i$位,当前$r$对$n$是否有限制(op1),当前$l$是否对$r$有限制(op2)。

  而$j$这一维状态,分两次$dp$考虑。

  第一次,我们要求出所有数对$(0 \leq l< r \leq n)$中两个数的二进制$1$的个数的和。

    所以,容易的设$j$为更高位$1$的数目,而整个dp的值表示方案数,总和就是方案数$\times j$的和

  第二次,我们要求出所有数对$(0 \leq l< r \leq n)$中两个数二进制串的$lcp$的$1$的个数

    所以,容易的设$j$为更高位公共$1$的个数,同时整个$dp$的值表示方案数,总和就是方案数$\times j$的和

  按照普通的数位$dp$转移即可,需要注意一些细节,这里就不再赘述一些沙雕错误了,代码用了循环展开,可读性极差。

  复杂度是$O(T {log_2}^2 n)$

# pragma GCC optimize(,"Ofast")
# include<bits/stdc++.h>
# define Rint register int
using namespace std;
const int mo=1e9+;
long long n;
int a[];
int f[][][][*];
inline void pls(int &a, int b) {
a = (a + b >= mo ? a + b - mo : a + b);
}
signed main(){
int T; scanf("%d",&T);
while (T--) {
scanf("%lld",&n);a[]=; while (n) { a[++a[]]=n&; n>>=;}
f[a[]][][][]=, f[a[]][][][]=, f[a[]][][][]=;
int HJCAK = a[] << ;
for (Rint i=a[];i>=;i--)
for (Rint op1=;op1<=;op1++){
Rint op2 = ;
for (Rint j=;j<=HJCAK;j++) {
pls(f[i-][(op1)&&!a[i-]][op2][j],f[i][op1][op2][j]);
if (op1 && a[i-]) pls(f[i-][][op2][j+],f[i][op1][op2][j]);
if (!op1) pls(f[i-][][op2][j+],f[i][op1][op2][j]);
if (!op2) pls(f[i-][op1&&!a[i-]][][j+],f[i][op1][op2][j]);
if (op1 && a[i-]) pls(f[i-][][][j+],f[i][op1][op2][j]);
if (!op1) pls(f[i-][][][j+],f[i][op1][op2][j]);
}
op2 = ;
for (Rint j=;j<=HJCAK;j++) {
pls(f[i-][(op1)&&!a[i-]][op2][j],f[i][op1][op2][j]);
if (op1 && a[i-]) pls(f[i-][][op2][j+],f[i][op1][op2][j]);
if (!op1) pls(f[i-][][op2][j+],f[i][op1][op2][j]);
if (!op2) pls(f[i-][op1&&!a[i-]][][j+],f[i][op1][op2][j]);
if (op1 && a[i-]) pls(f[i-][][][j+],f[i][op1][op2][j]);
if (!op1) pls(f[i-][][][j+],f[i][op1][op2][j]);
}
}
int res1=;
for (Rint op1=;op1<=;op1++)
for (Rint j=;j<=HJCAK;j++)
pls(res1,1ll*f[][op1][][j]*j%mo);
memset(f,,sizeof(f));
f[a[]][][][]=, f[a[]][][][]=, f[a[]][][][]=;
for (Rint i=a[];i>=;i--)
for (Rint op1=;op1<=;op1++){
Rint op2 = ;
for (Rint j=;j<=a[];j++) {
pls(f[i-][op1&&!a[i-]][op2][j],f[i][op1][op2][j]);
if (op1 && a[i-]) pls(f[i-][][op2][j+op2],f[i][op1][op2][j]);
if (!op1) pls(f[i-][][op2][j+op2],f[i][op1][op2][j]);
if (!op2) pls(f[i-][op1&&!a[i-]][][j],f[i][op1][op2][j]);
if (op1 && a[i-]) pls(f[i-][][][j],f[i][op1][op2][j]);
if (!op1) pls(f[i-][][][j],f[i][op1][op2][j]);
}
op2 = ;
for (Rint j=;j<=a[];j++) {
pls(f[i-][op1&&!a[i-]][op2][j],f[i][op1][op2][j]);
if (op1 && a[i-]) pls(f[i-][][op2][j+op2],f[i][op1][op2][j]);
if (!op1) pls(f[i-][][op2][j+op2],f[i][op1][op2][j]);
if (!op2) pls(f[i-][op1&&!a[i-]][][j],f[i][op1][op2][j]);
if (op1 && a[i-]) pls(f[i-][][][j],f[i][op1][op2][j]);
if (!op1) pls(f[i-][][][j],f[i][op1][op2][j]);
}
}
int res2=;
for (Rint op1=;op1<=;op1++)
for (Rint j=;j<=a[];j++)
pls(res2,1ll*f[][op1][][j]*j%mo);
res2 <<= ; res2 >= mo && (res2 -= mo);
printf("%d\n",(res1-res2+mo)%mo);
if(T) memset(f,,sizeof(f));
}
return ;
}

bit.cpp

Problem B 雇佣妹抖  

  给出初始$n$个数$a_i$,支持下列两种操作,共$m$个操作:

  1. 给出一个$b$,输出大于等于$b$的所有数,构成了多少个极大联通子段

  2.给出一个$p,k$,将$a_{p} = k$的单点修改。

  对于$100\%$的数据满足$1 \leq n,m \leq 2\times 10^5$

Solution :

  这道题目让我们想到了氨基酸和肽链。肽链数 $=$ 氨基酸数 $-$ 肽键数。(过于形象引起不适~)

  考虑答案是如何构成的,大于等于$b$的个数,减去相邻两个都大于等于$b$的个数。

  可以将单点和相邻数的$min$分开维护,离散化以后用树状数组或者直接写平衡树都行。

  时间复杂度为$O(mlog_2n)$

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = ;
int n, qq, B, c, d, d1, T, num;
int a[maxn], b[maxn], t[maxn], tr[maxn], q[maxn][];
int lowbit(int x) { return x & (-x); }
void add1(int x, int y) {
for (int i = x; i <= num; i += lowbit(i)) t[i] += y;
}
void add2(int x, int y) {
for (int i = x; i <= num; i += lowbit(i)) tr[i] += y;
}
int find1(int x) {
int ans = ;
for (int i = x; i >= ; i -= lowbit(i)) ans += t[i];
return ans;
}
int find2(int x) {
int ans = ;
for (int i = x; i >= ; i -= lowbit(i)) ans += tr[i];
return ans;
}
int main() {
scanf("%d%d", &n, &qq);
for (int i = ; i <= n; i++) {
scanf("%d", &a[i]);
b[i] = a[i];
}
int cnt = n;
for (int i = ; i <= qq; i++) {
scanf("%d", &T);
if (T == )
scanf("%d", &q[i][]);
else {
scanf("%d%d", &q[i][], &q[i][]);
}
b[++cnt] = q[i][];
}
sort(b + , b + cnt + );
num = unique(b + , b + cnt + ) - b - ;
for (int i = ; i <= n; i++) {
a[i] = lower_bound(b + , b + num + , a[i]) - b;
add1(a[i], );
}
for (int i = ; i < n; i++) add2(min(a[i], a[i + ]), );
for (int i = ; i <= qq; i++) {
if (q[i][] == ) {
int B1 = lower_bound(b + , b + + num, q[i][]) - b;
int ans = n - find1(B1 - ) - (n - - find2(B1 - ));
printf("%d\n", ans);
} else {
c = q[i][];
d = lower_bound(b + , b + num + , q[i][]) - b;
add1(a[c], -); if (c > )
add2(min(a[c], a[c - ]), -), add2(min(d, a[c - ]), );
if (c < n)
add2(min(a[c], a[c + ]), -), add2(min(d, a[c + ]), ); a[c] = d;
add1(a[c], );
}
}
return ;
}

maid.cpp

Problem C 建造

  有$n$幢房屋需要规划建设,高度恰好是$[1,n]$的一个排列,需要建设在$[1,X]$的数轴的整点上。

  设第$i$幢房屋位置为$pos_i$,高度为$h_i$,第$j$幢房屋的位置为$pos_j$,高度为$h_j$。

  一个合法的建设方案,需要有任意$|pos_i - pos_j| \leq max\{h_i,h_j\}$恒成立。

  两个建设方案不同,当且仅当至少有$1$个位置的建设情况不同。

  在模质数$p$的意义下输出答案。

  对于$100\%$的数据满足$1 \leq n \leq 100 , 1 \leq X \leq 10^5 , 10^8 \leq p \leq 10^9$

Solution :

  显然不能直接在数轴上直接$DP$,任意的限制只要按照排列顺序就可以转化为相邻的限制。

  设$d_1 = pos_1 - 1, d_i = pos_i - pos_{i-1} (i\geq 2)$

   于是限制可以转化为$d_i \geq max(h_i , h_{i-1})$且$\sum\limits_{i=1}^{n} d_i  + tmp = X$,

  且$d_1,...,d_n , tmp$都是非负整数。

  如果知道$S = \sum\limits_{i=2}^{n} max(h_{i-1},h_{i})$, 那么容易使用插板法求出合法答案数目$\binom{X-1-s+n}{n}$

  现在只要求出排列$n$个元素,获得$S$的方案数即可,答案就是方案数乘以组合数即可。

  设$f[i][j][k]$表示从低到高考虑到第$i$个建筑,当前已知的$S$的值至少是$j$,还有$k$个空位来插入更大的值。

  考虑定义$f[1][0][0] = 1$,刷表法求出所有答案。

  考虑将$i+1$放在已知序列的首位,考虑两种可能,

    • 首位和次位之间之间还要插入更大的数。
    • 首位和次位之后不会插入数。

  考虑将$i+1$放在已知序列的末尾,考虑两种可能。

    • 次末尾和末尾之间还要插入更大的数。
    • 次末尾和末尾值之间不会插入更大的数。

  考虑将$i+1$放在一般位置,考虑三种可能。

    • 前面和后面两个位置都会插入更大的数。
    • 前面和后面都不会插入更大的数。
    • 前面和后面其中只有1个位置会插入更大的数。

  时间复杂度就是$O(n^4+X)$,需要使用滚动数组+卡常$AC$本题。    

# pragma GCC optimize(,"Ofast")
# include <bits/stdc++.h>
# define Rint register int
# define int long long
using namespace std;
const int N=1e6+;
const int M=;
int n,m,mo;
int fac[N],inv[N],f[][M*M][M];
inline int Pow(int x,int n,int mo) {
int ans = ;
while (n) {
if (n&) ans=ans*x%mo;
x=x*x%mo;
n>>=;
}
return ans % mo;
}
inline int C(int n,int m) {
return (m>n)?():(fac[n]*inv[m]%mo*inv[n-m]%mo);
}
inline void pls(int &a, int b) {
a = (a + b >= mo ? a + b - mo : a + b);
}
signed main()
{
cin>>n>>m>>mo;
fac[]=; for (Rint i=;i<=n+m;i++) fac[i]=fac[i-]*i%mo;
inv[]=; for (Rint i=;i<=n+m;i++) inv[i]=Pow(fac[i],mo-,mo);
int p;
f[p=][][]=;
for (Rint i=;i<n;i++) {
for (Rint j=;j<=n*n;j++)
for (Rint k=;k<=i;k++) {
pls(f[p^][j][k+],f[p][j][k]);
pls(f[p^][j+i+][k],f[p][j][k]);
pls(f[p^][j][k+],f[p][j][k]);
pls(f[p^][j+i+][k],f[p][j][k]);
if (k!=) pls(f[p^][j+*(i+)][k-],f[p][j][k]*k%mo);
pls(f[p^][j][k+],f[p][j][k]*k%mo);
pls(f[p^][j+i+][k],*k*f[p][j][k]%mo);
}
memset(f[p],,sizeof(f[p]));
p^=;
}
int ans = ;
for (Rint i=;i<=n*n;i++) {
pls(ans,f[p][i][]*C(m--i+n,n)%mo);
}
cout<<ans<<'\n';
return ;
}

build.cpp

 

HGOI 20191107 题解的更多相关文章

  1. HGOI 20181028 题解

    HGOI 20181028(复赛备考) /* 真是暴力的一天,最后一题MLE?由于数组开得太大了!!! 270滚粗 考场上好像智商高了很多?!(假的) */ sol:暴力求解,然后没有数据范围吐槽一下 ...

  2. HGOI 20190310 题解

    /* 又是又双叒叕WA的一天... 我太弱鸡了... 今天上午打了4道CF */ Problem 1 meaning 给出q组询问,求下列函数的值$ f(a) = \max\limits_{0 < ...

  3. HGOI 20190303 题解

    /* 记一串数字真难. 5435 今天比赛又是hjcAK的一天. 今天开题顺序是312,在搞T1之前搞了T3 昨天某谷月赛真是毒瘤. 但是讲评的同学不错,起码T4看懂了... 构造最优状态然后DP的思 ...

  4. HGOI 20180224 题解

    /* The Most Important Things: ljc chat with fyh on QQTa说期末考Ta数学74分感觉不好但是我觉得fyh是地表最强的鸭~~(of course en ...

  5. HGOI 20190218 题解

    /* 又是AK局... hjc又双叒叕AK了... Hmmm...我侥幸 */ Problem A card 给出无序序列a[]可以选择一个数插入到合适的位置作为一次操作,至少多少次操作后可以把序列变 ...

  6. HGOI 20190217 题解

    /* for me,开训第一天 /beacuse 文化课太差被抓去补文化课了... 看一眼题 : AK局? 但是,Wa on test #10 in problem C 290! (就差那么一咪咪) ...

  7. HGOI 20181103 题解

    problem:把一个可重集分成两个互异的不为空集合,两个集合里面的数相乘的gcd为1(将集合中所有元素的质因数没有交集) solution:显然本题并不是那么容易啊!考场上想了好久.. 其实转化为上 ...

  8. HGOI 20181101题解

    /* 又是爆0的一天(不知道今年高考难不难,反正今天(信息学)真的难!) */ solution:对于两个数相加,有一个显然的结论就是要么不进位(相对于位数大的),要么(进最多一位) 然后对于整个数组 ...

  9. HGOI 20191108 题解

    Problem A 新婚快乐 一条路,被$n$个红绿灯划分成$n+1$段,从前到后一次给出每一段的长度$l_i$,每走$1$的长度需要$1$分钟. 一开始所有红绿灯都是绿色的,$g$分钟后所有红绿灯变 ...

随机推荐

  1. 整体二分(模板一)静态区间第K大

    #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #include <cstdio>//sprintf islower isupp ...

  2. Laravel 最佳实践

    单一职责原则 一个类和一个方法应该只有一个责任. 例如: public function getFullNameAttribute() { if (auth()->user() &&am ...

  3. jquery【点击】导航按钮的来回切换

    先获取元素的属性值,根据属性值进行判断,点击时对属性进行设置 <i class="layui-icon layui-icon-shrink-right" id="n ...

  4. centos7 yum安装nginx和 编译安装tengine

    说明 我这里给大家演示一下如何安装nginx,nginx我就不多介绍了,然后我再说一点就是,安装的两种方法都可以,编译安装和yum安装,我不能每个都演示两遍呀,所以看到我这博客的你,学会举一反三好吧? ...

  5. Pycharm 配置houdini

    一.houdini开发环境配置 1.添加Python可执行文件 2.设置代码自动补全 刚刚添加的Python.exe,右侧点击加号,依次添加以上长方形中的文件,路径会根据个人安装路径有所变化,后面的目 ...

  6. 【规律】A Rational Sequence

    题目描述 An infinite full binary tree labeled by positive rational numbers is defi ned by:• The label of ...

  7. HttpContext is null

    HttpContext context1 = System.Web.HttpContext.Current; HttpContext context2 = System.Runtime.Remotin ...

  8. redis常用api

    一.全局命令 1.keys *            //查看所有键 2.dbsize           //键总数,如果存在大量键,线上禁止使用此命令 3.exists key     //存在返 ...

  9. 动态对象(dynamic)的用法

    说到正确用法,那么首先应该指出一个错误用法: 常有人会拿var这个关键字来和dynamic做比较.实际上,var和dynamic完全是两个概念,根本不应该放在一起做比较.var实际上是编译期抛给我们的 ...

  10. ORM框架之EntityFramework介绍

    ORM框架之EntityFramework介绍 1. 简介 大家好!我是高堂. 作为一位伪前端程序猿,我给大家介绍一下微软的自家的 ORM框架. ADO.NET Entity Framework 以下 ...