ABC331 A-F 线段树维护字符串哈希

Daiwa Securities Co. Ltd. Programming Contest 2023(AtCoder Beginner Contest 331) - AtCoder

掉大分的一场。D好若至调了好久,EF倒是全都一眼出做法...然而F手搓写bug了直到赛后才找到sb错误...

A - Tomorrow

题意:

每年M月,每个月D天,求y年m月d日的下一天的日期

题解:

void solve()
{
	int m, d, a, b, c;
	scanf("%d%d%d%d%d", &m, &d, &a, &b, &c);
	if (++c > d)
	{
		c = 1;
		if (++b > m)
			b = 1, ++a;
	}
	printf("%d %d %d\n", a, b, c);
}

B - Buy One Carton of Milk

题意:

超市里有盒装鸡蛋,六只装S元,八只装M元,十二只装L元,问购买不少于N个蛋要至少多少元

题解:

简单dp即可

int dp[N];
void solve()
{
	memset(dp, 0x3f, sizeof dp);
	dp[0] = 0;
	int n, s, m, l;
	scanf("%d%d%d%d", &n, &s, &m, &l);
	for (int i = 1; i <= n + 12; ++i)
	{
		if (i >= 6)dp[i] = dp[i - 6] + s;
		if (i >= 8)dp[i] = min(dp[i], dp[i - 8] + m);
		if (i >= 12)dp[i] = min(dp[i], dp[i - 12] + l);
	}
	int ans = INF;
	for (int i = n; i <= n + 12; ++i)
		ans = min(ans, dp[i]);
	printf("%d\n", ans);
}

C - Sum of Numbers Greater Than Me

题意:

给出一个场为N的数组A,问对于每个i输出A中所有大于Ai值的元素之和

题解:

前缀(后缀)和,注意数据会爆int

const int N = 1e6 + 10;
LL a[N], s[N];
void solve()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i)
	{
		scanf("%lld", &a[i]);
		s[a[i]] += a[i];
	}
	for (int i = N - 2; i >= 0; --i)
		s[i] += s[i + 1];
	for (int i = 1; i <= n; ++i)
	{
		printf("%lld ", s[a[i] + 1]);
	}
}

D - Tile Pattern

题意:

给出一个N*N的仅由'W'和'B'构成的字符矩阵,它会在平面内无限延展(如样例1的图),给出Q个询问,每个询问需要求出从(a, b)到(c, d)的矩形区域中黑色块('B')的数量

题解:

思路其实很简单,首先计算一片区域内'B'的数量能想到二维前缀和,其次我们可以把这个矩阵分割成几块然后分别求数量然后相加。

在有了以上基础做法之后我们再想如何简单的求出,先把分割出的区域看成9块,然后我们发现左上左下右上右下四块可以拼成一块来计算,左右拼一块,上下拼一块,正中间的一块,然后就这么做,注意细节即可(然后我这个若至就在这里花了40多分钟),这里把N*N的图扩展一下变成2N*2N的图能便于求解

int s[N][N];
int get_sum(int ux, int uy, int vx, int vy)
{
	return s[vx][vy] - s[ux - 1][vy] - s[vx][uy - 1] + s[ux - 1][uy - 1];
}
void solve()
{
	int n, q;
	scanf("%d%d", &n, &q);
	for (int i = 1; i <= n; ++i)
	{
		getchar();
		for (int j = 1; j <= n; ++j)
		{
			s[i][j] = (getchar() == 'B');
			s[i + n][j] = s[i][j + n] = s[i + n][j + n] = s[i][j];
		}
	}
	for (int i = 1; i <= 2 * n; ++i)
	{
		for (int j = 1; j <= 2 * n; ++j)
			s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
	}
	while (q--)
	{
		int ux, uy, vx, vy;
		scanf("%d%d%d%d", &ux, &uy, &vx, &vy);
		++vx, ++vy;
		int tux = ux % n, tvx = vx % n, tuy = uy % n, tvy = vy % n;
		if (tvx < tux)tvx += n;
		if (tvy < tuy)tvy += n;
		//printf("%d %d %d %d\n", tux, tuy, tvx, tvy);
		LL sx = (vx - ux) / n, sy = (vy - uy) / n;
		//printf("%lld %lld\n", sx, sy);
		LL ans = get_sum(tux + 1, tuy + 1, tvx, tvy);
		ans += sx * get_sum(1, tuy + 1, n, tvy);
		ans += sy * get_sum(tux + 1, 1, tvx, n);
		ans += sx * sy * get_sum(1, 1, n, n);
		printf("%lld\n", ans);
	}
}

E - Set Meal

题意:

餐厅给出N主菜,M道配菜,每道主菜的价格是ai,配菜的价格是bi,L中不能搭配在一起的组合ci, di,我们可以选择任意一道主菜+一道配菜(除了前面说的不能组合的)组成套餐,套餐的价格是ai+bi,求套餐价格的最大值

