AtCoder Grand Contest 006 C:Rabbit Exercise

题目传送门:https://agc006.contest.atcoder.jp/tasks/agc006_c

题目翻译

数轴上有\(N\)只兔子,从\(1\)\(N\)编号,每只兔子初始位置是\(x_i\)。现在兔子们要开始做运动,运动都有\(M\)个步骤,对于第\(i\)个步骤,我们用\(a_i\)来形容它,意思是:

在当前步骤中,从左至右数第\(a_i\)只兔子将会跳跃。我们在\(a_i-1\)\(a_i+1\)两只兔子中等概率的选择一个兔子,假设我们选择的是\(x\),那么第\(a_i\)只兔子将会跳到数轴上关于\(x\)只兔子的坐标对称的另一个点上。

比如第\(a_i\)只兔子坐标为\(3\),第\(x\)只兔子坐标为\(5\),那么第\(a_i\)只兔子就会跳到\(7\)去。

接下来兔子们要做\(K\)轮这样的运动,请求出最后每个兔子所在坐标的期望。

\(N,M\leqslant 10^5,|x_i|\leqslant 10^9,K\leqslant 10^{18},a_i\in[2,n-1],x_i\)是整数。

题解

这种题目如果想到了就非常好做,可惜我想不到……

假设现在要跳第\(i\)只兔子,那么\(x_i=\frac{1}{2}\times (2*x_{i-1}-x_i)+\frac{1}{2}\times (2*x_{i+1}-x_i)=x_{i-1}+x_{i+1}-x_i\)

原本这三只兔子分别在:\(x_{i-1},x_i,x_{i+1}\)

现在这三只兔子分别在:\(x_{i-1},x_{i-1}+x_{i+1}-x_i,x_{i+1}\)

看起来怪怪的,上下长度不一,所以我们把这俩序列差分一下。

原本三只兔子坐标差分序列:\(x_{i-1},x_i-x_{i-1},x_{i+1}-x_i\)

现在三只兔子坐标差分序列:\(x_{i-1},x_{i+1}-x_i,x_i-x_{i-1}\)

然后我们发现对于一次跳跃,只是交换了差分数组的两个位置而已……

也就是说,我们可以求出在\(M\)个步骤后差分数组的置换,然后根据置换求出每个位置在\(K\)轮运动之后最后是原数组的哪个元素,最后前缀和加起来就是答案了。

时间复杂度:\(O(N+M)\)

空间复杂度:\(O(N)\)

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;

const int maxn=1e5+5;

ll k;
int n,m;
int c[maxn];
bool bo[maxn];
ll a[maxn],ans[maxn];//-1e9关于1e9对称就直接爆int了

int read() {
    int x=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
    return x*f;
}

int main() {
    n=read();
    for(int i=1;i<=n;i++)
        scanf("%lld",a+i),c[i]=i;
    m=read();scanf("%lld",&k);
    for(int i=1;i<=m;i++) {
        int x=read();
        swap(c[x],c[x+1]);
    }
    for(int i=1;i<=n;i++)
        if(!bo[i]) {
            int u=i,cnt=0,step,goal;
            while(!bo[c[u]])
                bo[c[u]]=1,cnt++,u=c[u];//求循环节大小
            step=k%cnt,goal=u=i;
            for(int j=1;j<=step;j++)
                goal=c[goal];
            for(int j=1;j<=cnt;j++)
                ans[u]=goal,u=c[u],goal=c[goal];//u和goal相差step步
        }
    for(int i=2;i<=n;i++) {
        int x=ans[i];
        ans[i]=a[x]-a[x-1];
    }
    ans[1]=a[1];
    for(int i=1;i<=n;i++)
        ans[i]+=ans[i-1],printf("%.1lf\n",(double)ans[i]);
    return 0;
}

转载于:https://www.cnblogs.com/AKMer/p/10027113.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值