[题解]Codeforces Round #519 - D. Mysterious Crime

【题目】

D. Mysterious Crime

【描述】

有m个n排列,求一共有多少个公共子段。

数据范围:1<=n<=100000,1<=m<=10

【思路】

对于第一个排列来说,如果第k个位置开始往后L长的子段是一个公共的子段,那么从k开始往后数1,2,...,L-1长的子段都是公共的子段;如果第k个位置开始往后L长的子段是一个公共的子段,但第k个位置开始往后L+1长的子段不是一个公共的子段,那么位置k到位置k+L中任一位置j开始往后直到位置k+L的子段都不是公共的子段。这就意味着公共的子段被划分成了若干个部分,每个部分一定有最长的一个公共子段。对于一个最长的公共子段,不妨设其长度为L,则与它划分在同一组内的公共子段也就是它的子段,长度为1的有L个,长度为2的有L-1个…… 于是这一组一共有1+2+...+L=(L+1)*L/2个公共子段。

用一个数组pos[x][i]=j表示数字x在第i个排列中是第j个。要判断第k个位置的数是否还跟前面是在同一组,就需要判断前面那一组的开始(设为第p个位置)处的数和第k个位置处的数在m个排列中的相对位置是否都一样,即是不是都相差k-p,做一次检查需要O(m)。而由于公共子段的划分是不重合的(即没有一个公共子段属于一个以上的组),于是只需要从前往后扫一遍:从i开始向后扩展公共子段,当新的位置不再属于前一个组时,起始位置i跳到这个新的位置继续重复之前的操作。于是总的复杂度为O(n*m)。

(智障的zyy在比赛的时候把上一段加粗处的地方写错了,直接把位置当做这个位置上的数那来算,竟然还过了6组数据orz…… 因为这个智障的问题,再一次跟跑回expert失之交臂…… (年轻时候的zyy真厉害啊…… (说不定这道题做对了也回不了expert呢orz…… (闭嘴吧……

【我的实现】

复杂度:O(n*m)

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5
 6 using namespace std;
 7 #define MaxN 100020
 8 #define MaxM 20
 9
10 long long pos[MaxN][MaxM];
11 long long a[MaxN];
12 long long Len[MaxN];
13 int n, m;
14 bool Check(int x, int y) //true: same
15 {
16     int delta = pos[x][1] - pos[y][1];
17     for(int i = 2; i <= m; i++)
18         if(pos[x][i] - pos[y][i] != delta)
19             return false;
20     return true;
21 }
22
23 int main()
24 {
25     int i, j;
26     int x;
27     long long Ans;
28     scanf("%d%d", &n, &m);
29     for(i = 1; i <= m; i++)
30     {
31         for(j = 1; j <= n; j++)
32         {
33             scanf("%d", &x);
34             pos[x][i] = j; //x zai i hang j lie
35             if(i == 1)
36                 a[j] = x;
37         }
38     }
39     memset(Len, 0, sizeof(Len));
40     for(i = 1; i <= n; )
41     {
42         Len[i]++;
43         for(j = i+1; j <= n; j++)
44         {
45             if(Check(a[i], a[j]))
46                 Len[i]++;
47             else
48             {
49                 //i = j;
50                 break;
51             }
52         }
53         i = j;
54     }
55     Ans = 0;
56     for(i = 1; i <= n; i++)
57         if(Len[i])
58             Ans += Len[i] * (Len[i] + 1) / 2;
59     cout << Ans << endl;
60     return 0;
61 }

【评测结果】

原文地址:https://www.cnblogs.com/CQBZOIer-zyy/p/9874024.html

时间: 2024-07-30 16:39:48

[题解]Codeforces Round #519 - D. Mysterious Crime的相关文章

Codeforces Round #519 D - Mysterious Crime

题目 题意: 在m组数,每组有n个数(数的范围1-n)中,找到某些序列 使它是每组数的一个公共子序列,问这样的某些序列的个数? 思路: 不难想出答案ans是≥n的. 创立一个next数组,使每组中第i个数的next 是第i+1个数,即 nex[ a[i] ] = a[ i+1 ] (实际上设next是二维数组).对第一组中的第i个数,如果在其余每组的next[ a[ i ] ]都是等于第一组中a[ i+1 ]的,意味着序列 a[ i ],a[ i+1 ]是一个公共子序列.       利用一个数

Codeforces Round #519 by Botan Investments

开个新号打打codeforces(以前那号玩废了),结果就遇到了这么难一套.touristD题被卡掉了(其实是对cf的评测机过分自信),G题没过, 700多行代码,码力惊人. 做了4道,本来想着上蓝名的.然后我第二题挂了,判断循环节写错了.绝望啊---- 比赛传送门:http://codeforces.com/contest/1043 A. Elections 这题很简单,就是说有n个人,每人投k张票,给你或你对手.第i个人会给你的对手投ai票,让你求最少的k.使得你的票数比你的对手多.模拟一下

Codeforces Round #519 by Botan Investments F. Make It One

https://codeforces.com/contest/1043/problem/F 题意 给你n个数,求一个最小集合,这个集合里面数的最大公因数等于1 1<=n<=3e5 1<=a[i]<=3e5 思路 先考虑什么情况下满足集合中的最大公因数=1? 集合中的每个数没有共同的素因子,即所有素因子并没有包含于选出集合的所有数中,存在结论前7个素因子的乘积为510510,所以可以得出选出的集合大小最大为7 定义dp[i][j]为,集合大小为i,集合最大公因数=j的方案数 dp[i

题解Codeforces Round #595 (Div. 3)(CF1249)

开题1小时(雾)严重影响我的提交以及做题心情..我刚开题就发现有人阿克了.. 实际上这场div3真心简单良心很休闲. A:送分题,先排序,每次枚举一下这个数可以加到哪个集合里,加进去就行. 1 #include<stdio.h> 2 #include<algorithm> 3 #define it register int 4 #define il inline 5 using namespace std; 6 const int N=1000005; 7 int a[N],o[N

题解——Codeforces Round #508 (Div. 2) T2 (构造)

按照题意构造集合即可 注意无解情况的判断 #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <vector> #include <map> using namespace std; int n,sum; int main(){ scanf("%d",&n); if(n==1){ printf

题解——Codeforces Round #508 (Div. 2) T1 (模拟)

依照题意暴力模拟即可A掉 #include <cstdio> #include <algorithm> #include <cstring> #include <set> using namespace std; char s[100100]; int n,k,barrel[30]; int main(){ scanf("%d %d",&n,&k); scanf("%s",s+1); for(int i

题解——Codeforces Round #508 (Div. 2) T3 (贪心)

贪心的选取最优解 然后相减好 记得要开long long #include <cstdio> #include <algorithm> #include <cstring> #include <set> #include <queue> #define int long long using namespace std; int ansa=0,ansb=0,posa=1,posb=1,n,a[1000100],b[1000100]; bool c

题解Codeforces Round #597 (Div. 2)

A:送分,裸的gcd. 1 #include<stdio.h> 2 #define il inline 3 #define it register int 4 int T,a,b,d; 5 il void gcd(int a,int b){ 6 if(!b){d=a;return;} 7 gcd(b,a%b); 8 } 9 il void fr(int &num){ 10 num=0;char c=getchar();int p=1; 11 while(c<'0'||c>'

题解 Codeforces Round #615 (Div. 3) (CF1294)

A:判断一下和是不是3的倍数,由于只加不减,所以还要判断有没有大于和的1/3. 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #define it register int 5 #define ct const int 6 #define il inline 7 using namespace std; 8 int T,a,b,c,n,x; 9 namespace io{ 10 il c