NOI online #1 提高组

序列#

题意:

给定数组A,B,有两种操作:

选择两个位置i,j,使得ai,aj都加一或减一

选择两个位置i,j使得ai加一,aj减一,或者反过来。

给出的操作可以做任意次,问A数组能否变成B数组?

n,m105

题解:

考虑操作二,因为可以在里面任意分配权值,可以缩点。

然后把操作一对应的点连边,对于任意连通块:

如果是二分图,那么左部点和右部点的权值和之差不变,必须为0

如果不是二分图,那么连通块内aibi点数和必须是偶数。

Copy
#include<bits/stdc++.h> using namespace std; namespace red{ #define int long long #define ls(p) (p<<1) #define rs(p) (p<<1|1) #define mid ((l+r)>>1) #define lowbit(i) ((i)&(-i)) const int N=1e5+10; int n,m; int a[N],b[N],s[N],c[5]; int f[N],col[N]; vector<int> eg[N]; struct node { int x,y; }q[N]; int num; inline int find(int k){return f[k]==k?k:f[k]=find(f[k]);} inline void init() { num=0; for(int i=1;i<=n;++i) { eg[i].clear(); f[i]=i; s[i]=col[i]=0; } } inline bool dfs(int now,int cc) { col[now]=cc; c[cc]+=s[now]; bool flag=1; for(int t:eg[now]) { if(col[t]==col[now]) flag=0; if(!col[t]&&!dfs(t,3-cc)) flag=0; } return flag; } inline void main() { int skx;cin>>skx; while(skx--) { cin>>n>>m; init(); for(int i=1;i<=n;++i) cin>>a[i]; for(int i=1;i<=n;++i) cin>>b[i]; for(int i=1;i<=m;++i) { int opt,x,y; cin>>opt>>x>>y; if(opt==1) q[++num].x=x,q[num].y=y; else f[find(x)]=find(y); } for(int i=1;i<=n;++i) { s[find(i)]+=a[i]-b[i]; } for(int i=1;i<=num;++i) { int x=find(q[i].x),y=find(q[i].y); eg[x].emplace_back(y); eg[y].emplace_back(x); } bool flag=1; for(int i=1;i<=n;++i) if(!col[i]&&find(i)==i) { c[1]=c[2]=0; bool ok=dfs(i,1); if(ok&&c[1]!=c[2]) {flag=0;break;} if(!ok&&(c[1]^c[2])&1) {flag=0;break;} } if(flag) cout<<"YES\n"; else cout<<"NO\n"; } } } signed main() { red::main(); return 0; } /* */

冒泡排序#

题意:

给定一个排列P

有两个操作:

p[x],p[x+1]交换位置。

问这个排列做k次冒泡排序后的逆序对数。

n,m105

题解:

假设一个序列的逆序对数是:

b1,b2,b3,bn

那么做一个冒泡排序后的逆序对数就是:

b21,b31,,bn1,0

当然,和0max

有了这个性质之后怎么做呢,其实不用真的去移位,因为第一位的逆序对数肯定<=1,等于是把第一位剪掉就行了。

b11,b21,b31,,bn1

k次之后就是

b1k,b2k,b3k,,bnk

所有项和0max,然后求和,显然可以树状数组。

Copy
#include<bits/stdc++.h> using namespace std; namespace red{ #define int long long #define ls(p) (p<<1) #define rs(p) (p<<1|1) #define mid ((l+r)>>1) #define lowbit(i) ((i)&(-i)) const int N=3e5+10; int n,m; int a[N],p[N]; struct BIT { int tr[N]; inline void update(int x,int k) { for(int i=x;i<=n;i+=lowbit(i)) tr[i]+=k; } inline int query(int x) { int sum=0; for(int i=x;i>=1;i-=lowbit(i)) sum+=tr[i]; return sum; } }T[2]; inline void main() { cin>>n>>m; for(int i=1;i<=n;++i) { cin>>p[i]; T[0].update(p[i],1); a[i]=i-T[0].query(p[i]); } for(int i=1;i<=n;++i) T[0].tr[i]=0; for(int i=1;i<=n;++i) T[0].update(n-a[i],1),T[1].update(n-a[i],a[i]); for(int i=1;i<=m;++i) { int opt,x; cin>>opt>>x; if(opt==1) { T[0].update(n-a[x],-1); T[1].update(n-a[x],-a[x]); T[0].update(n-a[x+1],-1); T[1].update(n-a[x+1],-a[x+1]); if(p[x]>p[x+1]) --a[x+1]; swap(p[x],p[x+1]); swap(a[x],a[x+1]); if(p[x]>p[x+1]) ++a[x+1]; T[0].update(n-a[x],1); T[1].update(n-a[x],a[x]); T[0].update(n-a[x+1],1); T[1].update(n-a[x+1],a[x+1]); } else { int sum=T[0].query(n-x); int val=T[1].query(n-x); cout<<val-x*sum<<'\n'; } } } } signed main() { red::main(); return 0; } /* */

最小环#

题意:

n个数字,每次给一个数字k,要求把n个数字排成一个环,让环上所有距离为k的两个数字的乘积的和最大。

n,m2105,kn2

题解:

每个数字k会把整个环分成几个不相交的小环,为了让积之和最大,我们尽量让大的乘大的,小的乘小的,所以最大的数字都应该分给同一个环,然后类推。

在一个环内怎么排列呢?我们先把最大的数字放进去,然后去考虑第二大的,让当前数字尽量挨着大的数字放:

0,0,6,0,0,00,0,6,5,0,00,4,6,5,0,00,4,6,5,3,02,4,6,5,3,02,4,6,5,3,1

大概是以上模式插入,其中首尾是相接的。

数字k是怎么把长度为n的环分割的呢?其实就是分成个环,每个长度是ngcd(n,k)

所以不同的情况其实只有d(n)种,其中d(n)n的约数个数。

所以时间复杂度O(nd(n))O(nn)

还可以更优,注意对于只有一个环的情况,其他情况和它的区别只在两个环的交界处,对于g个环,只要O(ng)的时间算一下交界。

总复杂度g|nngnHnHn是调和级数。约等于O(nlogn)

Copy
#include<bits/stdc++.h> using namespace std; namespace red{ #define int long long #define ls(p) (p<<1) #define rs(p) (p<<1|1) #define mid ((l+r)>>1) #define lowbit(i) ((i)&(-i)) const int N=3e5+10; int n,m; int a[N]; int st[2][N],top[2]; int ret[N]; inline void main() { ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); cin>>n>>m; int sum=0; for(int i=1;i<=n;++i) { cin>>a[i]; sum+=a[i]*a[i]; } sort(a+1,a+n+1); for(int i=1;i<=m;++i) { int k;cin>>k; if(!k) { cout<<sum<<'\n'; continue; } int t=__gcd(n,k); int d=n/t; if(ret[d]) { cout<<ret[d]<<'\n'; continue; } for(int j=0;j<t;++j) { int l=j*d+1,r=(j+1)*d; top[0]=top[1]=0; st[0][++top[0]]=a[r]; st[1][++top[1]]=a[r--]; while(r>l) { int b=0; if(st[0][top[0]]<st[1][top[1]]) b=1; ret[d]+=a[r]*st[b][top[b]]; st[b][++top[b]]=a[r--]; } ret[d]+=a[l]*(st[0][top[0]]+st[1][top[1]]); } cout<<ret[d]<<'\n'; } } } signed main() { red::main(); return 0; } /* */
posted @   lovelyred  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示
CONTENTS