【题目背景】

岁岁花藻檐下共将棠梨煎雪。

自总角至你我某日辗转天边。

天淡天青,宿雨沾襟。

一年一会信笺却只见寥寥数言。

——银临《棠梨煎雪》

【问题描述】

扶苏正在听《棠梨煎雪》的时候,山东省实验中学的 zzh 发来一道IOI2018集训队 互测题:我切了这集训队题,辣鸡扶苏快过来做题。扶苏定睛一看,这不 s* 题嘛,写 了一发交上去才发现自己看错题目了。但是写完的代码不能浪费,于是就有了这道题。

歌词中的主人公与她的朋友一年会有一次互相写信给对方,一共通信了 m 年。为了 简化问题,我们认为他们每封信的内容都是一条二进制码,并且所有二进制码的长度都 是 n。即每封信的内容都是一个长度为 n 的字符串,这个字符串只含字符 0 或 1。

这天她拿出了朋友写给她的所有信件,其中第 i 年的写的信件编号为 i。由于信件 保存时间过久,上面有一些字符已经模糊不清,我们将这样的位置记为 ?,? 字符可以 被解释为 0 或 1。由于她的朋友也是人,符合人类的本质,所以朋友在一段连续的时间 中书写的内容可能是相同的。现在她想问问你,对于一段连续的年份区间 [l,r] 中的所 有信件,假如朋友在这段时间展示了人类的本质,所写的是同一句话,那么这一句话一 共有多少种可能的组成。也即一共有多少字符串 S,满足在这个区间内的所有信件的内 容都可能是 S。

一个长度为 n 的只含 0,1,? 的字符串 A 可能是一个字符串 B 当且仅当 B 满足如 下条件:

  1. B 的长度也是 n
  2. B 中只含字符 0,1
  3. A 中所有为 0 的位置在 B 中也是 0
  4. A 中所有为 1 的位置在 B 中也是 1
  5. A 中为 ? 的位置在 B 中可以为 0 也可以是 1

同时她可能会突然发现看错了某年的信的内容,于是他可能会把某一年的信的内容 修改为一个别的只含 0,1,? 的长度为 n 的字符串。

【输入格式】

输入文件名为 lin.in。

输入文件中有且仅有一组数据,第一行为三个正整数 n,m,q,代表字符串长度,字 符串个数和操作次数。

下面 m 行,每行是一个长度为 n 的字符串,第 i 个字符串代表第 i 年信的内 容。

下面 q 行,每行的第一个数字是操作编号 opt。

如果 opt=0,那么后面接两个整数 [l,r],代表一次查询操作

如果 opt=1,那么后面接一个整数 pos,在一个空格后会有一个长度为 n 的字符 串,代表将第 pos 个字符串修改为新的字符串。

【输出格式】

输出文件名为 lin.out。

对于每个查询操作,输出一行一个整数代表答案。


SOLUTION:

子任务 1: 0 次询问,直接 freopen 即可得分,期望得分 5 分。

子任务 2: 考虑在询问的时候枚举所有可能出现的串,然后遍历区间所有的字符串看看是否 合法。由于字符串长度为 n,且每个位置只可能是 0/1,所以一共有 2 n 中情况。时间 复杂度 O(2 nmq),期望得分 10 分。

子任务 3: 考虑对于一段区间的所有字符串的第 x 个字符,一共有四种可能,确定为 0,确 定为 1,都可以,都不可以。如果都不可以则输出0,如果确定为 0 或 1,则这一位 只有一种可能,否则有两种可能,根据乘法原理,如果有 a 个位置都可以,则会有 2 a 种可能。输出答案即可。时间复杂地 O(nmq),期望得分 15 分。

(感觉我的代码就是介个思路呀,但是我肿么没有分呢??又玄学啦(改了几行吧,就对了可海星))

(啊经过一晚上不懈努力,我终于知道我错哪了:1.cnt1没有memset,然后l==r的特判也不到家(所以emm对自己无语惹))

45pts(数据水的说qwq)

#include<bits/stdc++.h>

using namespace std;

