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. Escape(多记一个方向状态的BFS)迷宫逃脱

    题意:https://www.nitacm.com/problem_show.php?pid=2266 vis记[x][y][dir]三个状态就行. 引用:https://blog.csdn.net/ ...

  2. LeetCode 答案(python)1-17

    1.给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 示例: 给定 nums = [2, 7, 11, 15], targe ...

  3. 【背包问题】PACKING

    题目描述 It was bound to happen.  Modernisation has reached the North Pole.  Faced with escalating costs ...

  4. ingress之tls和path使用

    ingress tls 上节课给大家展示了 traefik 的安装使用以及简单的 ingress 的配置方法,这节课我们来学习一下 ingress tls 以及 path 路径在 ingress 对象 ...

  5. KeyValuePair<string, string>

    ; #region CUP Method /// <summary> /// 请求与响应的超时时间 /// </summary> static public int Timeo ...

  6. hdu 4826 三维dp

    dp的问题除了递推过程的设计之外 还有数据结构的选择以及怎样合理的填充数据 这个的填充是个坑..#include<iostream> #include<cstdio> #inc ...

  7. MySQL去除查询结果重复值

    下面先来看看例子: table  id name  1 a  2 b  3 c  4 c  5 b 库结构大概这样,这只是一个简单的例子,实际情况会复杂得多. 比如我想用一条语句查询得到name不重复 ...

  8. springboot-异步、发送邮件(二)

    @Test //发送复杂邮件 void contextLoads2() throws MessagingException { MimeMessage mimeMessage = mailSender ...

  9. YOLO 学习之路

    参考自官网  https://pjreddie.com/darknet/install/ 1. 下载darknet  并编译 git clone https://github.com/pjreddie ...

  10. react portals 插槽 实现简易弹窗

    Portal 提供了一种将子节点渲染到存在于父节点以外的DOM节点的优秀方案: 尽管 portal 可以被放置在 DOM 树中的任何地方,但在任何其他方面,其行为和普通的 React 子节点行为一致. ...