计算贡献-----hdu 7055 dp解法 && cf上一道类似的

Yiwen with Sqc
题意:
给一个字符串,对于每个小写字母 c h ch ch ,问所有区间内 c h ch ch 出现次数的平方和,其中 ∣ s ∣ < 1 0 5 |s|<10^{5} s<105
∑ c = 97 122 ∑ i = 1 n ∑ j = i n s q c ( s , l , r , c h ) 2 \sum_{c=97}^{122}\sum_{i=1}^n\sum_{j=i}^nsqc(s,l,r,ch)^2 c=97122i=1nj=insqc(s,l,r,ch)2
s q c ( s , l , r , c h ) sqc(s,l,r,ch) sqc(s,l,r,ch) 表示的是 s s s [ l , r ] [l,r] [l,r] 这段区间内 c h ch ch 字符出现的次数
思路:
dp解法:
大佬的题解
另一个大佬的题解
公式显然表示的是对于每个字符,都求一遍所有子段的该字符的 s q c sqc sqc
可以考虑 d p dp dp
d p [ i ] dp[i] dp[i] 表示所有以 i i i 结尾的所有字符串的答案总和
d p [ i ] dp[i] dp[i] d p [ i − 1 ] dp[i-1] dp[i1] 相对于每一个字符串都多了一个 s [ i ] s[i] s[i] 字符,那么先考虑一个字符串多了一个字符的变化是什么,假设一个字符串本来有 c n t cnt cnt s [ i ] s[i] s[i] 字符,那么这个字符串的答案的变化为 ( c n t + 1 ) 2 (cnt+1)^2 (cnt+1)2 − - c n t 2 cnt^2 cnt2 = = = 2 c n t + 1 2cnt+1 2cnt+1,即一个字符串多了 2 c n t + 1 2cnt+1 2cnt+1,而对于前面 i i i 个字符串的答案,即变化了 ∑ 1 i ( 2 c n t + 1 ) \sum_1^{i}(2cnt+1) 1i(2cnt+1),即为前面所有字符串的 s [ i ] s[i] s[i] 字符的两倍 + i +i +i

∑ 1 i ( 2 c n t + 1 ) \sum_1^i(2cnt+1) 1i(2cnt+1) = = = 2 2 2 × \times × s u m [ x ] sum[x] sum[x] + + + i i i ( x = s [ i ] − ′ a ′ + 1 ) (x=s[i]-'a'+1) (x=s[i]a+1)
对于一个字符串,维护 s [ i ] s[i] s[i] i i i 之前出现的次数每次只需要 + 1 +1 +1,维护 i i i 个字符串每次加 i i i
code:

#include
#define ll long long
#define _ 0
using namespace std;
const int maxn = 1e5 + 9;
const int mod = 998244353; 
ll n, m, k;
int dp[maxn], sum[30];
char s[maxn];
void work()
{
	memset(sum, 0, sizeof(sum));
    scanf("%s", s + 1);
    n = strlen(s + 1);
    ll ans = 0;
    for (ll i = 1; i <= n; ++i)
    {
    	int x = s[i] - 'a' + 1;
    	dp[i] = (dp[i-1] + 2ll * sum[x] + i) % mod;
    	sum[x] = (sum[x] + i) % mod;// 每个字符串+1,以i结尾的字符串有i个,所以+i
    	ans = (ans + dp[i]) % mod;
	}
	cout << ans << endl;
}

int main()
{
	int TT;cin>>TT;while(TT--)
	work();
	return ~~(0^_^0);
}

C. Sequence Pair Weight
题意:
定义一个子段的权重为:子段内相同元素的数对个数
给定一个序列,求序列所有子段权重之和
思路:
核心思想计算贡献
考虑 f [ i ] f[i] f[i] 表示以 i i i 结尾的所有子段的权重之和
首先以 i i i 结尾的子段所有的贡献,分为两部分
i − 1 i-1 i1 结尾的子段的贡献和加上 a i a_i ai 后多出的贡献
a i a_i ai 只会与前边与它相同的数匹配,假设 a j = a i ( j < i ) a_j=a_i(jaj=ai(j<i)
那么以 k ( 1 < = k < = j ) k(1<=k<=j) k(1<=k<=j) 为左端点的子段,都会多出 1 1 1 的贡献,累计会多出 j j j 的贡献
i i i 之前所有与 a i a_i ai 匹配的位置 k k k,都会产生 k k k 的贡献
m a p map map 维护即可
a n s ans ans 即为所有以 i i i 结尾的子段贡献之和
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
code:

#include
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 1e5 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
ll f[maxn];
int a[maxn];
map <int, ll> ma;
void work()
{
	cin >> n;
	for(int i = 0; i <= n; ++i) f[i] = 0;
	ma.clear();
	for(int i = 1; i <= n; ++i) cin >> a[i];
	ll ans = 0;
	for(int i = 1; i <= n; ++i){
		f[i] = f[i-1];
		if(ma.count(a[i])){
			f[i] += ma[a[i]];
		}
		ma[a[i]] += i;
		ans += f[i];
	}
	cout << ans << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

还有一种思路:
枚举到 a i a_i ai,假设 a j = a i ( j < i ) a_j=a_i(jaj=ai(j<i)
那么只考虑这个数对的贡献,找包含这个数对的子段个数,左端点可以选 [ 1 , j ] [1,j] [1,j],右端点可以选 [ i , n ] [i,n] [i,n]
那么就会贡献 j ∗ ( n − i + 1 ) j * (n - i + 1) j(ni+1)
对于所有 a j = a i a_j=a_i aj=ai j j j 都会产生贡献,因此累计 j j j 的下标和即可
扫一遍统计贡献即可

你可能感兴趣的:(动态规划,没理解很好的题目,哈希算法,算法,图论)