inline int read(){
int ans=;
char last=' ',ch=getchar();
while(ch<''||ch>'') last=ch,ch=getchar();
while(ch>=''&&ch<='') ans=ans*+ch-'',ch=getchar();
if(last=='-') ans=-ans;
return ans;
} int n,m,q,opt,l,r;
char s[][]; int cx(int l,int r){
long long cnt1;
int ans=;
int qj=r-l+;
for(int i=;i<=n;i++){
char d=' ';
cnt1=;
for(int j=l;j<=r;j++){
if(s[j][i]=='?'){
cnt1++;
continue;
}
if(d==' ') d=s[j][i];
if(d==s[j][i]) continue;
else return ;
}
if(cnt1==qj) ans*=;
}
return ans;
} int main(){
n=read();m=read();q=read();
if(m==)
return ;
for(int i=;i<=m;i++)
scanf("%s",s[i]+);
for(int i=;i<=q;i++){
opt=read();
if(opt==){
l=read();r=read();
cout<<cx(l,r)<<"\n";
}
else {
char ss[];
int f=read();
scanf("%s",ss+);
for(int i=;i<=n;i++){
s[f][i]=ss[i];
}
}
} return ;
}

45pts Code

#include<bits/stdc++.h>

using namespace std;

inline int read(){
int ans=;
char last=' ',ch=getchar();
while(ch<''||ch>'') last=ch,ch=getchar();
while(ch>=''&&ch<='') ans=ans*+ch-'',ch=getchar();
if(last=='-') ans=-ans;
return ans;
} int n,m,q,opt,l,r;
char s[][]; int cx(int l,int r){
int cnt1[];
memset(cnt1,,sizeof(cnt1));
if(l==r) {
int cnt=;
for(int i=;i<=n;i++)
if(s[l][i]=='?') cnt*=;
return cnt;
}
int k=l;
for(int i=;i<=n;i++){
char d='x';
for(int j=l;j<=r;j++){
if(s[j][i]=='?'){
cnt1[i]++;
continue;
}
if(d=='x') d=s[j][i];
if(d==s[j][i]) continue;
else return ;
}
}
int ans=;
int qj=r-l+;
for(int i=;i<=n;i++){
if(cnt1[i]==qj) ans*=;
else ans*=;
}
return ans;
} int main(){
freopen("lin.in","r",stdin);
freopen("lin.out","w",stdout);
n=read();m=read();q=read();
if(m==)
return ;
for(int i=;i<=m;i++)
scanf("%s",s[i]+);
for(int i=;i<=q;i++){
opt=read();
if(opt==){
l=read();r=read();
cout<<cx(l,r)<<"\n";
}
else {
char ss[];
int f=read();
scanf("%s",ss+);
for(int i=;i<=n;i++){
s[f][i]=ss[i];
}
}
} return ;
}

最少改动的45pts Code

好了接下来是正解做法了(我不保证我能看懂因为zay太爱用指针啦)

的一部分(突然发现还有好多子任务的说)

子任务 4:

考虑 n 只有 30,可以状压到 int 中,具体的,维护两个 int,第一个 int 维 护对应位是否确定为 0 或 1,第二个 int 维护如果确定为 0 或 1 了则具体是 0 还 是 1。通过位运算操作一下可以快速的该区间内的信息。

具体的,考虑 a1,a2 是从 L 到某个位置左侧的区间信息,b1,b2 是该位置的区 间信息,如果该询问没有能匹配的字符串,则 (a1|b1)&(a2^b2) 应该不为 0,输出 0 即可。否则 a1=a1|b1;a2=(a1|b1)&(a2&b2)。

验题人的神仙做法是维护一个 longlong,两个二进制位一组,01 代表确定为 0,10代表确定为 1,11 代表都可以,00 代表不合法,维护的时候直接按位与即可。 时间复杂度 O(mq),期望得分 15 分。

子任务 5:

考虑 n 只有 1,可以开一个线段树维护每个位置的字符是什么,pushup的时候操 作一下即可。时间复杂度 O(q logm),期望得分 15 分。

子任务 6:

世界上没有什么事情是开一棵线段树不能解决的。如果有,那就开 30 棵。时间 复杂度 O(nq logm),期望得分 10 分。

子任务 7:

考虑用一棵线段树维护子任务 4 中的状压信息,通过位运算可以把 n 省掉,于 是总复杂度 O(nq + qlogm),期望得分 30 分。

#include <cstdio>

