E. Permutation Shift(思维题)
长为n的排列,初始为1 2 3 ... n,
你可以初始右移k步,比如右移一步的序列就是2 3 ... n 1,
你有m(0<=m<=n/3)次交换机会,每次可以交换两个任意位置,
给定一个目标排列,
问存在多少种右移偏移量k,
使得序列能通过不超过m次交换,
换成给定的目标排列,输出所有的k
诈骗题,一个显然的事实是,经过一次交换最后有2个位置归位,
所以m次只能最多2m个归位,需要初始情况需要满足至少已经有n/3个归位了,
这样的偏移量不会超过三个,所以找到所有合法的偏移量,暴力判断能不能m步换回来即可
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int t,n,m,a[N],dis[N],par[N];
vector<int>res;
int find(int x){
return par[x]==x?x:par[x]=find(par[x]);
}
int main(){
scanf("%d",&t);
while(t--){
res.clear();
scanf("%d%d",&n,&m);
for(int i=0;i<n;++i){
dis[i]=0;
}
for(int i=0;i<n;++i){
scanf("%d",&a[i]);
a[i]--;
dis[(i-a[i]+n)%n]++;//i需要左挪dis个值归位
}
for(int i=0;i<n;++i){
if(dis[i]>=n/3){
for(int j=0;j<n;++j){
par[j]=j;
}
int cnt=0;
for(int j=0;j<n;++j){
int u=find(a[j]),v=find((j-i+n)%n);//如果j的值左挪i个恰好与aj相同 说明无需改变
if(u!=v)par[v]=u,cnt++;
}
if(cnt<=m){
res.push_back(i);
}
}
}
printf("%d",(int)res.size());
for(auto &v:res)printf(" %d",v);
puts("");
}
return 0;
}
F. Pairwise Modulo(数论)
有一个n(n<=2e5)个不同整数的数组,第i个数为ai(1<=ai<=3e5)
定义p函数为,
对每个i从1到n,输出pi的值
做法很显然,即考虑加入最后一个数时候的新的贡献,
设当前加入的是数值是7,则贡献分为两部分,aj%7和7%aj,
aj%7,即aj-[aj/7]*7,考虑枚举区间范围,
[aj/7]的值从0,1..往上枚举,统计区间内的值的和,
对于aj的部分求和,显然是已经插入的值的总和,
7%aj,即7-[7/aj]*aj,即考虑7内能数出几个aj,
把aj分别插入aj,2aj,3aj...的位置,即可统计区间内的和
对于7的部分求和,显然是(i-1)个7,
所以需要开两棵BIT,分别维护上述两个区间和
#include<bits/stdc++.h>
using namespace std;
const int N=3e5,M=N+10;
typedef long long ll;
int n;
ll a[M],ans,now;
struct BIT{
ll n,tr[M];
void init(int _n){
n=_n;
memset(tr,0,sizeof tr);
}
void add(int x,ll v){
for(int i=x;i<=n;i+=i&-i)
tr[i]+=v;
}
ll sum(int x){
if(x<0)return 0;
ll ans=0;
for(int i=x;i;i-=i&-i)
ans+=tr[i];
return ans;
}
}d[2];
//设当前插入的数是7
//ai%7:ai-[ai/7]*7 枚举[0,7) [7,14)
//7%ai:7-[7/aj]*aj 实际是统计<=7有几个aj 对aj的倍数+=aj
int main(){
scanf("%d",&n);
for(int i=0;i<2;++i){
d[i].init(N);
}
for(int i=1;i<=n;++i){
scanf("%lld",&a[i]);
ans+=now;
for(int j=0;j<=N/a[i];++j){
int l=a[i]*j,r=min((ll)N+1,l+a[i]);//[l,r)
ans-=1ll*(d[0].sum(r-1)-d[0].sum(l-1))*j*a[i];
}
d[0].add(a[i],1);
ans+=1ll*(i-1)*a[i];
ans-=d[1].sum(a[i]);
for(int j=a[i];j<=N;j+=a[i]){
d[1].add(j,a[i]);
}
now+=a[i];
printf("%lld%c",ans," \n"[i==n]);
}
return 0;
}