2023.9.23 总结

T1

题面:从左往右有n个格子,编号 \(1\)\(n\) 。一开始每个格子都有1颗糖果。你总共需要进行 \(k\) 次操作,每次操作把从某个格子取 \(1\) 颗糖(前提是该格子有糖),放到另一个格子。当 \(k\) 次操作全部结束以后,从左往右检查,这 \(n\) 个格子的糖果数量。求这 \(n\) 个格子总共有多少种不同的状态,答案模 \(1000000007\) ,$ 1 \le n \le 200000 , 1 \le k \le 10^9$ 。
因为最多换 \(k\) 次,且只有 \(n\) 个位,所以最多只有 \(min(n,k+1)\) 个位为 \(0\)
那我们可以枚举有 \(i\)\(0\) ,有 \(C(n,i)\) 种位置为 \(0\) ,挪到其他任意位置上,可一个都不放(就是只有原来那个1),方案数就是一个经典放盘子问题了,即为 \(C(n,i) \times C(n-1,i)\)
最后特判一下全是1即可。

T2

题意:给出一个 \(n \times m\) 的网格,其中有一些格子是黑色,现定义一个美丽的图为:对于一个黑色格子,它的下面那个格子与它右下面那个格子都需为黑色,现在可以将一些格子染成黑色,求最终有多少个美丽的图?
分析美丽的图的性质,可发现:对于每一列,只有最上面那一个需要染色,对于一个斜行,也只有最左边的需要染色。
当然,已有的需要考虑。
我们可以以此来考虑$ DP $ 。
就是每一列每一列的做,考虑每一个斜行就可以了。

T3

题面:给出包含 \(n\) 个整数的数组 \(a\) ,其中对于所有的 \(i\) ,都满足 \(1\le a[i] \le m\)。同时 \(1\) ~ \(m\)\(a\) 中至少出现了一次,请你找出数组 \(a\) 的一个长度为 \(m\) 的子序列,使得 \(m\) 个整数在子序列中恰好只出现 \(1\) 次,输出满足条件的字典序最小的子序列。
这道题可以用线段树暴做,这里提供一种贪心的想法。
选出一个满足题目要求的子序列很简单,但是重点是让字典序最小。
很明显,我们希望让越大的数越后选,但是又必须选到这个数,在他最后一次出现切前面没选到的时候就必须选他了。
所以,我们可以这样贪心,开一个数组 \(v\) 以及 \(last\) ,记录这个数是否选到过以及最后一次出现的位置。
从前往候选,开一个记录选数的序列,若最后选的一个数以后还有且选现在这个数比最后选的一个数优,替换之,否则将其加进去。
时间复杂度 \(O(n)\) 可过。
还可以这样:
我们可以从后往前选,随便构造出一个序列,不一定字典序最小,用链表存储。
每次往前找,看一下当前选的这个数放进去之后会不会比原来更优,是则替换之,然后看前一个。

T4

题意:给出一棵树,求不在同一条路径上的三元点对数量。
这道题是正着思考。
从一个节点出发,若有三个节点不在同一路径上,则要不然在他不同的三棵子树,或两个在两棵不同的子树,一个不在自己子树,或者两个或三个节点都在其祖先但不在自己的子树上。
对于最后一种情况,其实考虑到别的节点,还会变成前两种情况。
所以,只考虑前两种情况。
我们令 \(size_{x}\) 为以 \(x\) 为子树根节点,子树的节点数量。
对于以 \(x\) 为根节点的子树,那么对于第一种情况,就是 \((n-size_{x})\) 乘上任意两个子树 \(size\) 的乘积和。
对于第二种情况,就是任意三个子树 \(size\) 的乘积和。
累加输出答案。

T5

题意:给出一个字符串 \(a\) ,求有多少个字符串 \(b\) ,使得 \(bb\)\(a\) 的子序列。
一个基本的思路,枚举在第 \(i\) 个位置分开 \(a\) ,前面有个 \(b\) ,后面也有个 \(b\)
但要统计的是字符串,有可能会有重复,我们要想方法去重。
先预处理出每个位置往后字符 \(a,b,c,d...\) 第一次出现的位置,记为 \(b_{i,j}\)
动规数组 \(f_{i,j}\) 表示从 \(1~i,x~j\) 中新增了多少个满足的字符串,\(x\) 为分开的位置,注意,是新增的字符串。
转移我们只需枚举每个字母,他们对于 \(i,j\) 往后第一个出现的位置 \(x,y\) ,让 \(f_{x,y}\) 加上 \(f{i,j}\) 即可。
这里可以保证一个东西,就是字符串一定在最前面出现。
例如:

abc|abcabc

我们在如图转移时,就会在 \(f_{3,6}\) 加上 \(abc\) 的答案,而不会在 \(f_{3,9}\) 加上。
如何统计答案呢?
若在 \(x\) 处分开,第 \(x\) 个字符为 \(A\),对于 \(f_{i,j}\) ,若 \(i\)\(A\) 第一次出现是在 \(x\) 处,就可以使答案加上 \(f_{i,j}\)
因为,如果不是在 \(x\) 处,设是在 \(y\) 处,就说明之前已经有一次以 \(y\) 为分割处理过这个答案了。
举例:

abcabc|abc

我们在做 \(f_{3,9}\) 时,中间包含了 \(abc\)
\(3\) 后第 \(1\) 次出现 \(a\) 是在 \(4\) ,说明我们在 \(4\) 前分割时已处理过这个答案。
最后输出即可。
代码:

#include<bits/stdc++.h>
using namespace std;
const long long mod=998244353;
string a;
long long t[105][55],s,f[105][105];
void dijah(long long x)
{
	long long q=t[1][a[x]-'a'],w;
	if(q==0||q>=x)return;
	memset(f,0,sizeof(f));
	f[q][x]=1;
	for(int i=1;i<x;i++)
	{
		for(int j=x;j<a.size();j++)
		{
			for(int u=0;u<26;u++)
			{
				q=t[i+1][u],w=t[j+1][u];
				if(q&&w&&q<x)f[q][w]+=f[i][j],f[q][w]%=mod;
			}
			q=t[i+1][a[x]-'a'];
			if(q==x)s+=f[i][j],s%=mod;
		}
	}
	return;
}
int main()
{
	cin>>a;
	a=' '+a;
	for(int i=1;i<a.size();i++)
	{
		for(int j=0;j<26;j++)
		{
			for(int u=i;u<a.size();u++)
			{
				if(j+'a'==a[u])
				{
					t[i][j]=u;
					break;
				}
			}
		}
	}
	for(int i=2;i<a.size();i++)dijah(i),s%=mod;
	cout<<s;








  return 0;
}

posted @ 2023-09-30 20:54  dijah  阅读(22)  评论(0编辑  收藏  举报