template <typename T>
inline void qr(T &x) {
char ch = getchar(), lst = ' ';
while ((ch > '') || (ch < '')) lst = ch, ch=getchar();
while ((ch >= '') && (ch <= '')) x = (x << ) + (x << ) + (ch ^ ), ch = getchar();
if (lst == '-') x = -x;
} const int maxn = ; int n, m, q;
char s[maxn][]; #ifdef ONLINE_JUDGE
int Ans;
#endif struct Tree {
Tree *ls, *rs;
int l, r, x/*int1*/, y/*int2*/;
bool leg;//判断 Tree() {
ls = rs = NULL;
l = r = x = y = ;
leg = true;
} void pushup() {
if (!(this->ls->leg && this->rs->leg)) {//左右子树都不合法
this->leg = false;
} else {
if ((this->ls->x & this->rs->x) & (this->ls->y ^ this->rs->y)) {
//只有当左右儿子对应位置是否全为1或0都相同并且左右儿子对应的y不相同时,显然不合法
this->leg = false;
} else {
this->leg = true;
this->x = this->ls->x | this->rs->x;
this->y = this->ls->y | this->rs->y;
}
}
}
};
Tree *rot; void ReadStr(char *p);
void Update(const int x);
void Query(const int l, const int r);
void update(Tree *const u, const int p);
Tree query(Tree *u, const int l, const int r);
void build(Tree *const u, const int l, const int r); int main() {
freopen("lin.in", "r", stdin);
freopen("lin.out", "w", stdout);
qr(n); qr(m); qr(q);
for (int i = ; i <= m; ++i) {
ReadStr(s[i] + );//读入一串字符串的神仙操作
}
build(rot = new Tree, , m);
int opt, l, r;
while (q--) {
opt = ; qr(opt);
if (opt == ) {
l = r = ; qr(l); qr(r);
Query(l, r);
} else {
l = ; qr(l);
ReadStr(s[] + );
Update(l);
}
}
#ifdef ONLINE_JUDGE
printf("%d\n", Ans);
#endif
return ;
} void ReadStr(char *p) {
do *p = getchar(); while ((*p != '') && (*p != '') && (*p != '?'));
do *(++p) = getchar(); while ((*p == '') || (*p == '') || (*p == '?'));
*p = ;
} void build(Tree *const u, const int l, const int r) {
if ((u->l = l) == (u->r = r)) {//叶节点
for (int i = ; i <= n; ++i) {//直接扫描记录即可
if (s[l][i] != '?') {
u->x |= << i;
if (s[l][i] == '') {
u->y |= << i;
}
}
}
} else {//不是叶节点,递归建树;
int mid = (l + r) >> ;
build(u->ls = new Tree, l, mid);
build(u->rs = new Tree, mid + , r);
u->pushup();
}
} Tree query(Tree *u, const int l, const int r) {
if ((u->l > r) || (u->r < l)) return Tree();
if ((u->l >= l) && (u->r <= r)) return *u;
Tree _ret;
auto ll = query(u->ls, l, r), rr = query(u->rs, l, r);
_ret.ls = &ll; _ret.rs = &rr;
_ret.pushup();
return _ret;
} void Query(const int l, const int r) {
auto _ret = query(rot, l, r);
if (!_ret.leg) {
#ifndef ONLINE_JUDGE
puts("");
#endif
} else {
int ans = ;
for (int i = ; i <= n; ++i) if (!(_ret.x & ( << i))) {
ans <<= ;
}
#ifdef ONLINE_JUDGE
Ans ^= ans;
#else
printf("%d\n", ans);
#endif
}
} void update(Tree *u, const int p) {
if (u->ls) {
if (u->ls->r >= p) {
update(u->ls, p);
} else {
update(u->rs, p);
}
u->pushup();
} else {
*u = Tree();
u->l = u->r = p;
for (int i = ; i <= n; ++i) {
if (s[][i] != '?') {
u->x |= << i;
if (s[][i] == '') {
u->y |= << i;
}
}
}
}
} void Update(const int x) {
update(rot, x);
}

我尽力了,但我真的没有全看懂

end-

