Codeforces Round #707 实录


睡眠不足啊,午睡也没补好,马上迎来又一场CF。

不做人了,我做鬼。

上次不用样例检验吃了大亏。

这次不管怎么样,都用样例检验一遍。

【A】

模拟题。因为有延误的问题,因此不一定按照期望时间表运行。

每次判断下一次出发的时间即可。

【B】

竟然有彩图

区间标记,差分即可。

【C】

PK题?

但这规模有点大啊

感觉只能排序 然后双指针?

设x y z w是排序后的新下标

那么 x<z<w<y

为什么ai不是10^9?

我们倒是可以从ai入手,首先ax+ay=az+aw

变形 ax-az=aw-ay

变成差的问题,范围[1,2.5*10^6]

枚举这个差

但是配对的端点如果也枚举的话就时间不够了

转D

调整法,可以认为区间[x,z]和[w,y]是z<w的,不穿梭

反向差分,求前缀和,那么可以转为求两个相等的区间和。

f[i][x]表示1~i内是否存在区间和==x

那么f[i][x]=f[i-1][x] | f[i][x-b[i]]

 

还是想双指针

排序后,p和q分别在1和2

然后哪一个指针右移造成的增量小,就移动哪个,(但是单步小不能说明什么)

这样会产生一系列配对和,记录它们,然后——

反过来做一遍?

 

首先,对每种数值,记录相应的ai

如果存在两种数值v1和v2,其数组的size的大小都>1

那么取里面的数字下标即可。

否则,最多只有一个数值其size>1

分 有 或 没有 讨论

如果有 我们可以以2*v1为sum,然后扫一遍数组看看有无这样的配对。O(N)

否则和 没有 的情况一同处理

此时四个目标都不相同

再思考相邻数字的差,一共n-1个

假设有两个差相同,且对应的配对中数字不重复,

取它们作为答案

否则

要么所有差都不相等 或 相等的差都是 x y z (x,y) (y,z)的形式

如果所有差都不相等,那么差至少>= 0 1 2 ... n-1

a的max>=1+n*(n-1)/2

而因为ai是<=2.5*10^6的,因此这种情况下n一定是sqrt(10^6)的级别,直接枚举配对即可。

如果有相等的差,且数量>=3个,也可以取为答案,否则最多一个三元形式。

n也是sqrt(10^6)级别的,O(N^2)处理即可。

 

【D】

找循环节

首先,最多n*m次,一定开始循环,最坏情况是可能达到的。两个素数。

首先看n=m的情况

这种情况比较简单,统计一下不同点数量c,然后k%c,就知道是哪一天会暴走。

n!=m,假设n<m

注意a数组都是不同的,b同理

我们其实只要关注k<=n*m的情况就行了。

TMD,感觉我被题目牵着走。

能不能把循环转化成别的东西。

如果把max(n,m)天视为一轮,那么

这里就是m

第一轮我们直接复制n的循环凑齐m

然后计算不同点的数量,第二轮,其实就相当于左移n天的循环?

可是计算不同偏移下的不同点数量似乎也要n*m?

转E

发现一个性质,因为a和b分别都是无重复元素的

因此对于b中的一个元素,最多只有一个a的元素等于它。

它们的日程碰到的时候才产生贡献。

对于b,(注意我们以后就设b的m>a的n,否则我们交换数组a和b)

每个bi,可以确定唯一的aj=bi,记h[i]=j,可以为0

对于一个配对(aj,bi),如果某天同时访问到它们,且,有一个bk,满足j-k=d,

其对应的al 也满足 j-l=d,那么这个配对(al,bk)必然在d天前也被同时访问。

 

对于bi,下一轮访问bi,对应的a指针会偏移 d=m%n

我们可以思考,从第一轮出发,多少次偏移会使得当b指针在bi时

a指针重新回到第一次的bi的对应点。

期间是否偏移到了这个bi的同数值a

这样就可以算出这个 大轮回 中bi产生的贡献

每个bi的大轮回长度是一样的,即lcm(n,m)

我们看k会出现在第几个大轮回内。

如果最后一个大轮回模拟,O(NM)

或者我们O(M)枚举最后的一天是bi(假设)

然后计算对应的——

这命运之轮,让我想起旋转的吊灯,沙皇,我主贝伦,

谁都拥有幸福的权利。

 

讨论剩余系,而不是+1 +1天的观察

对每个b,以相碰为起点观察轮回

对于有对应同数值的a的bi,第一次相碰的天数记为fi

那么,lcm(n,m)后,bi会第二次相碰。

第w天的时候,bi产生的贡献就是 floor((w-fi)/lcm(n,m))

所有这些加起来就是总贡献,若等于k,这就是我们要找的w

这感觉是数论分块。

的确,是个数论题,lcm也有。

因为k关于w的函数显然是单调的,直接二分w。

 

但是最后答案输出与正确答案总是相差1或者2.

没时间DEBUG了,END

发现3题实时排名就300+,这场比较难?还是做的人太少了?

 

我猜我是 寻找第一个t使得t*d%n==c,这个过程出了错。

以下是错误代码:

#include <bits/stdc++.h>
using namespace std;
#define FOR(i,n) for (int i=1;i<=n;i++)
#define REP(i,a,b) for (int i=a;i<=b;i++)

#define pb push_back
#define fi first
#define se second
#define pi pair<int,int>
#define mp make_pair

typedef long long ll;
typedef complex<double> comp;

const int inf=0x3f3f3f3f;
const ll linf=1e18;
const int N=5e5+10;
const double eps=1e-10;
const ll mo=998244353;

int n,m;
ll k;
int a[N],b[N];
int p[2*N];
ll f[N];
ll T;
ll gcd(ll a,ll b) {
    return (b==0)?a:gcd(b,a%b);
}
ll lcm(ll a,ll b) {
    return a/gcd(a,b)*b;
}

ll get(ll d,ll n,ll c) {
    c%=n;
    ll t1=ceil(n/d);
    ll u=t1*d%n;
    if (u==0||c%u) return -1;
    else return t1*c/u;
}
ll get(ll a,ll b) {
    // (t*m)%n==(a-1-b+n)%n;
    ll t;
    if (a==b) t=0;
    else t=get(m,n,a-b+n);
    return t+b;
}
bool check(ll w) {
    ll r=0;
    FOR(i,m) if (p[b[i]]) {
        if (w>f[i]&&f[i]!=-1) r+=(w-f[i])/T;
    }
    if (w-r>=k) return 1;
    else return 0;
}
int main() {

    std::ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    cin>>n>>m>>k;
    FOR(i,n) cin>>a[i];
    FOR(i,m) cin>>b[i];
    if (n>m) {
        swap(n,m);
        FOR(i,max(n,m)) swap(a[i],b[i]);
    }
    FOR(i,n) {
        p[a[i]]=i;
    }
    FOR(i,m) if (p[b[i]]) {
        if (p[b[i]]==i) f[i]=i;
        else f[i]=get(p[b[i]],i);
    }
    T=lcm(n,m);
    ll l=1,r=1e12;
    ll w=0,ans=0;
    while (l<=r) {
        w=(l+r)>>1;
        if (check(w)) {
            ans=w;
            r=w-1;
        } else l=w+1;
    }
    cout<<ans<<endl;
    return 0;
}

  

【E】

把一个东西变为另一个东西,转化题。

首先,你需要判断无解,有解的必要条件:行集合应该一样。

下一次排序为什么不会撤销上一次排序的效果?

效果依然保留的是,那些在这次排序中数值相等但是上一次对应数值不相等的那些。

只剩1h了,回去D

枚举最后一次操作?

【F】


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM