Codeforces Round #775 (Div. 2)

C.Weird Sum

题目大意:给你一个 n ∗ m n*m nm的方格,每个格子上都有一个数字,求相同数字之间的曼哈顿距离之和。
分析:我们考虑每一个数字内部对答案的贡献,不难发现贡献来源于横坐标和纵坐标两部分,而且两部分互不干涉,因此我们可以采用加法原理,对于一个数的某一坐标上的贡献,我们可以采用前缀和的思想来快速计算答案,具体来说,我们可以先计算这个数在这个坐标的总和记为 t o t tot tot总个数记为 c n t cnt cnt,然后对于每一个数的坐标 a i a_i ai,它与后面所有数的距离和即为 ( t o t − ∑ j = 1 i − 1 a i ) − ( c n t − i + 1 ) ∗ a i (tot-\sum_{j=1}^{i-1}a_i)-(cnt-i+1)*a_i (totj=1i1ai)(cnti+1)ai,画柱状图更有助于理解。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
#define clean(x) memset(x,0,sizeof(x))
#define maxn 100005
#define int long long
using namespace std;

int read()
{
	int x=1,res=0;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-')
		x=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		res=res*10+(c-'0');
		c=getchar();
	}
	return res*x;
}
int n,m;
vector<int>f[maxn],g[maxn];
signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		int x=read();
		f[x].push_back(i);
		g[x].push_back(j);
	}
	int ans=0;
	for(int i=1;i<=1e5;i++){
		sort(f[i].begin(),f[i].end());
		int tot=0,cnt=0;
		for(int j:f[i]){
			tot+=j;cnt++;
		}
		for(int j:f[i]){
			ans+=tot-j*cnt;
			cnt--;tot-=j;
		}
	}
	for(int i=1;i<=1e5;i++){
		sort(g[i].begin(),g[i].end());
		int tot=0,cnt=0;
		for(int j:g[i]){
			tot+=j;cnt++;
		}
		for(int j:g[i]){
			ans+=tot-j*cnt;
			cnt--;tot-=j;
		}
	}
	cout<<ans;
	return 0;
}

D. Integral Array

题目大意:给一序列的数判断这个序列是否满足这样的条件,任选两个数 x , y ( x ≤ y ) x,y(x\leq y) x,y(xy)使得 y / x y/x y/x的值仍为序列中的数, x , y x,y x,y可以是同一个数。
分析:枚举序列中的数作为除数,然后枚举非序列中的数作为商,然后通过除数和商来确定被除数的范围,并用前缀和思想判断这个范围中是否有数存在。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
#define clean(x) memset(x,0,sizeof(x))
#define maxn 1000005
using namespace std;

int read()
{
	int x=1,res=0;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-')
		x=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		res=res*10+(c-'0');
		c=getchar();
	}
	return res*x;
}
int a[maxn],vis[maxn],s[maxn];
int n,c;
void clear()
{
	for(int i=1;i<=c;i++) vis[i]=s[i]=0;
}

void solve()
{
	n=read();c=read();clear();
	for(int i=1;i<=n;i++) a[i]=read(),vis[a[i]]=1;
	sort(a+1,a+1+n);
	for(int i=1;i<=c;i++) s[i]=s[i-1]+vis[i];
	int tott=unique(a+1,a+1+n)-a-1;
	if(a[1]!=1) {
		puts("No");
		return;
	}
	for(int i=2;i<tott;i++){
		for(int j=2;j*a[i]<=a[tott];j++){
			if(vis[j]) continue;
			int l=a[i]*j,r=min(a[tott],a[i]*(j+1)-1);
			if(s[l-1]!=s[r]) {
				puts("No");
				return;
			}
		}
	}
	puts("Yes");
}

int main()
{
	int t=read();
	while(t--)
	solve();
	return 0;
}

E. Tyler and Strings

我们可以考虑对于每个 j j j,(假设前 j − 1 j-1 j1位均相同)有以下两种情况:
1. a j < b j a_j<b_j aj<bj此时我们需要计算所有可能的情况,此时为 ∑ i = 1 k ( c n t i ∗ ( n − j − 1 ) ! / Π i = 1 k ( c n t i ! ) ) \sum _{i=1}^k(cnt_i*(n-j-1)! / \Pi_{i=1}^k (cnt_i!)) i=1k(cnti(nj1)!/Πi=1k(cnti!))
2. a j = b j a_j=b_j aj=bj我们可以用 j + 1 j+1 j+1位的答案来表示
对于1只需要维护每个i的 c n t i ∗ ( n − j − 1 ) ! / Π i = 1 k ( c n t i ! ) cnt_i*(n-j-1)! / \Pi_{i=1}^k (cnt_i!) cnti(nj1)!/Πi=1k(cnti!)即可,每次修改只需要对区间进行进行操作,可以使用线段树维护区间和来实现。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
#define clean(x) memset(x,0,sizeof(x))
#define maxn 1000005
#define mod 998244353
#define int long long
using namespace std;

