Educational Codeforces Round 110 div.2 A~F题解

视频讲解:BV1254y137Rn

A. Fair Playoff

题目大意

4 4 4 位选手参加比赛,第 i i i 位选手的水平为 s i ( 1 ≤ s i ≤ 100 ) s_i(1 \leq s_i \leq 100) si(1si100) 且每位选手的水平不同。如果两位选手比赛,那么水平更高的选手会获胜。
比赛采用以下赛制:

  • 第一位选手和第二位选手比赛
  • 第三位选手和第四位选手比赛
  • 上述两场比赛的胜者进行决赛

如果是水平最高的两位选手在决赛相遇,则称比赛是公平的。
请判断比赛是否公平。

题解

简单的模拟题,有多种方法可以实现:

  • 直接判断决赛的两位选手的水平是否为最高与次高
  • 判断 m a x ( s 1 , s 2 ) > m i n ( s 3 , s 4 ) max(s_1,s_2)>min(s_3,s_4) max(s1,s2)>min(s3,s4) m a x ( s 3 , s 4 ) > m i n ( s 1 , s 2 ) max(s_3,s_4)>min(s_1,s_2) max(s3,s4)>min(s1,s2) 是否都成立
  • 判断 m i n ( m a x ( a , b ) , m a x ( c , d ) ) ≥ m a x ( m i n ( a , b ) , m i n ( c , d ) ) min(max(a,b),max(c,d)) \geq max(min(a,b),min(c,d)) min(max(a,b),max(c,d))max(min(a,b),min(c,d)) 是否成立

参考代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int main()
{
	int T,i,s[10],b[10];
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d%d",&s[1],&s[2],&s[3],&s[4]);
		for(i=1;i<=4;i++)
			b[i]=s[i];
		sort(b+1,b+5);
		if(max(s[1],s[2])+max(s[3],s[4])==b[3]+b[4])
			printf("YES\n");
		else
			printf("NO\n");
	}
}

B. Array Reodering

题目大意

给定包含 n ( 1 ≤ n ≤ 2000 ) n(1 \leq n \leq 2000) n(1n2000) 个元素的数组 a ( 1 ≤ a i ≤ 1 0 5 ) a(1 \leq a_i \leq 10^5) a(1ai105)
对于一对索引 i , j i,j i,j ,若 1 ≤ i < j ≤ n 1 \leq i < j \leq n 1i<jn g c d ( a i , 2 a j ) > 1 gcd(a_i,2a_j)>1 gcd(ai,2aj)>1 ,则称其为好的索引对。
现在你可以对数组进行重新排列,求最大的好索引对数量。

题解

如果 a i a_i ai 是偶数,那么必定满足 g c d ( a i , 2 a j ) > 1 gcd(a_i,2a_j)>1 gcd(ai,2aj)>1 ,因此在重新排列时,应该将偶数都排在前面,奇数排在后面。
对于奇数的 a i , a j a_i,a_j ai,aj g c d ( a i , 2 a j ) = g c d ( a i , a j ) gcd(a_i,2a_j)=gcd(a_i,a_j) gcd(ai,2aj)=gcd(ai,aj) ,可以直接用两重循环在 O ( N 2 ) O(N^2) O(N2) 复杂度内求解。
也可以对每个数分解质因数后,用容斥处理。由于分解质因数后 a i a_i ai 的质因数个数最多为 5 5 5 个,因此复杂度可以优化到 O ( N ∗ 2 5 ) O(N*2^5) O(N25) 。(其实不优化也完全没事,还多打了好多代码)

参考代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int MAXN=100100;
int npri[MAXN],sum[MAXN],a[MAXN];
vector<int> fac[MAXN];

int main()
{
	int i,j,k,T,n,limit,tmp,num1,cnt,x;
	ll ans;
	for(i=2;i<MAXN;i++)
	{
		if(npri[i])
			continue;
		fac[i].push_back(i);
		for(j=i+i;j<MAXN;j+=i)
		{
			npri[j]=1;
			fac[j].push_back(i);
		}
	}
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		cnt=0;
		for(i=1;i<=n;i++)
		{
			scanf("%d",&x);
			if(x%2)
				a[++cnt]=x;
		}
		ans=1ll*(n-1+cnt)*(n-cnt)/2;
		sort(a+1,a+cnt+1);
		memset(sum,0,sizeof(sum));
		for(i=1;i<=cnt;i++)
		{
			limit=1<<(fac[a[i]].size());
			for(j=1;j<limit;j++)
			{
				tmp=1;
				num1=0;
				for(k=0;k<fac[a[i]].size();k++)
				{
					if((j>>k)&1)
					{
						num1++;
						tmp*=fac[a[i]][k];
					}
				}
				if(num1&1)
					ans+=sum[tmp];
				else
					ans-=sum[tmp];
				sum[tmp]++;
			}
		}
		printf("%lld\n",ans);
	}
}

C. Unstable String

题目大意

给定一个仅由 0,1 和 ? 组成的字符串 s ( 1 ≤ ∣ s ∣ ≤ 2 ⋅ 1 0 5 ) s(1 \leq |s| \leq 2 \cdot 10^5) s(1s2105)
如果一个字符串由 0 和 1 组成,且相邻字符不同,则称其为不稳定字符串。
如果一个字符串由 0,1 和 ? 组成,且可以通过替换 ? 为 0 或 1 (每个 ? 可以替换为不同的字符),则称其为漂亮字符串。
求字符串 s s s 有多少个漂亮的连续子串。

题解

如果子串 s [ l , r ] s[l,r] s[l,r] 是漂亮的,那么 s [ i , r ] ( l ≤ i ≤ r ) s[i,r](l \leq i \leq r) s[i,r](lir) 也是漂亮的。
因此可以记录上一次的非法位置,那么非法位置之后的子串,都是漂亮的。
由于不确定 ? 变成 0 还是 1 ,因此不妨设:

  • b e f 0 bef0 bef0 表示无法满足奇数位为 1 偶数位为 0 的前一个位置
  • b e f 1 bef1 bef1 表示无法满足奇数位为 0 偶数位为 1 的前一个位置

如果连续子串右端点为 r r r ,则

  • 左端点 l ∈ [ b e f 0 + 1 , r ] l \in [bef0+1,r] l[bef0+1,r] 时,子串 s [ l , r ] s[l,r] s[l,r] 可以构成奇数位为 1 1 1 偶数为为 0 0 0 的漂亮串;
  • 左端点 l ∈ [ b e f 1 + 1 , r ] l \in [bef1+1,r] l[bef1+1,r] 时,子串 s [ l , r ] s[l,r] s[l,r] 可以构成奇数位为 0 0 0 偶数为为 1 1 1 的漂亮串;

因此当 r = i r=i r=i 时, l ∈ [ m i n ( b e f 0 , b e f 1 ) + 1 , r ] l \in [min(bef0,bef1)+1,r] l[min(bef0,bef1)+1,r] 时,子串 s [ l , r ] s[l,r] s[l,r] 是漂亮串。
实现时,可以枚举右端点 r r r ,不断更新 b e f 0 bef0 bef0 b e f 1 bef1 bef1 ,并统计贡献。

参考代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int MAXN=200200;
char s[MAXN];

int main()
{
	int T,i,len,bef0,bef1;
	ll ans;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%s",&s);
		len=strlen(s);
		bef0=bef1=-1;
		ans=0;
		for(i=0;i<len;i++)
		{
			if(s[i]=='?')
				ans+=max(i-bef0,i-bef1);
			else if((s[i]-'0')==i%2)
			{
				ans+=i-bef0;
				bef1=i;
			}
			else
			{
				ans+=i-bef1;
				bef0=i;
			}
		}
		printf("%lld\n",ans);
	}
}

D. Playoff Tournament

题目大意

2 k ( 1 ≤ k ≤ 18 ) 2^k(1 \leq k \leq 18) 2k(1k18) 支队伍参加淘汰赛,他们共进行 2 k − 1 2^k-1 2k1 场比赛。
其中前 2 k − 1 2^{k-1} 2k1 场比赛,分别为 1 1 1 队与 2 2 2 队比赛, 3 3 3 队与 4 4 4 队比赛… 2 k − 1 2^k-1 2k1 队与 2 k 2^k 2k 队比赛。
之后的 2 k − 2 2^{k-2} 2k2 场比赛,分别为第 1 1 1 场的胜者与第 2 2 2 场比赛的胜者比赛,第 3 3 3 场的胜者与第 4 4 4 场比赛的胜者比赛…第 2 k − 1 − 1 2^{k-1}-1 2k11 场的胜者与第 2 k − 1 2^{k-1} 2k1 场比赛的胜者比赛。
以此类推,直到只剩下一支队伍。

