本文的解析由杭州二中信奥队提供,有删改
小 D 有一个长度为 n 的整数序列 ai(下标从 1 开始编号,下同),她想通过若干次操作把它变成序列 bi。
小 D 有 m种可选的操作,第 i 种操作可使用三元组 (ti,ui,vi) 描述:若 ti=1,则她可以使 aui 与 avi 都加一或都减一;若 ti=2,则她可以使 aui-1、avi+1,或是 aui+1、avi-1,因此当 ui=vi 时,这种操作相当于没有操作。
小 D 可以以任意顺序执行操作,且每种操作都可进行无限次。现在给定序列与所有操作,请你帮她判断是否存在一种方案能将 ai 变为 bi。题目保证两个序列长度都为 n。若方案存在请输出 YES
,否则输出 NO
。
从 sequence.in 读入数据。
本题输入文件包含多组数据。
第一行一个正整数 T 表示数据组数。对于每组数据:
第一行两个整数 n,m,表示序列长度与操作种数。
第二行 n 个整数表示序列 ai。
第三行 n 个整数表示序列 bi。
接下来 m 行每行三个整数 ti,ui,vi,第 i 行描述操作 i。
注意:同一个三元组 (ti,ui,vi) 可能在输入中出现多次。
输出到文件 sequence.out 中。
对于每组数据输出一行一个字符串 YES
或 NO
表示答案。
3
1 1
1
3
1 1 1
2 3
1 2
4 5
1 1 2
2 1 2
1 1 2
3 3
1 2 3
5 5 4
1 1 2
1 1 3
2 2 3
YES
YES
YES
第一组数据:使用一次操作 1。第二组数据:使用三次操作 1。第三组数据:使用三次操作 1,令 a1,a2 都增加 3,再使用一次操作 2,令 a1,a3 都增加 1。
对于测试点 1 - 5:n=2,m=1,ai,bi ≤ 99,u1 ≠ v1,t1=1。
对于测试点 6 - 10:n=2,m=1,ai,bi ≤ 99,u1 ≠ v1,t1=2。
对于测试点 11 - 12:n=2,ai,bi ≤ 99,ui ≠ vi。
对于测试点 13 - 16:ti=2。
对于测试点 17:n,m ≤ 20。
对于测试点 18:n,m ≤ 1000。
对于所有测试点:1 ≤ T ≤ 10,1 ≤ n,m ≤ 105,1 ≤ ai,bi ≤ 109,ti ∈ {1,2},1≤ ui,vi≤ n。
2.0s
256MB
上传c, cpp或pas语言源程序,文件名应依次为sequence.c, sequence.cpp, sequence.pas。
我们令vali =ai - bi ,显然只需要对val数组进行操作,使它变成全0即可。
把第二种操作提取出来,在ui和vi之间连一边,在某个联通块内如果val的和等于0,就可以构造出一组使这个联通块变为全0的方案。我们将其缩点,就变成了没有二操作的情况,这个时候考虑一个联通块是不是二分图,如果是二分图, 只需要判断二分图两边的val之和是否相等,否则判断val之和的奇偶性。构造方案留作思考。
#include
#include
#include
#include
#include
#include
#define R register int
#define re(i,a,b) for(R i=a; i<=b; i++)
#define ms(i,a) memset(a,i,sizeof(a))
#define MAX(a,b) (((a)>(b)) ? (a):(b))
#define MIN(a,b) (((a)<(b)) ? (a):(b))
using namespace std;
typedef long long LL;
template <typename T>
inline void read(T &x){
x=0;
char c=0;
T w=0;
while (!isdigit(c)) w|=c=='-',c=getchar();
while (isdigit(c)) x=x*10+(c^48),c=getchar();
if(w) x=-x;
}
template <typename T>
inline void print(T x) {
if(x<0) putchar('-'),x=-x;
if(x<10) putchar(x+'0');
else print(x/10),putchar(x%10+'0');
}
int const N=1e5+5;
int T,n,m,cnt,flag;
int a[N],b[N],t[N],u[N],v[N],col[N],f[N],dep[N];
LL sum[N],s[2];
vector <int> adj[N];
inline int find(int x) {
return !f[x] ? x : f[x]=find(f[x]);
}
void dfs(int u,int fa) {
s[dep[u]&1]+=sum[u];
for(int i=0; i<adj[u].size(); i++) {
int v=adj[u][i];
if(v==fa) continue;
if(!dep[v]) {
dep[v]=dep[u]+1;
dfs(v,u);
} else if(((dep[u]+dep[v])&1)==0) flag=1;
}
}
int main() {
//freopen("sequence.in","r",stdin);
//freopen("sequence.out","w",stdout);
read(T);
while(T--) {
memset(dep,0,sizeof(dep));
memset(sum,0,sizeof(sum));
memset(f,0,sizeof(f));
cnt=0;
read(n),read(m);
for(int i=1; i<=n; i++) read(a[i]);
for(int i=1; i<=n; i++) read(b[i]);
for(int i=1; i<=m; i++) {
read(t[i]),read(u[i]),read(v[i]);
if(t[i]==2) {
if(find(u[i])!=find(v[i])) {
f[find(u[i])]=find(v[i]);
}
}
}
for(int i=1; i<=n; i++) if(find(i)==i)
col[i]=++cnt;
for(int i=1; i<=n; i++) col[i]=col[find(i)],sum[col[i]]+=(a[i]-b[i]);
for(int i=1; i<=cnt; i++) adj[i].clear();
for(int i=1; i<=m; i++) {
if(t[i]==1) {
adj[col[u[i]]].push_back(col[v[i]]);
adj[col[v[i]]].push_back(col[u[i]]);
}
}
int ans=1;
for(int i=1; i<=cnt; i++) {
if(!dep[i]) {
dep[i]=1;
flag=0;
s[0]=s[1]=0;
dfs(i,0);
if(flag) {
if((s[0]+s[1])&1) {
ans=0;
break;
}
} else {
if(s[0]!=s[1]) {
ans=0;
break;
}
}
}
}
if(ans) printf("YES\n");
else printf("NO\n");
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
给定一个 1 ~ n 的排列 pi,接下来有 m 次操作,操作共两种:
for i = 1 to n-1 :
if p[i] > p[i + 1] :
swap(p[i], p[i + 1])
从文件 bubble.in 中读入数据。
第一行两个整数 n,m,表示排列长度与操作个数。
第二行 n 个整数表示排列 pi。
接下来 m 行每行两个整数 ti,ci,描述一次操作:若 ti=1,则本次操作是交换操作,x=ci;若 ti=2,则本次操作是询问操作,k=ci。
输出到文件 bubble.out 中。
对于每次询问操作输出一行一个整数表示答案。
3 6
1 2 3
2 0
1 1
1 2
2 0
2 1
2 2
0
2
1
0
第一次操作:排列为 {1,2,3},经过 0 轮冒泡排序后为 {1,2,3},0 个逆序对。
第二次操作:排列变为 {2,1,3}。
第三次操作:排列变为 {2,3,1}。
第四次操作:经过 0 轮冒泡排序后排列变为 {2,3,1},2 个逆序对。
第五次操作:经过 1 轮冒泡排序后排列变为 {2,1,3},1 个逆序对。
第六次操作:经过 2 轮冒泡排序后排列变为 {1,2,3},0 个逆序对。
对于测试点 1 ~ 2:n,m ≤ 100。
对于测试点 3 ~ 4:n,m ≤ 2000。
对于测试点 5 ~ 6:交换操作个数不超过 100。
对于所有测试点:2 ≤ n,m ≤ 2 × 105,ti ∈ {1,2},1 ≤ x < n,0 ≤ k < 231。
1.0s
256MB
上传c, cpp或pas语言源程序,文件名应依次为bubble.c, bubble.cpp, bubble.pas。
定义 v i v_i vi表示第 i i i个数之前大于 p i p_i pi的数的个数,逆序对数是 v i v_i vi的和。
每经过一轮冒泡排序,如果 v i v_i vi不为0,就会有一个大于 p i p_i pi的数从它前面到它后面,经过k轮冒泡排序后, v i v_i vi会变成 m a x ( 0 , v i − k ) max(0,v_i- k) max(0,vi−k)。
我们考虑当前序列经过 0 0 0 - n − 1 n- 1 n−1次冒泡的答案如何高效维护。先考虑交换 p i p_i pi和 p p pi+1时对 v i v_i vi和 v v vi+1的影响:
我们再考虑 v v v的变化对答案产生的影响:对于一个 v i v_i vi=x,经过 k k k轮冒泡排序后,它产生的贡献是
m a x ( 0 , x − k ) max(0,x - k) max(0,x−k),如果变成了 v i = x + 1 v_i=x+1 vi=x+1,经过 0 − x 0 - x 0−x 轮冒泡排序的答案都要加一,变成 v i = x − 1 v_i=x- 1 vi=x−1类似,需要的操作只有区间加减一和单点查询,用树状数组即可。
树状数组总结:点击这里
#include
#include
#include
#include
#include
#include
#define R register int
#define re(i,a,b) for(R i=a; i<=b; i++)
#define ms(i,a) memset(a,i,sizeof(a))
#define MAX(a,b) (((a)>(b)) ? (a):(b))
#define MIN(a,b) (((a)<(b)) ? (a):(b))
using namespace std;
typedef long long LL;
template <typename T>
inline void read(T &x){
x=0;
char c=0;
T w=0;
while (!isdigit(c)) w|=c=='-',c=getchar();
while (isdigit(c)) x=x*10+(c^48),c=getchar();
if(w) x=-x;
}
template <typename T>
inline void print(T x) {
if(x<0) putchar('-'),x=-x;
if(x<10) putchar(x+'0');
else print(x/10),putchar(x%10+'0');
}
template <typename T>
inline void print(T x,char t) {
print(x);
putchar(t);
}
int const N=2e5+5;
int n,m,now;
int p[N],val[N],cnt[N];
LL sum;
LL f[N];
inline int lowbit(int x) {
return x & -x;
}
inline void add(int x,LL y) {
++x;
while(x<=n) {
f[x]+=y;
x+=lowbit(x);
}
}
LL query(int x) {
++x;
LL ans=0;
while(x) {
ans+=f[x];
x=x^lowbit(x);
}
return ans;
}
int main() {
//freopen("bubble.in","r",stdin);
//freopen("bubble.out","w",stdout);
read(n),read(m);
for(int i=1; i<=n; i++) read(p[i]);
for(int i=1; i<=n; i++) {
val[i]=i-1-query(p[i]-2);
sum+=val[i];
add(p[i]-1,1);
++cnt[val[i]];
}
memset(f,0,sizeof(f));
add(0,sum);
now=n;
for(int i=0; i<n-1; i++) {
now-=cnt[i];
add(i+1,-now);
}
for(int i=1; i<=m; i++) {
int opt,x;
read(opt),read(x);
if(opt==1) {
if(p[x]<p[x+1]) {
swap(val[x],val[x+1]);
++val[x+1];
add(0,1);
add(val[x+1],-1);
swap(p[x],p[x+1]);
} else {
swap(val[x],val[x+1]);
add(0,-1);
add(val[x],1);
--val[x];
swap(p[x],p[x+1]);
}
}
if(opt==2) {
if(x>=n) print(0,'\n');
else print(query(x),'\n');
}
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
给定一个长度为 n 的正整数序列 ai,下标从 1 开始编号。我们将该序列视为一个首尾相邻的环,更具体地,对于下标为 i,j(i ≤ j) 的两个数 ai,aj,它们的距离为 min(j-i,i+n-j)。
现在再给定 m 个整数 k1,k2,…,km,对每个 ki(i=1,2,…,m),你需要将上面的序列 ai 重新排列,使得环上任意两个距离为 ki 的数字的乘积之和最大。
从文件ring.in中读入数据。
第一行两个正整数 n,m,表示序列长度与询问数。
接下来一行 n 个正整数表示 ai。
接下来 m 行每行一个非负整数表示 ki。
输出到文件ring.out中。
共 m 行,每行一个整数表示答案。
6 3
1 2 3 4 5 6
0
1
2
91
82
85
ki=0 时:答案为每个数平方的和。
ki=1 时:一种最优方案:{3,1,2,4,6,5}。
答案为 3 × 1 + 1 × 2 + 2 × 4 + 4 × 6 + 6 × 5 + 5 × 3 = 82。
ki=2 时:一种最优方案:{3,6,1,4,2,5}。
答案为 3 × 1 + 1 × 2 + 2 × 3 + 6 × 4 + 4 × 5 + 5 × 6 = 85。
6 1
1 2 3 4 5 6
3
88
附加说明:样例当 k=3 时,一个合法的排列是 1,5,3,2,6,4,答案为 88。注意这里答案不是 44。
对于所有测试数据:1 ≤ m ≤ n ≤ 2 × 105,0 ≤ k ≤ ⌊n/2⌋,1 ≤ ai ≤ 105。
每个测试点的具体限制见下表:
测试点编号 | n ≤ | 特殊性质 |
---|---|---|
1 | 10 | 无 |
2 | 18 | 无 |
3 | 36 | n 为偶数且 m=1,k=2 |
4,5 | 1000 | m ≤ 10,k=1 |
6 | 50 | m ≤ 10,k ≤ 2 |
7,8 | 3000 | 无 |
9,10 | 2 × 105 | 无 |
2.0s
256MB
上传c, cpp或pas语言源程序,文件名应依次为ring.c, ring.cpp, ring.pas。
对一个 k = x k= x k=x的答案,和对 k = g c d ( x , n ) k= gcd(x, n) k=gcd(x,n)的答案是一样的, 200000 以内因数个数最多的数有160个因数,只需要算出这160个因数的答案即可。
一个因数 d d d可以把 n n n个位置拆成 n / d n/d n/d组,组与组之间相互独立,每组都相当于有 d d d个空位, k = 1 k= 1 k=1的问题。打表发现最大的 d d d个数会被分在一组,次大的 d d d个数分在一组,以此类推。一组内的 d d d个数从大到小依次为 a 1 , a 2 , … … , a d a_1,a_2,……,a_d a1,a2,……,ad,最优方案是 … a 7 , a 5 , a 3 , a 1 , a 2 , a 4 , a 6 … …a_7,a_5,a_3,a_1,a_2,a_4,a_6… …a7,a5,a3,a1,a2,a4,a6…。对每个因数都求一下就行了。
#include
#include
#include
#include
#include
#include
#define R register int
#define re(i,a,b) for(R i=a; i<=b; i++)
#define ms(i,a) memset(a,i,sizeof(a))
#define MAX(a,b) (((a)>(b)) ? (a):(b))
#define MIN(a,b) (((a)<(b)) ? (a):(b))
using namespace std;
typedef long long LL;
template <typename T>
inline void read(T &x){
x=0;
char c=0;
T w=0;
while (!isdigit(c)) w|=c=='-',c=getchar();
while (isdigit(c)) x=x*10+(c^48),c=getchar();
if(w) x=-x;
}
template <typename T>
inline void print(T x) {
if(x<0) putchar('-'),x=-x;
if(x<10) putchar(x+'0');
else print(x/10),putchar(x%10+'0');
}
template <typename T>
inline void print(T x,char t) {
print(x);
putchar(t);
}
int const N=2e5+5;
int n,m;
int a[N];
LL ans[N];
inline int gcd(int a,int b) {
return b ? gcd(b,a%b) : a;
}
LL solve(int d) {
if(ans[d]) return ans[d];
if(d==1) {
for(int i=1; i<=n; i++) ans[d]+=1LL*a[i]*a[i];
return ans[d];
}
if(d==2) {
for(int i=1; i<=n; i+=2) ans[d]+=1LL*a[i]*a[i+1];
ans[d]<<=1;
return ans[d];
}
for(int i=n; i>=1; i-=d) {
if(d&1) {
ans[d]+=1LL*a[i]*a[i-1];
ans[d]+=1LL*a[i]*a[i-2];
for(int j=i-1; j-2>=i-d+1; j--) ans[d]+=1LL*a[j]*a[j-2];
ans[d]+=1LL*a[i-d+1]*a[i-d+2];
} else {
ans[d]+=1LL*a[i]*a[i-1];
ans[d]+=1LL*a[i]*a[i-2];
for(int j=i-1; j-2>=i-d+2; j--) ans[d]+=1LL*a[j]*a[j-2];
ans[d]+=1LL*a[i-d+1]*a[i-d+2];
ans[d]+=1LL*a[i-d+1]*a[i-d+3];
}
}
return ans[d];
}
int main() {
//freopen("ring.in","r",stdin);
//freopen("ring.out","w",stdout);
read(n),read(m);
for(int i=1; i<=n; i++) read(a[i]);
sort(a+1,a+n+1);
for(int i=1; i<=m; i++) {
int x;
read(x);
print(solve(n/gcd(n,x)),'\n');
}
//fclose(stdin);
//fclose(stdout);
return 0;
}