【HMOI】小C的填数游戏 DP+线段树维护
【题目描述】
一个长为n的序列,每个元素有一个a[i],b[i],a[i]为0||1,每个点和他相邻的两个点分别有两条边,权值为cost1[i],cost2[i],对于每个区间l,r,我们可以给每一个数一个c[i]值,c[i]=0||1,使得Σ(b[j]*(a[j]^c[j]))+cost1[j]*(c[j]^c[j+1])+cost2[j]*(c[j]^c[j+2]),j,j+1,j+2在[l,r]内时累加,现在有m次操作:
M x:将x位置的a[i]^=1;
Q l r:询问区间l,r的答案。
首先假设询问的只是一个区间,那么我们可以比较容易的用dp来求解w[i][s]表示到第i位的时候,后两位选的s的最小值,那么我们可以转移到w[i+1][ss]。这样就可以得到答案了。
但是现在我们支持区间询问和修改,所以我们用线段树来维护,每个节点记录w[s],s为这个区间左面两个点c[i]的选取方法和右面两个点的选取方法,那么只要我们可以维护线段树每个节点的w[i],我们就可以解决这个问题了。
对于合并操作比较简单,只需要枚举两个节点的s,然后更新的ss就可以了。
反思:一个节点的情况需要特殊处理,开始没注意到这个,后来改了半天,合并的时候还分了三种情况讨论= =。
还需要开LL,设inf的时候设的是#define inf (1<<60) 结果改了半天= =
评测数据的5号评测点少了一个操作,然后我的输出就多了一行= =。
//By BLADEVIL
#include <cstdio>
#include <cstring>
#define min(x,y) (x>y)?y:x;
#define inf (1ll<<60)
#define maxn 30010
#define LL long long using namespace std; struct rec {
int left,right;
LL key;
LL w[];
rec() {
left=right=;
key=inf;
for (LL i=;i<;i++) w[i]=inf;
}
}t[maxn<<]; LL n,m;
LL a[maxn],b[maxn],cost1[maxn],cost2[maxn];
char s[]; void init(int x) {
t[x].w[]=b[t[x].left]*a[t[x].left];
t[x].w[]=b[t[x].left]*(a[t[x].left]^);
t[x].key=min(t[x].w[],t[x].w[]);
} rec update(rec a,rec b) {
rec ans;
ans.left=a.left; ans.right=b.right;
if ((a.left==a.right)&&(b.left==b.right))
for (LL s=;s<;s++) {
LL ss=s|(s<<);
LL c1=((s&)==)?:,c2=((s&)==)?:;
//if ((ss==3)&&(a.left==1)) printf("%d %d\n",c1,c2);
if ((a.w[c1]==inf)||(b.w[c2]==inf)) continue;
ans.w[ss]=min(ans.w[ss],a.w[c1]+b.w[c2]+(c1^c2)*cost1[a.right]);
//if ((ss==3)&&(a.left==1)&&(a.right==1)) printf("%d\n",ans.w[ss]);
ans.key=min(ans.key,ans.w[ss]);
//if ((a.left==1)&&(a.right==1)) printf("%d %d\n",ans.key,ss);
} else
if (a.left==a.right)
for (LL s1=;s1<;s1++)
for (LL s2=;s2<;s2++) {
if ((a.w[s1]==inf)||(b.w[s2]==inf)) continue;
LL s=;
s|=s1<<; s|=(s2&)>>; s|=s2&;
LL c2=((s2&)==)?:,c3=((s2&)==)?:;
ans.w[s]=min(ans.w[s],a.w[s1]+b.w[s2]+(s1^c2)*cost1[a.left]+(s1^c3)*cost2[a.left]);
ans.key=min(ans.key,ans.w[s]);
} else
if (b.left==b.right)
for (LL s1=;s1<;s1++)
for (LL s2=;s2<;s2++) {
if ((a.w[s1]==inf)||(b.w[s2]==inf)) continue;
LL s=;
s|=s2; s|=(s1&)<<; s|=s1&;
LL c1=((s1&)==)?:,c2=((s1&)==)?:;
ans.w[s]=min(ans.w[s],a.w[s1]+b.w[s2]+(c1^s2)*cost2[a.right-]+(c2^s2)*cost1[a.right]);
ans.key=min(ans.key,ans.w[s]);
} else
if ((a.left!=a.right)&&(b.left!=b.right))
for (LL s1=;s1<;s1++)
for (LL s2=;s2<;s2++) {
if ((a.w[s1]==inf)||(b.w[s2])==inf) continue;
LL s=(s1>>)<<;
s|=s2&;
LL c1=((s1&)==)?:,c2=((s1&)==)?:;
LL c3=((s2&)==)?:,c4=((s2&)==)?:;
LL Ans=a.w[s1]+b.w[s2];
Ans+=cost1[a.right]*(c2^c3)+cost2[a.right]*(c2^c4)+cost2[a.right-]*(c1^c3);
ans.w[s]=min(ans.w[s],Ans);
ans.key=min(ans.key,ans.w[s]);
}
return ans;
} void build(int x,int l,int r) {
t[x].left=l; t[x].right=r;
if (t[x].left==t[x].right) {
init(x);
return ;
}
int mid=l+r>>;
build(x<<,l,mid); build((x<<)+,mid+,r);
t[x]=update(t[x<<],t[(x<<)+]);
} rec query(int x,int l,int r) {
if ((t[x].left==l)&&(t[x].right==r)) return t[x];
int mid=t[x].left+t[x].right>>;
if (mid>=r) return query(x<<,l,r); else
if (mid<l) return query((x<<)+,l,r); else
return update(query(x<<,l,mid),query((x<<)+,mid+,r));
} void change(int x,int y) {
if (t[x].left==t[x].right) {
init(x);
return ;
}
LL mid=t[x].left+t[x].right>>;
if (y>mid) change((x<<)+,y); else change(x<<,y);
t[x]=update(t[x<<],t[(x<<)+]);
} int main() {
freopen("game.in","r",stdin); freopen("game.out","w",stdout);
scanf("%lld%lld",&n,&m);
for (LL i=;i<=n;i++) scanf("%lld",&a[i]);
for (LL i=;i<=n;i++) scanf("%lld",&b[i]);
for (LL i=;i<n;i++) scanf("%lld",&cost1[i]);
for (LL i=;i<n-;i++) scanf("%lld",&cost2[i]);
build(,,n);
/*
for (LL s=0;s<16;s++) printf("%d %d\n",s,query(1,1,3).w[s]);
return 0;
*/
//printf("%d\n",query(1,2,4).key); return 0;
while (m--) {
scanf("%s",s);
LL x,y;
if (s[]=='Q') {
scanf("%lld%lld",&x,&y);
printf("%lld\n",query(,x,y).key);
} else {
scanf("%lld",&x);
a[x]^=;
change(,x);
}
}
//for (LL s=0;s<16;s++) printf("%d %d\n",s,query(1,1,4).w[s]);
fclose(stdin); fclose(stdout);
return ;
}
【HMOI】小C的填数游戏 DP+线段树维护的更多相关文章
- 小C的填数游戏
题意: 给出一张n个点的无向图 i连向i-1和i-2 边权为wij 有两个点权ai和bi ai为0或1 在给m个操作 1.将ai异或1 2.将区间x到y的点都填上一个数ci 使得Σ(bi*(ai^ci ...
- Codeforces 834D The Bakery【dp+线段树维护+lazy】
D. The Bakery time limit per test:2.5 seconds memory limit per test:256 megabytes input:standard inp ...
- [动态dp]线段树维护转移矩阵
背景:czy上课讲了新知识,从未见到过,总结一下. 所谓动态dp,是在动态规划的基础上,需要维护一些修改操作的算法. 这类题目分为如下三个步骤:(都是对于常系数齐次递推问题) 1先不考虑修改,不考虑区 ...
- Subsequence Count 2017ccpc网络赛 1006 dp+线段树维护矩阵
Problem Description Given a binary string S[1,...,N] (i.e. a sequence of 0's and 1's), and Q queries ...
- bzoj 1594: [Usaco2008 Jan]猜数游戏——二分+线段树
Description 为了提高自己低得可怜的智商,奶牛们设计了一个新的猜数游戏,来锻炼她们的逻辑推理能力. 游戏开始前,一头指定的奶牛会在牛棚后面摆N(1 <= N<= 1,000,00 ...
- 【bzoj1594-猜数游戏】线段树
题解: 矛盾只有两种情况: 一.先前确定了x在区间(l,r),但是现在发现x在区间(l1,r1),并且两个区间不相交. 二.一个区间的最小值是x,这个区间中有一个子区间的最小值比x更小. 首先可以明确 ...
- DP+线段树维护矩阵(2019牛客暑期多校训练营(第二场))--MAZE
题意:https://ac.nowcoder.com/acm/contest/882/E 给你01矩阵,有两种操作:1是把一个位置0变1.1变0,2是问你从第一行i开始,到最后一行j有几种走法.你只能 ...
- Codeforces 833B 题解(DP+线段树)
题面 传送门:http://codeforces.com/problemset/problem/833/B B. The Bakery time limit per test2.5 seconds m ...
- @NOIP2018 - D2T2@ 填数游戏
目录 @题目描述@ @题解@ @代码@ @题目描述@ 小 D 特别喜欢玩游戏.这一天,他在玩一款填数游戏. 这个填数游戏的棋盘是一个 n×m 的矩形表格.玩家需要在表格的每个格子中填入一个数字(数字 ...
随机推荐
- 选择正确的C/C++ runtime library
本文是对http://www.davidlenihan.com/2008/01/choosing_the_correct_cc_runtim.html的翻译,如有错误,还请指正 c/c++运行库(ru ...
- 使用 Python 操作 Git 版本库 - GitPython
GitPython 是一个用于操作 Git 版本库的 python 包, 它提供了一系列的对象模型(库 - Repo.树 - Tree.提交 - Commit等) 用于操作版本库中的相应对象. 版本库 ...
- input 元素 相对父元素错位
<div class="recommend"> <i class="iconfont icon-user"></i> < ...
- hdu3625-Rooms
题目 有\(n\)个房间,\(n\)个钥匙,每个钥匙随机出现在一个房间里,一个房间里有且仅有一个钥匙.我们现在手上没有钥匙,但我们要搜索所有的房间,所以我们有\(k\)次机会把一个房间炸开.一号房间里 ...
- BZOJ4565 HAOI2016字符合并(区间dp+状压dp)
设f[i][j][k]为将i~j的字符最终合并成k的答案.转移时只考虑最后一个字符是由哪段后缀合成的.如果最后合成为一个字符特殊转移一下. 复杂度看起来是O(n32k),实际常数极小达到O(玄学). ...
- BZOJ4245 ONTAK2015 OR-XOR(贪心)
贪心的按位考虑.如果所有数在某一位上有奇数个为1,显然无论如何划分这一位最终都会为1:否则将每一部分都划分为偶数个1就能保证最终该位为0,可以标记上哪些位置可以作为划分点(当然也要满足之前可为0的位上 ...
- hdu 3339 In Action (最短路径+01背包)
In Action Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total S ...
- [洛谷P1452]Beauty Contest
题目大意:给你$n$个点,求出其中最远点的距离 题解:求出凸包,最远点一定都在凸包上,可以对每条边求出最远的点(可以双指针),然后求出和这条边的端点的距离,更新答案 卡点:最开始对每个点求出最远点,但 ...
- POJ3261:Milk Patterns——题解
http://poj.org/problem?id=3261 给一个序列,求至少出现 k 次的最长重复子串,这 k 个子串可以重叠. 论文题+傻逼题. 上一道题(POJ1743)会做即可. 还是二分长 ...
- BZOJ4943 & 洛谷3823 & UOJ315:[NOI2017]蚯蚓排队——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4943 http://uoj.ac/problem/315 https://www.luogu.or ...