loli终于又来hu测了 T2.青蛙(差分+置换)

这里写图片描述
这里写图片描述

分析:
看到k的范围真的大,所以以为要用矩阵乘法,但是1000*1000不是矩阵乘法的数据范围
所以打了个20分就跑了

40%

实际上,由期望的线性性我们可以得到每个跳跃点的期望计算公式:

xi=(2xi1xi)+(2xi+1xi)2=xi1+xi+1xi x i ′ = ( 2 x i − 1 − x i ) + ( 2 x i + 1 − x i ) 2 = x i − 1 + x i + 1 − x i

多么简单的一个公式啊,我们就可以直接模拟,40分到手

70%

我们把进行完一轮后每只青蛙的位置看做是一个序列
虽然k非常大,但是跳跃后能形成的序列个数是达不到1e18级别的
也就是说,序列存在循环节
我们只要找到这个循环节,剩下的几轮暴力模拟即可

看来70分好像也很好拿,还是自己不够认真,不过这个循环节的思路很妙啊
看了一下队友的实现方法,有hash,也有直接比较

//胖爷的code,感觉还是比较优美的
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m;
long long t[100010],d[100010],a[100010],k;
int check()
{
    for(int i=1;i<=n;i++)
    if(a[i]!=d[i]) return 1;
    return 0;
}
int main()
{
    freopen("frog.in","r",stdin);
    freopen("frog.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        d[i]=a[i];
    }
    scanf("%d%lld",&m,&k);
    for(int i=1;i<=m;i++)
    scanf("%lld",&t[i]);
    int now=0;
    do
    {
        now++;
        for(int i=1;i<=m;i++)
        a[t[i]]=a[t[i]-1]+a[t[i]+1]-a[t[i]];
    }
    while(check()&&now!=k);
    if(now==k)
    {
        for(int i=1;i<=n;i++)
        printf("%lld\n",a[i]);
        return 0;
    }
    k%=now;
    while(k--)
    for(int i=1;i<=m;i++)
    a[t[i]]=a[t[i]-1]+a[t[i]+1]-a[t[i]];
    for(int i=1;i<=n;i++)
    printf("%lld\n",a[i]);
}
100%

可以发现每次跳跃后有

xixi1=xi+1xi,xi+1xi=xixi1 x i ′ − x i − 1 = x i + 1 − x i , x i + 1 − x i ′ = x i − x i − 1

也就是说,每次两个差分交换了位置( xi+1xi<=>xixi1 x i + 1 − x i <=> x i − x i − 1
那么一轮就是对差分做了一个置换,要求这个置换的k次幂
我们找到这个置换的每个轮换,对于每个轮换记录k次之后的状态
这样我们就得到了最终的差分序列了,最后还原原序列即可
时间复杂度 O(n) O ( n )

tip

最后的答案要开ll
这道题真的提醒我:对待任何一道题都不能浅尝辄止,一定要好好考虑

概率期望题还是要优先考虑公式
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long

using namespace std;

const double eps=1e-7;
const int N=100005;
int n,m,a[N],d[N],p[N],ans[N];
bool vis[N];
ll k;

int main()
{
    freopen("frog.in","r",stdin);
    freopen("frog.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&d[i]);
    for (int i=n;i>=1;i--) d[i]=d[i]-d[i-1];
    scanf("%d%lld",&m,&k);
    for (int i=1;i<=n;i++) p[i]=i;
    for (int i=1;i<=m;i++) {
        int x;
        scanf("%d",&x);
        swap(p[x],p[x+1]);    //x[i]-x[i-1]=x[i+1]-x[i];
    }
    for (int i=1;i<=n;i++) 
        if (!vis[i]) {
            int cnt=0;
            a[cnt++]=i;       //记录轮换中的元素 
            vis[i]=1;
            int t=p[i]; 
            while (t!=i) {
                a[cnt++]=t;
                vis[t]=1;
                t=p[t];
            }
            for (int i=0;i<cnt;i++)
                ans[a[i]]=a[((ll)i+k)%cnt];    //a[i]的差分 
        }
    ll sum=d[1];
    printf("%lld\n",sum);
    for (int i=2;i<=n;i++) printf("%lld\n",sum+=d[ans[i]]); 
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值