题解:

贪心,先将配菜存成对{价格,编号}然后使它按价格升序,将每个主菜不能搭配的配菜编存入set[i],遍历所有主菜,再从高价到低价遍历配菜,当遇到能做到的搭配时直接计算价格与答案取max然后break即可

int a[N];
PII b[N];
set<int>st[N];
void solve()
{
	int n, m, l;
	scanf("%d%d%d", &n, &m, &l);
	for (int i = 1; i <= n; ++i)
	{
		scanf("%d", &a[i]);
		st[i].clear();
	}
	for (int i = 1; i <= m; ++i)
	{
		scanf("%d", &b[i].first);
		b[i].second = i;
	}
	sort(b + 1, b + 1 + m);
	for (int i = 1; i <= l; ++i)
	{
		int c, d;
		scanf("%d%d", &c, &d);
		st[c].insert(d);
	}
	int ans = 0;
	for (int i = 1; i <= n; ++i)
	{
		for (int j = m; j >= 1; --j)
		{
			if (st[i].find(b[j].second) == st[i].end())
			{
				ans = max(ans, a[i] + b[j].first);
				break;
			}
		}
	}
	printf("%d\n", ans);
}

F - Palindrome Query

题意:

给出一个长度为N的由小写字母构成的字符串以及Q次操作:

1:修改下标为x的字符为c;2:询问子串l, r是否为回文串

题解:

线段树维护字符串哈希,哈希自然溢出就能过

typedef long long LL;
typedef unsigned long long ULL;
const LL N = 1e6 + 10, P = 13331;
#define ls(i) (i<<1)
#define rs(i) (i<<1|1)
#define lh(i) tr[i].lh
#define rh(i) tr[i].rh
#define d(i) tr[i].d
struct node
{
	int d;
	ULL lh, rh;
}tr[N << 2];
ULL p[N];
void push_up(int i)
{
	lh(i) = lh(ls(i)) * p[d(rs(i))] + lh(rs(i));
	rh(i) = rh(ls(i)) + rh(rs(i)) * p[d(ls(i))];
}
void build(int l, int r, int i)
{
	d(i) = r - l + 1;
	if (l == r)
	{
		lh(i) = rh(i) = getchar();
		return;
	}
	int mid = l + r - 1 >> 1;
	build(l, mid, ls(i));
	build(mid + 1, r, rs(i));
	push_up(i);
}
void motify(int pos, int data, int l, int r, int i)
{
	if (l == r)
	{
		lh(i) = rh(i) = data;
		return;
	}
	int mid = l + r - 1 >> 1;
	if (pos <= mid)motify(pos, data, l, mid, ls(i));
	else motify(pos, data, mid + 1, r, rs(i));
	push_up(i);
}
node query_range(int ql, int qr, int l, int r, int i)
{
	if (ql <= l && r <= qr)
		return tr[i];
	int mid = l + r - 1 >> 1;
	if (qr <= mid)return query_range(ql, qr, l, mid, ls(i));
	if (ql > mid)return query_range(ql, qr, mid + 1, r, rs(i));
	node ll = query_range(ql, qr, l, mid, ls(i)), rr = query_range(ql, qr, mid + 1, r, rs(i));
	node res = { ll.d + rr.d, ll.lh * p[rr.d] + rr.lh, ll.rh + rr.rh * p[ll.d] };
	return res;
}
void solve()
{
	int n, q;
	scanf("%d%d", &n, &q);
	getchar();
	p[0] = 1;
	for (int i = 1; i <= n; ++i)
		p[i] = p[i - 1] * P;
	build(1, n, 1);
	while (q--)
	{
		int op;
		scanf("%d", &op);
		if (op == 1)
		{
			int x;
			char c;
			scanf("%d %c", &x, &c);
			motify(x, c, 1, n, 1);
		}
		else
		{
			int l, r;
			scanf("%d%d", &l, &r);
			node t = query_range(l, r, 1, n, 1);
			if (t.lh == t.rh)
				printf("Yes\n");
			else
				printf("No\n");
		}
	}
}

最搞笑的一集,看完题直接出做法然告诉队友之后开始手搓线段树,然后队友去贴板子小修一下直接过了,我写个bug调了20分钟爆金币了

感觉这个写的还蛮好的,以后可以直接当自己的板子用了

