C.Weird Sum
题目大意:给你一个
    
     
      
       
        n
       
       
        ∗
       
       
        m
       
      
      
       n*m
      
     
    n∗m的方格,每个格子上都有一个数字,求相同数字之间的曼哈顿距离之和。
 分析:我们考虑每一个数字内部对答案的贡献,不难发现贡献来源于横坐标和纵坐标两部分,而且两部分互不干涉,因此我们可以采用加法原理,对于一个数的某一坐标上的贡献,我们可以采用前缀和的思想来快速计算答案,具体来说,我们可以先计算这个数在这个坐标的总和记为
    
     
      
       
        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
      
     
    (tot−∑j=1i−1ai)−(cnt−i+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(x≤y)使得
    
     
      
       
        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
      
     
    j−1位均相同)有以下两种情况:
 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∗(n−j−1)!/Π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∗(n−j−1)!/Π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;
}









