E. Permutation Shift

思路:
原问题等价于把往左挪位,然后最多交换次得到原排列,求有多少种

假设排列表示原排列,表示原排列往右挪位得到的排列,那么当时,对应位置上数字相同的个数为

一个通常能想到的套路就是枚举,然后把排列往左挪位得到排列,然后判断排列能不能通过最多次交换得到原排列。一个很简单的结论就是,排列和排列如果有个位置不同,那么最少需要交换次,最多需要交换次。转化一下就是,排列最小需要有个对应位置的数字相同。

假设有满足这一题的条件,满足条件的的集合,那么排列对应位置上数字相同的个数不小于,又因为当时,对应位置上数字相同的个数为。所以有不等式,而,解得。也就是说最多只有三种情况,当排列对应位置上数字相同的个数不小于时可以直接暴力判断是否满足题目条件。

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5+7,mod=998244353;
vector<int>v;
int cnt[maxn],a[maxn],p[maxn],n,m;
bool vis[maxn];
bool check(int k) {
    int tot(0);
    for(int i=k+1;i<=n;++i) p[++tot]=a[i],vis[i]=false;
    for(int i=1;i<=k;++i) p[++tot]=a[i],vis[i]=false;
    int ans=n,pos;
    for(int i=1;i<=n;++i) {
        if(vis[i]) continue;//i已经交换过了
        --ans;
        pos=i;
        while(!vis[pos]) {
            vis[pos]=true;
            pos=p[pos];
        }
    }
    return ans<=m;
}
inline void solve() {
    cin>>n>>m;
    int minsize=n-2*m;
    for(int i=0;i<n;++i) cnt[i]=0;
    for(int i=1;i<=n;++i) {
        cin>>a[i];
        cnt[(i-a[i]+n)%n]+=1;
    }
    v.clear();
    for(int i=0;i<n;++i) {
        if(cnt[i]>=minsize&&check(i)) v.emplace_back(i);
    }
    cout<<v.size();
    for(int i:v) cout<<' '<<i;
    cout<<'\n';
}
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int T=1;
    cin>>T;
    while(T--) solve();
    return 0;
}

F. Pairwise Modulo

的下标大小关系不确定,不方便理解,一般的套路拆分成两个公式:

1.

2.

尝试去掉取余来对公式变形:

1.

考虑公式后面的一部分,可以发现对大于等于的数的贡献是,所以可以把所有树状数组下标为的位置加上,然后前面所有的贡献就是树状数组区间的和了。

2.

考虑公式后面的部分,其实可以枚举,然后用树状数组求区间内有多少个数,贡献就是

code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 3e5+7,mod=998244353;
struct node{
    ll t[maxn];
    void add(int p,ll x) {
        while(p<maxn) {
            t[p]+=x;
            p+=(p&-p);
        }
    }
    ll sum(int p) {
        ll res(0);
        while(p>0) {
            res+=t[p];
            p-=(p&-p);
        }
        return res;
    }
}A,B;
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    int n;
    ll ans(0),pre(0);
    cin>>n;
    for(int i=0,x;i<n;++i) {
        cin>>x;
        ans+=1LL*x*i;
        ans+=A.sum(x);
        ans+=pre;
        pre+=x;
        for(int j=x;j<maxn;j+=x) {
            int l=j,r=min(j+x-1,maxn-1);
            ans-=(B.sum(r)-B.sum(l-1))*j;
            A.add(l,-x);//log maxn
        }//log maxn
        B.add(x,1);
        cout<<ans<<' ';
    }//n
    cout<<'\n';
    return 0;
}