int read()
{
	int x=1,res=0;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-')
		x=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		res=res*10+(c-'0');
		c=getchar();
	}
	return res*x;
}
struct ST{
	int tag[maxn<<2],tr[maxn<<2],a[maxn];
	void build(int k,int l,int r)
	{
		tag[k]=1;
		if(l==r)
		{
			tr[k]=a[l]%mod;
			return;
		}
		int mid=(l+r)>>1;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
		tr[k]=(tr[k<<1]+tr[k<<1|1])%mod;
	}
	void pushdown(int k,int l,int r,int mid)
	{
		tr[k<<1]=tag[k]*tr[k<<1]%mod;
		tr[k<<1|1]=tag[k]*tr[k<<1|1]%mod;
		tag[k<<1]=(tag[k<<1]*tag[k])%mod;
		tag[k<<1|1]=(tag[k<<1|1]*tag[k])%mod;
		tag[k]=1;
	}
	void modify(int k,int l,int r,int x,int y,int val)
	{
		if(x<=l&&r<=y) 
		{
			tag[k]=(tag[k]*val)%mod;
			tr[k]=tr[k]*val%mod;
			return;
		}
		int mid=(l+r)>>1;
		if(tag[k]) pushdown(k,l,r,mid);
		if(x<=mid) modify(k<<1,l,mid,x,y,val);
		if(mid+1<=y) modify(k<<1|1,mid+1,r,x,y,val);
		tr[k]=(tr[k<<1]+tr[k<<1|1])%mod;
	}
	int query(int k,int l,int r,int x,int y)
	{
		if(x<=l&&r<=y)	return tr[k];
		int mid=(l+r)>>1,ans=0;
		if(tag[k]) pushdown(k,l,r,mid);
		if(x<=mid) ans=(ans+query(k<<1,l,mid,x,y))%mod;
		if(mid+1<=y) ans=(ans+query(k<<1|1,mid+1,r,x,y))%mod;
		return ans;
	}
}st;
int pd,n,m,x,y,ans;
int a[maxn],b[maxn],cnt[maxn],jc[maxn],inv[maxn],vis[maxn];
void exgcd(int a,int b)
{
	if(b==0)
	{
		x=1;y=0;
		return;
	}
	exgcd(b,a%b);
	int temp=x;
	x=y;y=temp-a/b*y;
	return;
}

signed main()
{
	n=read();m=read();
	for(int i=1;i<=n;i++) a[i]=read(),cnt[a[i]]++;
	for(int i=1;i<=m;i++) b[i]=read();
	jc[0]=1;inv[0]=1;inv[1]=1;jc[1]=1;
	for(int i=2;i<=n;i++){
		jc[i]=(jc[i-1]*i)%mod;
		exgcd(jc[i],mod);
		inv[i]=(x%mod+mod)%mod;
	}
	sort(a+1,a+1+n);
	int tott=unique(a+1,a+1+n)-a-1;
	int tot=1;
	for(int i=1;i<=tott;i++) tot=(tot*inv[cnt[a[i]]])%mod;
	for(int i=1;i<=tott;i++){
		st.a[a[i]]=(tot*cnt[a[i]])%mod;
	}
	st.build(1,1,2e5);
	for(int i=1;i<=m;i++){
		if(cnt[b[i]]==0) {
			pd=i;
			if(b[i]==1) break;
			ans=(ans+(jc[n-i]*st.query(1,1,2e5,1,b[i]-1))%mod)%mod;
			break;
		}
		if(b[i]>1) ans=(ans+(jc[n-i]*st.query(1,1,2e5,1,b[i]-1))%mod)%mod;
		if(b[i]>1) st.modify(1,1,2e5,1,b[i]-1,cnt[b[i]]);
		st.modify(1,1,2e5,b[i],b[i],cnt[b[i]]-1);
		if(b[i]<2e5) st.modify(1,1,2e5,b[i]+1,2e5,cnt[b[i]]);
		cnt[b[i]]--;
	}
	if(pd==n+1) ans=(ans+1)%mod;
	cout<<ans;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

snowy2002

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值