给定一个长度为 2 k − 1 2^k-1 2k1 的字符串,每个字符表示某一场比赛的结果:

  • s i = 0 s_i=0 si=0 ,则表示在第 i i i 场比赛中,编号更小的队伍获胜;
  • s i = 1 s_i=1 si=1 ,则表示在第 i i i 场比赛中,编号更大的队伍获胜;
  • s i = ? s_i=? si=? ,则表示在第 i i i 场比赛中,两只队伍都可能获胜;

q ( 1 ≤ q ≤ 2 ⋅ 1 0 5 ) q(1\leq q \leq 2 \cdot 10^5) q(1q2105) 次对字符串的修改操作,每次操作如下:

  • 输入 p ( 1 ≤ p ≤ 2 k − 1 ) p(1 \leq p \leq 2^k-1) p(1p2k1) c ( c ∈ { 0 , 1 , ? } ) c(c \in \{0,1,?\}) c(c{0,1,?}) ,将 s i s_i si 修改为 c c c

每次修改后,需要输出最终有多少只队伍可能获胜。

题解

不妨先将整个字符串 s s s 翻转,并对每场比赛重新编号,那么 s 1 s_1 s1 就代表总决赛, s 2 , s 3 s_2,s_3 s2,s3 代表四分之一决赛, s 4 , s 5 , s 6 , s 7 s_4,s_5,s_6,s_7 s4,s5,s6,s7 代表八分之一决赛…
以此类推,发现第 i i i 场比赛的选手,分别是第 2 i 2i 2i 场比赛的胜者和第 2 i + 1 2i+1 2i+1 场比赛的胜者。这一结构和线段树十分相似,考虑采用类似线段树的方式维护。

s u m i sum_i sumi 表示第 i i i 场比赛的胜者有几种可能性。那么将子节点信息汇总到父节点的 p u s h U p pushUp pushUp 函数可以表示为:

  • s i = 0 s_i=0 si=0 ,则 s u m i = s u m 2 i + 1 sum_i=sum_{2i+1} sumi=sum2i+1
  • s i = 1 s_i=1 si=1 ,则 s u m i = s u m 2 i sum_i=sum_{2i} sumi=sum2i
  • s i = ? s_i=? si= ,则 s u m i = s u m 2 i + s u m 2 i + 1 sum_i=sum_{2i}+sum_{2i+1} sumi=sum2i+sum2i+1

每次修改时,逐个用 p u s h U p pushUp pushUp 函数修改从第 p p p 场比赛到总决赛的 s u m sum sum 信息即可。

s u m 1 sum_1 sum1 即是最终可能获胜的队伍数。

参考代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int MAXN=600600;
char s[MAXN];
int k,mx,sum[MAXN];

void pushUp(int i)
{
	if(s[i]=='0')
		sum[i]=sum[i<<1|1];
	else if(s[i]=='1')
		sum[i]=sum[i<<1];
	else
		sum[i]=sum[i<<1]+sum[i<<1|1];
}

void build(int i)
{
	if(i>=mx)
	{
		sum[i]=1;
		return;
	}
	build(i<<1);
	build(i<<1|1);
	pushUp(i);
}

void change(int i)
{
	if(!i)
		return;
	pushUp(i);
	change(i>>1);
}

int main()
{
	int q,p;
	char c;
	scanf("%d",&k);
	mx=(1<<k);
	scanf("%s",s+1);
	reverse(s+1,s+mx);
	build(1);
	scanf("%d",&q);
	while(q--)
	{
		scanf("%d %c",&p,&c);
		p=mx-p;
		s[p]=c;
		change(p);
		printf("%d\n",sum[1]);
	}
}

E. Gold Transfer

题目大意

给定一棵有根树,根节点编号为 0 0 0 。每个节点有 a i ( 1 ≤ a i ≤ 1 0 6 ) a_i(1 \leq a_i \leq 10^6) ai(1ai106) 吨黄金,每吨黄金价格为 c i ( 1 ≤ c i ≤ 1 0 6 ) c_i(1 \leq c_i \leq 10^6) ci(1ci106)

