Intel Code Challenge Elimination Round (Div.1 + Div.2, combined) F

题意:

有n个序列,,保证每个序列中最大的数字不超过m,每个序列长度为ki,出现的数字不重复,每个时刻,将每个序列最前面的那个数字拿出来,依此排列,组成一个数组,对于i∈[1,m],每次要求记录最长的全部由i组成的子段的长度,,然后,每个序列左移一位,即第i个位置的数变成第i-1个位置的数,第1个位置的数变成第n个位置。最后,对于每个数字,输出所有时刻中,能取到的最长子段的长度。

范围n,m <= 1E5,ki <= 40,∑ki <= 2E5,操作一共执行10^100次


solution:

这个10^100等于告诉你,,,每种情况都能试一次。。。

对于第i个序列的第j个数,它出现在数组中的时刻s,一定满足s ≡ j - 1 (mod ki),那么连续的相邻的序列中,我们都要取这个数字(因为这样才能满足想找的子段尽可能长),就相当于判断一个线性同余方程组是否有解。

先预处理出每个数字能有的期望连续区间。。

假设序列[i,j]都存在数字x,那么等于我们要判断从i到j的一系列方程组有无解。。

预处理t[i][j] = [i,i+2^j-1]组成的方程组,或标记其无解

那么对于每个位置i,不断地用倍增以及合并方程的手段尽可能地右移,找到最右端点即可

令len = j - i + 1,∑len = ∑ki <= 2E5

因此总复杂度O(nlognlogLCM)

事实上,,可以证(suan)明(chu)LCM这个东西,,小于10^16





哦不,,,这题并没有那么容易。。就算上述过程全部正确也还是会wa。。。。

事实上用扩展欧几里得算法求出合并方程所需要的逆元的时候,,这个逆元可能偏大,,然后一乘就爆long long了。。。

观察式子ax + by = g,,,如果让a < b,那么算出来的x也能小一点。。。

执行合并前特判一下防止爆long long。。。。。

因为这个调了一整天。。。。。。。。。。。。。。。




此题还有一种解法。。事实上,,出现的同余方程的种类并没有很多

O(n^4logn)预处理每个方程和哪些冲突

对于当前要处理的模方程组,,用头尾指针弄一下,,,既然你预处理了哪些会冲突。。

瞎搞一下就行了

#include<iostream>  
#include<cstdio>  
#include<cmath>  
#include<vector>  
#include<cstring>
#include<cstdlib>  
using namespace std;  
  
typedef long long LL;  
const int maxn = 1E5 + 10;  
  
struct data{  
    int num,pos,len;  
    data(){}  
    data(int num,int pos,int len): num(num),pos(pos),len(len){}  
}d[maxn];  
  
int n,m;  
LL r[maxn][20],a[maxn][20];  
  
vector <data> v[maxn];  
  
void gcd(LL a,LL &x,LL b,LL &y,LL &g)  
{  
    if (!b) {  
        x = 1;   
        y = 0;  
        g = a;  
        return;  
    }  
    gcd(b,y,a%b,x,g);  
    y -= x*(a/b);
}    
  
void Merge(LL r1,LL a1,LL r2,LL a2,LL &R,LL &A)  
{  
    LL x,y,g;  
    if (a1 > a2) swap(r1,r2),swap(a1,a2);
    gcd(a2,x,a1,y,g);  
    if ((r1 - r2) % g) {A = -1; return;}    
    x = (r1 - r2)/g*(x%(a1/g))%(a1/g);
    A = a1/g*a2;
    R = (a2*x%A + r2 + A)%A;
}  
  
void Pre_Work(int tot)
{
    for (int i = 1; i <= tot; i++) r[i][0] = d[i].pos,a[i][0] = d[i].len;  
    for (int j = 1; j < 20; j++)  
        for (int i = 1; i <= tot; i++) {  
            if (i + (1<<j) - 1 > tot) {a[i][j] = -1; continue;}  
            int p = i + (1<<(j-1));  
            LL r1 = r[i][j-1],a1 = a[i][j-1];  
            LL r2 = r[p][j-1],a2 = a[p][j-1];  
            if (a1 == -1 || a2 == -1) {a[i][j] = -1; continue;}  
            LL R,A;  
            Merge(r1,a1,r2,a2,R,A);  
            r[i][j] = R;  
            a[i][j] = A;  
        }  
}
  
int Work(int tot)  
{  
	Pre_Work(tot);
    int ret = 1;  
    for (int i = 1; i < tot; i++) {  
        int now = 1,pos = i + 1;  
        LL r1 = r[i][0],a1 = a[i][0];  
        for (int j = 19; j >= 0; j--) {  
            if (pos > tot) break;  
            if (a[pos][j] == -1) continue;  
            LL r2 = r[pos][j],a2 = a[pos][j];  
            LL R,A;  
            Merge(r1,a1,r2,a2,R,A);  
            if (A == -1) continue;  
            now += (1<<j);  
            pos += (1<<j);  
            r1 = R; a1 = A;  
        }  
        ret = max(ret,now);  
    }  
    return ret;  
}  
  
int Solve(int now)  
{  
    int ret = 0,tot = 0;  
    for (int i = 0; i < v[now].size(); i++) {  
        data k = v[now][i];  
        if (!tot || d[tot].num + 1 == k.num) d[++tot] = k;  
        else ret = max(ret,Work(tot)),d[tot = 1] = k;  
    }  
    if (tot) ret = max(ret,Work(tot));  
    return ret;  
}  
  
int main()  
{  
    //freopen("DMC.txt","r",stdin);
    cin >> n >> m;  
    for (int i = 1; i <= n; i++) {  
        int k; scanf("%d",&k);  
        for (int j = 0; j < k; j++) {  
            int x; scanf("%d",&x);  
            v[x].push_back(data(i,j,k));  
        }  
    }  
    for (int i = 1; i <= m; i++) 
    	printf("%d\n",Solve(i));
    return 0;  
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值