HGOI 20191106 题解
Problem A 旅行者
有$n$种转移装置,每种转移装置本质相同,每种装置可以前进$a_i$单位,但只有$b_i$个。
从初始坐标为$0$出发,途中不能经过$c_1,c2,...,c_m$中的任意一个点。
走到$\sum\limits_{i = 1}^n a_ib_i$位置的方案数$mod 10^9 + 7$的值。
对于$100\%$的数据满足$1 \leq n \leq 6 , 1 \leq m \leq 10^5 ,0<c_i < \sum\limits_{i = 1}^n a_ib_i$
Solution :
由于每个装置本质相同,那么我们只需要记录当前使用的转移装置数作为状态即可。
这样定义状态的总状态数时$\prod_{i = 1}^n b_i \leq 13^6 = 4826809$
注意,由于有$m$点不能走,还需要开一个$hash$存当前值能不能走,特殊判掉即可。
转移的时候枚举当前通过那个转移装置走到当前位置,转移时间复杂度为$O(n)$
请注意,本题的模数为$10^8 + 7$,您是否数错了零?
所以,本题的总时间复杂度是$O(n\prod\limits_{i=1}^{n} b_i)$
# pragma GCC optimize()
# include<bits/stdc++.h>
# define int long long
# define hash Hash
# define Rint register int
using namespace std;
const int mo=;
int f[][][][][][];
struct rec{int a,b;}a[];
int n,m;
vector<int>hash[];
inline int read() {
int X=,w=; char c=;
while(c<''||c>'') {w|=c=='-';c=getchar();}
while(c>=''&&c<='') X=(X<<)+(X<<)+(c^),c=getchar();
return w?-X:X;
}
void insert(int key) {
int k = key % ,sz = hash[k].size();
for (int i=;i<sz;i++) if (hash[k][i] == key) return;
hash[k].push_back(key);
}
bool find(int key) {
int k=key % ,sz = hash[k].size();
for (int i=;i<sz;i++) if (hash[k][i] == key) return true;
return false;
}
signed main() {
n=read();
for (Rint i=;i<=n;i++) {
a[i].a=read(); a[i].b=read();
}
m=read();
for (Rint i=;i<=m;i++) {
int t=read(); insert(t);
}
f[][][][][][]=;
for (Rint a1=;a1<=a[].b;a1++) for (Rint a2=;a2<=a[].b;a2++)
for (Rint a3=;a3<=a[].b;a3++) for (Rint a4=;a4<=a[].b;a4++)
for (Rint a5=;a5<=a[].b;a5++) for (Rint a6=;a6<=a[].b;a6++) {
int tmp = a1*a[].a+a2*a[].a+a3*a[].a+a4*a[].a+a5*a[].a+a6*a[].a;
if (find(tmp)) {
f[a1][a2][a3][a4][a5][a6]=;
continue;
}
if (a1+<=a[].b) (f[a1+][a2][a3][a4][a5][a6]+=f[a1][a2][a3][a4][a5][a6])%=mo;
if (a2+<=a[].b) (f[a1][a2+][a3][a4][a5][a6]+=f[a1][a2][a3][a4][a5][a6])%=mo;
if (a3+<=a[].b) (f[a1][a2][a3+][a4][a5][a6]+=f[a1][a2][a3][a4][a5][a6])%=mo;
if (a4+<=a[].b) (f[a1][a2][a3][a4+][a5][a6]+=f[a1][a2][a3][a4][a5][a6])%=mo;
if (a5+<=a[].b) (f[a1][a2][a3][a4][a5+][a6]+=f[a1][a2][a3][a4][a5][a6])%=mo;
if (a6+<=a[].b) (f[a1][a2][a3][a4][a5][a6+]+=f[a1][a2][a3][a4][a5][a6])%=mo;
}
printf("%lld\n",f[a[].b][a[].b][a[].b][a[].b][a[].b][a[].b]%mo);
return ;
}
traveller.cpp
Problem B 序列
定义两个数$(x,y)$是好的,当且仅当$x \leq y$且$x \oplus y$二进制表示下含有奇数个$1$。
现在给出$n$个区间$[l_i,r_i]$,对于每一个$i\in[1,n]$,输出前$i$个区间并中好的数对的个数。
即输出满足下列条件的$(x,y)$的对数。
- $x,y \in\cup_{j=1}^i [l_j,r_j]$
- $x\leq y$
- $x \oplus y$二进制表示下含有奇数个$1$。
对于$100\%$的数据满足$1 \leq n \leq 10^5 , 1\leq l_i\leq r_i\leq 2^{32}-1$
Solution :
首先,$x \oplus y$的二进制表示下有奇数个$1$有充要条件:一个数有偶数个$1$,一个数有奇数个$1$ 。
设$x,y$按二进制位写开来,同$1$的数的对数为$w$,那么剩余的奇数个$1$和偶数个$1$都会对答案产生$1$的贡献。
如果我们要统计数集$S$中好的数对的个数,我们就只要数一数这个数集中有多少个数含有偶数个$1$,有多少数有奇数个一,即可。最后的答案就是他们两个之积。
如何求一个区间$[l,r]$内有多少个数含有奇数个$1$或者偶数个$1$呢。
我们只要求前缀和即可,问题转化为求$[1,x]$的答案。
观察到一对数$0,1 ; 2, 3 ; 4,5 ... $从$0$开始每两个数一组一定是一个数字含有奇数个$1$,另外一个数字含有偶数个$1$.
所以,若$x$是奇数,$[1,x]$的答案直接是$x/2$了; 否则还需要特殊判断$x$这个数字到底是含有奇数个$1$,另外一个数字含有偶数个$1$.
接下来的问题就转化为线段的并了,我们可以很方便的用主席树来解决。
对值域建动态开点的线段树,当前区间如果被覆盖了直接打上一个$vis$标记,下一次不覆盖当前区间即可。
设数的值域是$S$,那么这样做的时间复杂度是$O(n log_2S)$
# pragma GCC optimize()
# include <bits/stdc++.h>
# define int long long
# define lowbit(x) (x&(-x))
const int N=1e5+;
using namespace std;
int n,tot;
inline int read() {
int X=,w=; char c=;
while(c<''||c>'') {w|=c=='-';c=getchar();}
while(c>=''&&c<='') X=(X<<)+(X<<)+(c^),c=getchar();
return w?-X:X;
}
void write(int x) {
if (x<) x=-x,putchar('-');
if (x>) write(x/);
putchar(''+x%);
}
int query(int x) {
if (x&) return x/+;
int ret=,res=x/;
while (x) { x-=lowbit(x); ret^=;}
return res+ret;
}
bool vis[N*];
struct Seg {
int ls,rs,cnt1,cnt2;
}tr[N*];
# define ls tr[x].ls
# define rs tr[x].rs
void update(int &x,int l,int r,int opl,int opr) {
if (!x) x=++tot; if (vis[x]) return;
if (opl<=l && r<=opr) {
vis[x]=;
tr[x].cnt1=query(r)-query(l-);
tr[x].cnt2=r-l+-tr[x].cnt1;
return;
}
int mid=(l+r)>>;
if (opl<=mid) update(ls,l,mid,opl,opr);
if (opr>mid) update(rs,mid+,r,opl,opr);
tr[x].cnt1=tr[ls].cnt1+tr[rs].cnt1;
tr[x].cnt2=tr[ls].cnt2+tr[rs].cnt2;
}
signed main() {
n=read();
int root=;
for (int i=;i<=n;i++) {
int l=read(),r=read();
update(root,,(1ll<<)-,l,r);
write(tr[].cnt1*tr[].cnt2);
putchar('\n');
}
return ;
}
sequence.cpp
Problem C 钢琴家
给出一个基本字符串$S$,给出$n$个目标字符串集合$t_i$。
要求修改一些$S_i$使得可以将$S$划分成一些子串,使得这些子串都在目标字符串集合中出现。
让替换次数尽可能小,且保证存在至少一个最优解。
对于$100\%$的数据,满足 $1 \leq |t_i| \leq |S|\leq 10^3 , 1\leq n\leq 10^2$
Solution :
本题可以直接用$DP$求出最优策略,设$f[i]$表示匹配到长度$i$的最小代价。
枚举一个目标字符串$1 \leq j \leq n$,可以考虑$[i-|t_j|+1 , i]$用串$t_j$来修改。
然后计算这样做的代价,就是$\sum\limits_{k = 1}^{|t_j|} [\ S[i-|t_j|+k]\neq t_j[k]\ ]$
记录每一次最优的转移是从何而来,这样可以输出方案。
最后直接按照最优方案输出即可。
时间复杂度为$O(n|S|^2)$
# pragma GCC optimize()
# include <bits/stdc++.h>
using namespace std;
const int N=1e3+;
char t[N][N],s[N];
int n,f[N],len[N];
int pre[N];
int ans[N];
int main() {
scanf("%s",s+);int l=strlen(s+);
scanf("%d",&n);
for (int i=;i<=n;i++) {
scanf("%s",t[i]+);
len[i]=strlen(t[i]+);
}
memset(f,0x3f,sizeof(f)); f[]=;
for (int i=;i<=l;i++)
for (int j=;j<=n;j++) if (i>=len[j]){
int res = ;
for (int k=;k<=len[j];k++) {
if (t[j][k]!=s[i-len[j]+k]) res++;
}
if (f[i-len[j]]+res<f[i]) {
f[i] = f[i-len[j]]+res;
pre[i] = j;
}
}
int now = l;
while (true) {
if (now == ) break;
ans[++ans[]] = pre[now];
now=now-len[pre[now]];
}
for (int i=ans[];i>=;i--) {
for (int j=;j<=len[ans[i]];j++)
putchar(t[ans[i]][j]);
putchar('\n');
}
return ;
}
pianist.cpp
HGOI 20191106 题解的更多相关文章
- HGOI 20191106
HGOI 20191106 t1 旅行家(traveller) 2s,256MB [题目背景] 小X热爱旅行,他梦想有一天可以环游全世界-- [题目描述] 现在小X拥有n种一次性空间转移装置,每种装置 ...
- HGOI 20181028 题解
HGOI 20181028(复赛备考) /* 真是暴力的一天,最后一题MLE?由于数组开得太大了!!! 270滚粗 考场上好像智商高了很多?!(假的) */ sol:暴力求解,然后没有数据范围吐槽一下 ...
- HGOI 20190310 题解
/* 又是又双叒叕WA的一天... 我太弱鸡了... 今天上午打了4道CF */ Problem 1 meaning 给出q组询问,求下列函数的值$ f(a) = \max\limits_{0 < ...
- HGOI 20190303 题解
/* 记一串数字真难. 5435 今天比赛又是hjcAK的一天. 今天开题顺序是312,在搞T1之前搞了T3 昨天某谷月赛真是毒瘤. 但是讲评的同学不错,起码T4看懂了... 构造最优状态然后DP的思 ...
- HGOI 20180224 题解
/* The Most Important Things: ljc chat with fyh on QQTa说期末考Ta数学74分感觉不好但是我觉得fyh是地表最强的鸭~~(of course en ...
- HGOI 20190218 题解
/* 又是AK局... hjc又双叒叕AK了... Hmmm...我侥幸 */ Problem A card 给出无序序列a[]可以选择一个数插入到合适的位置作为一次操作,至少多少次操作后可以把序列变 ...
- HGOI 20190217 题解
/* for me,开训第一天 /beacuse 文化课太差被抓去补文化课了... 看一眼题 : AK局? 但是,Wa on test #10 in problem C 290! (就差那么一咪咪) ...
- HGOI 20181103 题解
problem:把一个可重集分成两个互异的不为空集合,两个集合里面的数相乘的gcd为1(将集合中所有元素的质因数没有交集) solution:显然本题并不是那么容易啊!考场上想了好久.. 其实转化为上 ...
- HGOI 20181101题解
/* 又是爆0的一天(不知道今年高考难不难,反正今天(信息学)真的难!) */ solution:对于两个数相加,有一个显然的结论就是要么不进位(相对于位数大的),要么(进最多一位) 然后对于整个数组 ...
随机推荐
- windows下memcache扩展安装和搭建
### windows下memcache扩展安装和搭建 背景:在做微信公众号的开发时,token的有效期为7200秒,所以需要对token进行保存,在这选择了memcache作为缓存工具 memcac ...
- adb shell dumpsys [options]的使用
adb shell dumpsys [options]该命令用于打印出当前系统信息,默认打印出设备中所有service的信息.由于service比较多,这里选几个用的比较多的service来进行讲解: ...
- python — 函数基础知识(一)
目录 1 面向过程编程与函数式编程 2 函数的基本结构 3 函数的参数 1 面向过程编程与函数式编程 截至目前我们所接触.所写的编程为:面向过程式编程[可读性差/可重用性差] # 面向过程编程 use ...
- gdb暂停或恢复程序的运行
ref : https://blog.csdn.net/seu_lyr/article/details/9050657 一 暂停程序的运行: (一)GDB的暂停方式:断点(BreakPoint). ...
- hdu 2821 学习一点dfs的小技巧吧。。 还是自己太弱了
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int r,c ...
- Java写学生管理系统
package Homework08;/*调试了一上午,收获:学会了昨天的debug的使用吸取教训:Student stus[]=new Student[2]; for (int i=0;i<s ...
- upxmake --- upx source compilation
upxmake --- upx source compilation 1. 下载upx所依赖的组件源码 zlib-1.2 http://www.zlib.net/zlib-1.2.11.tar.gz ...
- CN丶Moti-个人博客
欢迎访问我的个人博客,获取更多有用的东西 链接一 链接二 也可以关注我的微信订阅号:CN丶Moti
- 【php设计模式】门面模式
门面模式又叫外观模式,用来隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口.这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性. 这种模式涉及到一个单一的类 ...
- jQuery实现购物车效果
简单的购物车效果 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...