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;
}