初始只有 0 0 0 号节点,接下来有 q ( 1 ≤ q ≤ 3 ⋅ 1 0 5 ) q(1 \leq q \leq 3 \cdot 10^5) q(1q3105) 次操作,每次操作有以下两种类型:

  • 1    p i    a i    c i 1 \; p_i \; a_i \; c_i 1piaici -添加节点 i i i ,其父亲为 p i p_i pi ,有 a i a_i ai 吨黄金,每吨黄金价格为 c i c_i ci ,保证 c i > c p i c_i>c_{p_i} ci>cpi
  • 2    v i    w i 2 \; v_i \; w_i 2viwi -在 v i v_i vi 到根节点的路径上的节点中,用最小的花费购买 w i w_i wi 吨黄金,如果无法买齐 w i w_i wi 吨黄金,则买下所有能购买的黄金。在每个节点购买黄金后,该节点的 a i a_i ai 会减少相应数值。

读入强制在线。

题解

因为 c i > c p i c_i>c_{p_i} ci>cpi ,因此从 v i v_i vi 到根节点的路径上,黄金是越来越便宜的。
得到贪心策略:

  • 对于 v i v_i vi ,寻找 v i v_i vi 到根节点的路径上,最靠近根节点且还有黄金余量的节点 s t st st ,从 s t st st 开始向 v i v_i vi 逐个购买黄金,直到买够 w i w_i wi 吨黄金或到达 v i v_i vi 节点。

由于每次修改只会向树中添加节点,因此可以用倍增的方式维护树。设 p i , j p_{i,j} pi,j 表示 i i i 节点向上 2 j 2^j 2j 步的节点。
基于 p i , j p_{i,j} pi,j ,可以在 O ( l o g ( q ) ) O(log(q)) O(log(q)) 的复杂度内找到任意节点向上若干步的祖先节点编号。
寻找 s t st st 时,可以采用二分结合倍增寻找,实现时两者可以结合在一起,减少一个 l o g log log 的复杂度。

总复杂度 O ( q ∗ l o g ( q ) ) O(q*log(q)) O(qlog(q))

参考代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int MAXN=300300;
const int MAXM=20;
int p[MAXN][MAXM];
ll a[MAXN],c[MAXN];

int findfa(int x,int dep)
{
	for(int i=0;dep>=(1<<i);i++)
	{
		if(x==-1)
			return -1;
		if(dep&(1<<i))
			x=p[x][i];
	}
	return x;
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int q,x,y,z,op,i,dis,rt,now;
	ll tmp,ans,w,num;
	memset(p,-1,sizeof(p));
	cin>>q>>a[0]>>c[0];
	for(x=1;x<=q;x++)
	{
		cin>>op;
		if(op==1)
		{
			cin>>p[x][0]>>a[x]>>c[x];
			for(i=1;i<MAXM;i++)
			{
				if(p[x][i-1]==-1)
					p[x][i]=-1;
				else
					p[x][i]=p[p[x][i-1]][i-1];
			}
		}
		else
		{
			cin>>y>>w;
			dis=0;
			now=y;
			for(i=MAXM-1;i>=0;i--)
			{
				if(p[now][i]!=-1&&a[p[now][i]])
				{
					dis+=1<<i;
					now=p[now][i];
				}
			}
			num=0;
			ans=0;
			for(i=dis;i>=0&&(w-num);i--)
			{
				now=findfa(y,i);
				tmp=min(w-num,a[now]);
				ans+=tmp*c[now];
				num+=tmp;
				a[now]-=tmp;
			}
			if(w-num)
			{
				tmp=min(w-num,a[y]);
				ans+=tmp*c[y];
				num+=tmp;
				a[y]-=tmp;
			}
			cout<<num<<" "<<ans<<endl;
		}
	}
}

F. String Distance

题目大意

对于两个字符串 a a a b b b ,你可以对其进行任意次下述操作:

  • 选择字符串 a a a b b b 的任意连续子串并排序

f ( a , b ) f(a,b) f(a,b) 表示使得 a = b a=b a=b 的最小操作次数。如果不存在 a = b a=b a=b 的方案,则 f ( a , b ) = 1337 f(a,b)=1337 f(a,b)=1337

