睡眠不足啊,午睡也没补好,马上迎来又一场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】