``` #include<bits/stdc++.h> #define ll long long using namespace std; struct Node{ int v,p,sz; unsigned ll he; Node *cl,*cr; Node(int x):v(x),p(rand()),sz(1),he(x),cl(nullptr),cr(nullptr){} ~Node(){ delete cl; delete cr; } friend int siz(Node *x){ if(x==nullptr)return 0; return x->sz; } void push_up(){ sz=1; he=v*(1u<<siz(cl)); if(cl!=nullptr){ sz+=cl->sz; he+=cl->he; } if(cr!=nullptr){ sz+=cr->sz; he+=(1ull<<(siz(cl)+1))*cr->he; } } friend Node* merge(Node *x,Node *y){ if(x==nullptr)return y; if(y==nullptr)return x; if(x->p<y->p){ x->cr=merge(x->cr,y); x->push_up(); return x; }else{ y->cl=merge(x,y->cl); y->push_up(); return y; } } friend Node* split(Node *&x,int r){ if(x==nullptr)return nullptr; if(siz(x->cl)>=r){ Node *t=split(x->cl,r); swap(t,x->cl); x->push_up(); swap(t,x); return t; }else{ Node *t=split(x->cr,r-siz(x->cl)-1); x->push_up(); return t; } } friend void change(Node *&h,int x,Node w){ Node *wr=split(h,x),*dq=split(h,x-1); delete dq; h=merge(h,merge(new Node(w),wr)); } friend void add(Node *&h,int x,Node w){ Node *wr=split(h,x); h=merge(h,merge(new Node(w),wr)); } }; int main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); string s; cin>>s; Node *tr1=nullptr,*tr2=nullptr; for(int i=0;i<s.size();++i){ tr1=merge(tr1,new Node(s[i]-'a'+1)); tr2=merge(tr2,new Node(s[i]-'a'+1)); } int T; cin>>T; while(T--){ char op; cin>>op; if(op=='Q'){ int x,y; cin>>x>>y; Node *r1=split(tr1,x-1),*r2=split(tr2,y-1); int ans=0; for(int i=20;i>=0;--i){ if(ans+(1<<i)>min(siz(r1),siz(r2)))continue; Node *rr1=split(r1,ans+(1<<i)),*rr2=split(r2,ans+(1<<i)); if(r1->he==r2->he)ans+=1<<i; merge(r1,rr1); merge(r2,rr2); } cout<<ans<<endl; tr1=merge(tr1,r1); tr2=merge(tr2,r2); }else if(op=='R'){ int x; char c; cin>>x>>c; change(tr1,x,Node(c-'a'+1)); change(tr2,x,Node(c-'a'+1)); }else{ int x; char c; cin>>x>>c; add(tr1,x,Node(c-'a'+1)); add(tr2,x,Node(c-'a'+1)); } } delete tr1; delete tr2; return 0; }```debug # P4036 [JSOI2008] 火星人 ## 题目描述 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀。 比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号: ``` 序号 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m ``` 现在,火星人定义了一个函数 $LCQ(x, y)$,表示:该字符串中第 $x$ 个字符开始的字串,与该字符串中第 $y$ 个字符开始的字串,两个字串的公共前缀的长度。比方说,$LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0$ 在研究 $LCQ$ 函数的过程中,火星人发现了这样的一个关联:如果把该字符串的所有后缀排好序,就可以很快地求出 $LCQ$ 函数的值;同样,如果求出了 $LCQ$ 函数的值,也可以很快地将该字符串的后缀排好序。 尽管火星人聪明地找到了求取 $LCQ$ 函数的快速算法,但不甘心认输的地球人又给火星人出了个难题:在求取 $LCQ$ 函数的同时,还可以改变字符串本身。具体地说,可以更改字符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。地球人想考验一下,在如此复杂的问题中,火星人是否还能够做到很快地求取 $LCQ$ 函数的值。 ## 输入格式 第一行给出初始的字符串。第二行是一个非负整数 $M$ ,表示操作的个数。接下来的M行,每行描述一个操作。操作有 $3$ 种,如下所示 1. 询问。语法:$Q$ $x$ $y$ ,$x$ ,$y$ 均为正整数。功能:计算 $LCQ(x,y)$ 限制:$1$ $\leq$ $x$ , $y$ $\leq$ 当前字符串长度 。 2. 修改。语法:$R$ $x$ $d$,$x$ 是正整数,$d$ 是字符。功能:将字符串中第 $x$ 个数修改为字符 $d$ 。限制:$x$ 不超过当前字符串长度。 3. 插入:语法:$I$ $x$ $d$ ,$x$ 是非负整数,$d$ 是字符。功能:在字符串第 $x$ 个字符之后插入字符 $d$ ,如果 $x=0$,则在字符串开头插入。限制:$x$ 不超过当前字符串长度 ## 输出格式 对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。 ## 输入输出样例 #1 ### 输入 #1 ``` madamimadam 7 Q 1 7 Q 4 8 Q 10 11 R 3 a Q 1 7 I 10 a Q 2 11 ``` ### 输出 #1 ``` 5 1 0 2 1 ``` ## 说明/提示 1. 所有字符串自始至终都只有小写字母构成。 2. $M\leq150,000$ 3. 字符串长度L自始至终都满足$L\leq100,000$ 4. 询问操作的个数不超过 $10,000$ 个。 对于第 $1$,$2$ 个数据,字符串长度自始至终都不超过 $1,000$ 对于第 $3$,$4$,$5$ 个数据,没有插入操作。 2024/07/40 更新一组 hack。
04-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值