给定 n ( 1 ≤ n ≤ 2 ⋅ 1 0 5 ) n(1 \leq n \leq 2 \cdot 10^5) n(1n2105) 个不同的字符串 s 1 , s 2 , . . . , s n ( ∣ s 1 ∣ = ∣ s 2 ∣ = . . . = ∣ s n ∣ , n ⋅ ∣ s 1 ∣ ≤ 2 ⋅ 1 0 5 ) s_1,s_2,...,s_n(|s_1|=|s_2|=...=|s_n|,n \cdot |s_1| \leq 2 \cdot 10^5) s1,s2,...,sn(s1=s2=...=sn,ns12105) ,求 ∑ i = 1 n ∑ j = i + 1 n f ( s i , s j ) \sum_{i=1}^n{\sum_{j=i+1}^n{f(s_i,s_j)}} i=1nj=i+1nf(si,sj)

题解

参考了AliShahali1382的代码。

m m m 为字符串长度, S [ l , r ] S[l,r] S[l,r] 为字符串 S S S 的从 S l S_l Sl S r S_r Sr 的子串。

对于两个字符串 A , B ( A < B ) A,B(A < B) A,B(A<B) ,有以下几种情况:

  1. A A A B B B 的组成字符不一样, f ( A , B ) = 1337 f(A,B)=1337 f(A,B)=1337
  2. 需要对 A A A B B B 都排序后才能得到 A = B A=B A=B f ( A , B ) = 2 f(A,B)=2 f(A,B)=2
  3. B B B 的某一子串排序后得到 A = B A=B A=B f ( A , B ) = 1 f(A,B)=1 f(A,B)=1

对于第一种情况,直接对字符串按不同字符组成分类即可。

对于第二种和第三种情况,不妨先假设都是第二种情况计算贡献,再减去第三种情况的数量。

考虑第三种情况出现的条件。
i n c i inc_i inci 表示满足字符串 A A A 的子串 A [ i , i n c i ] A[i,inc_i] A[i,inci] 是连续不下降子串的最大值。
如果字符串 B B B 满足 B [ 0 , i − 1 ] = A [ 0 , i − 1 ] B[0,i-1]=A[0,i-1] B[0,i1]=A[0,i1] B [ i n c i + 1 , m − 1 ] = A [ i n c i + 1 , m − 1 ] B[inc_i+1,m-1]=A[inc_i+1,m-1] B[inci+1,m1]=A[inci+1,m1] ,则将子串 B [ i , i n c i ] B[i,inc_i] B[i,inci] 排序后即可得到 A = B A=B A=B ,即 f ( A , B ) = 1 f(A,B)=1 f(A,B)=1

因此可以先对同一组成的字符串排序,枚举字符串 A A A ,再从大到小枚举最长公共前缀 l c p lcp lcp ,寻找满足 B [ 0 , l c p − 1 ] = A [ 0 , l c p − 1 ] B[0,lcp-1]=A[0,lcp-1] B[0,lcp1]=A[0,lcp1] B [ l c p ] ≠ A [ l c p ] B[lcp] \neq A[lcp] B[lcp]=A[lcp] B [ i n c l c p + 1 , m − 1 ] = A [ i n c l c p + 1 , m − 1 ] B[inc_{lcp}+1,m-1]=A[inc_{lcp}+1,m-1] B[inclcp+1,m1]=A[inclcp+1,m1] 的字符串 B B B 的数量。

  • 对于 B [ 0 , l c p − 1 ] = A [ 0 , l c p − 1 ] B[0,lcp-1]=A[0,lcp-1] B[0,lcp1]=A[0,lcp1] B [ l c p ] ≠ A [ l c p ] B[lcp] \neq A[lcp] B[lcp]=A[lcp] 的条件,由于字符串是排序后的,因此可以基于二分确定字符串 B B B 的范围 B ∈ { s l , s l + 1 , . . . , s r } B \in \{s_l,s_{l+1},...,s_r \} B{sl,sl+1,...,sr} ,其中 r r r 通过二分查找确定, l l l 由上一次的 r r r 转移得到。
  • 对于 B [ i n c l c p + 1 , m − 1 ] = A [ i n c l c p + 1 , m − 1 ] B[inc_{lcp}+1,m-1]=A[inc_{lcp}+1,m-1] B[inclcp+1,m1]=A[inclcp+1,m1] 的条件。由于是求相同后缀的数量,不妨将字符串都倒序存入字典树,并在字典树上的每个节点都创建一个向量,存储字符串的编号。那么求指定后缀的字符串,可以直接在该后缀对应的节点上的向量内二分查找编号在 [ l , r ] [l,r] [l,r] 范围内数量。