【6.24校内test】T3 棠梨煎雪的更多相关文章

  1. 【线段树】【P5522】[yLOI2019] 棠梨煎雪

    C [yLOI2019] 棠梨煎雪 Background 岁岁花藻檐下共将棠梨煎雪 自总角至你我某日辗转天边 天淡天青 宿雨沾襟 一年一会信笺却只见寥寥数言 --银临<棠梨煎雪> Desc ...

  2. P5522 [yLOI2019] 棠梨煎雪

    updata on 2020.3.19 今天把博客从洛谷往博客园搬,图炸了 其实早就发现了,懒得管 那图其实就是一个用dev自带的调试功能调试时,RE了的报错 当时觉得很奇怪看不出是啥,现在再看已经觉 ...

  3. 洛谷P5522 【[yLOI2019] 棠梨煎雪】

    区间操作考虑用线段树维护. 建\(n*2\)棵线段树,前\(n\)棵线段树维护每个串的第i位是否是0. 后\(n\)棵线段树维护每个串的第i位是否是1. 如果是问号的话,直接跳过就好(通过1和0能看出 ...

  4. 6.25考试整理:江城唱晚&&不老梦&&棠梨煎雪——题解

    按照旧例,先安利一下主要作者:一扶苏一 以及扶苏一直挂念的——银临姐姐:银临_百度百科 (滑稽) 好哒,现在步入正题: 先看第一题: 题解: 在NOIP范围内,看到“求方案数”,就说明这个题是一个计数 ...

  5. 【7.24校内交流赛】T3【qbxt】复读警告

    数据范围:N,key<=1000; 首先看题目背景,显然不是DP就是图论,但是这显然不是个图论,因此这就是个DP: 接下来考虑怎么DP 我们定义dp[i][j]表示现在dp到了第i个数,当前i个 ...

  6. [3.24校内训练赛by hzwer]

    来自FallDream的博客,未经允许,请勿转载,谢谢. ----------------------------------------------------------------------- ...

  7. 【6.24校内test】T1 江城唱晚

    [题目背景] 墙角那株海棠,是你种下的思念. 生死不能忘,高烛照容颜. 一曲江城唱晚,重忆当年坐灯前, 青衫中绣着你留下的线. ——银临<江城唱晚> [问题描述] 扶苏是个喜欢一边听古风歌 ...

  8. 18清明校内测试T3

    扫雷(mine) Time Limit:1000ms   Memory Limit:128MB 题目描述 rsy最近沉迷于一款叫扫雷的游戏. 这个游戏是这样的.一开始网格上有n*m个位置,其中有一些位 ...

  9. 2019.7.9 校内测试 T3 15数码问题

    这一次是交流测试?边交流边测试(滑稽 15数码问题 大家应该都玩过这个15数码的游戏吧,就在桌面小具库那里面哦. 一看到这个题就知道要GG,本着能骗点分的原则输出了 t 个无解,本来以为要爆零,没想到 ...

随机推荐

  1. DOM添加

    ㈠添加元素的步骤 ⑴创建空元素 ⑵设置关键属性 ⑶将元素添加到DOM树   ㈡创建空元素 var elem = document.createElement('table');   示例: var t ...

  2. cursor(鼠标手型)属性

    ㈠简单介绍 在浏览网页时,通常看到的鼠标光标形状有箭头.手形.沙漏等,而在 windows 中实际看到的鼠标指针种类比这个还要多. 一般情况下,鼠标光标的形状由浏览器负责控制,大多数情况的光标形状为箭 ...

  3. CSS样式,语法,添加方法,文本,字体

    总结一些css的基础知识 ㈠css样式 css:cascading style sheets  层叠样式表 css内容和样式相分离,便于修改样式. ㈡css语法 ㈢css添加方法 ⑴行内添加:放在&l ...

  4. javascript中的原型和原型链(五)

    Array.prototype 先记住一句话——每一个函数,都有一个prototype属性——每一个函数,无论是你自定义的,还是系统内置的 var fn = function() {} console ...

  5. [VIJOS2053][SDOI2019]世界地图:最小生成树+虚树

    分析 可以发现第一列和最后一列永远不会被删除,于是我们可以想到维护前后缀最小生成树,但是直接维护的话显然时间空间两爆炸.(通过上网找题解)可以发现我们关心的只是最左边和最右边两列,而不关心内部的连边情 ...

  6. Super Mario(主席树)

    Super Mario  Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded ...

  7. C++入门经典-例3.6-判断某一年是否是闰年之复合表达式法

    1:代码如下: // 3.6.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> using ...

  8. c++匿名函数精简写法

    main.cpp: #include<stdio.h> #include<functional> #include<unistd.h> std::function& ...

  9. C# 下载文件的心得

    下载文件最常用的有两种: 第一种:直接使用A标签,对应着文件的地址. 第二种:将文件写成流,然后在回传给客户端. 第一种,使用起来方便,但是有个坏处,如果你的文件是可编辑的文件(比如Excel wor ...

  10. 可滚动UIStackView 竖向居中 / 横向右对齐

    重点: 在scroll view和stack view之间加一个UIView. 竖向居中Tutorial: https://stackoverflow.com/questions/50766400/c ...