为了快速找到后缀 A [ i n c i + 1 , m − 1 ] A[inc_i+1,m-1] A[inci+1,m1] 对应的节点,可以用倍增的方式,在字典树上查找后缀 A A A 所在的叶节点的向上 i n c i + 1 inc_i+1 inci+1 步的祖先。

总复杂度 O ( N M l o g N ) O(NMlogN) O(NMlogN) N N N 为字符串数量, M M M 为字符串长度。

参考代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const int MAXN=200200;
const int MAXM=26;
const int MAXG=18;
int n,m;
int p[MAXN][MAXG],tr[MAXN][MAXM],dest[MAXN],inc[MAXN];
string s[MAXN];
vector<int> vec[MAXN];
map<vector<int>,vector<string> > mp;
map<vector<int>,vector<string> >::iterator it;

int getLcp(string &s1,string &s2)
{
	int ret=0;
	while(ret<m&&s1[ret]==s2[ret])
		ret++;
	return ret;
}

int getPar(int x,int dis)
{
	for(int i=0;(1<<i)<=dis;i++)
	{
		if(dis&(1<<i))
			x=p[x][i];
	}
	return x;
}
ll cal()
{
	if(m==1)
		return 1ll*n*(n-1)/2;
	sort(s,s+n);
	ll ret=0;
	int cnt=0,i,j,c,l,r,lef,rig,mid;
	memset(tr[0],0,sizeof(tr[0]));
	vec[0].clear();
	for(i=0;i<n;i++)//倒序存入字典树 
	{
		vec[0].push_back(i);
		int v=0;
		for(j=m-1;j>=0;j--)
		{
			c=s[i][j]-'a';
			if(!tr[v][c])
			{
				tr[v][c]=++cnt;
				p[cnt][0]=v;
				vec[cnt].clear();
				memset(tr[cnt],0,sizeof(tr[cnt]));
			}
			v=tr[v][c];
			vec[v].push_back(i);
		}
		dest[i]=v;
	}
	for(i=1;i<=cnt;i++)
		sort(vec[i].begin(),vec[i].end());
	for(j=1;j<MAXG;j++)
		for(i=1;i<=cnt;i++)
			p[i][j]=p[p[i][j-1]][j-1];
	for(i=0;i<n;i++)
	{
		inc[m-1]=m-1;
		for(j=m-2;j>=0;j--)
		{
			if(s[i][j]<=s[i][j+1])
				inc[j]=inc[j+1];
			else
				inc[j]=j;
		}
		l=i+1;
		for(int lcp=m-1;lcp>=0;lcp--)
		{
			lef=l;rig=n-1;
			while(lef<=rig)
			{
				mid=(lef+rig)>>1;
				int tmp=getLcp(s[i],s[mid]);
				if(tmp<lcp)
					rig=mid-1;
				else
					lef=mid+1;
			}
			r=lef;
			if(l==r)
				continue;
			int p=getPar(dest[i],inc[lcp]+1);
			ret+=lower_bound(vec[p].begin(),vec[p].end(),r)
				-lower_bound(vec[p].begin(),vec[p].end(),l);
			l=r;
		}
	}
	return ret;
}

int main()
{
	ios::sync_with_stdio(false);
	int i,j;
	ll ans;
	cin>>n;
	for(i=0;i<n;i++)
	{
		cin>>s[i];
		vector<int> tmp(26,0);
		for(j=0;j<s[i].length();j++)
			tmp[s[i][j]-'a']++;
		mp[tmp].push_back(s[i]);
	}
	m=s[0].length();
	ans=1337ll*n*(n-1)/2;
	for(it=mp.begin();it!=mp.end();++it)
	{
		n=0;
		for(i=0;i<it->second.size();i++)
			s[n++]=(it->second)[i];
		ans+=(2ll-1337ll)*n*(n-1)/2;
		ans-=cal();
	}
	cout<<ans<<endl;
}
  • 7
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值