今天的题自认为比Day1简单一些,而且出的质量很高(虽然数据有点水)。
T1 a
期望得分:30
实际得分:30
首先不得不吐槽一下:这题名字起的真糊弄。
说下题吧,一拿到题发现O(n2)暴力就是送的,枚举起始点,再O(n)扫一遍。15分钟敲完后开始打表想想正解,发现对于一个数s[i],他对旋转k次的贡献有一部分是一个等差数列,而有一部分不是,准确说应该是分别有一个递增和递减,公差是1和-1的等差数列 ,以及单出来的一个1。于是我就想找规律,找了30多分钟硬是没找出来,不得不放弃写T2.
T2,T3写完后又会来杠T1,总共用时1小时20分钟,还是只有暴力分……
正解其实和我的思路比较像,但是他找到了等差数列的规律:s[i]对每一个答案的贡献可以分成三个等差数列,然后这道题就变成了区间加一个等差数列,最后求每一个数修改后的值。可以用线段树实现,不过更有的是二阶差分(详见这篇博客),总复杂度O(n)。
但是规律比较复杂,没写出来。
于是我们看第二种方法吧。
强的过分的Dukelv用了不到40分钟AC了这道题,令人震惊。他跟我们说:就是暴力模拟啊!于是就有了一个O(n)暴力模拟的方法。
对于每一次旋转,我们只用考虑旋转后相对于旋转前的答案变了什么,然后不断的维护当前的答案。
因此我们需要一个big:记录当前有几个数s[i] >= i,以及sma:有几个数s[i] < i。然后每一次旋转,先考虑第一个数,因为他的移动方式和其他的都不一样:移到了最后,所以首先是big--, sma++,对答案的贡献少了s[i] - 1,多了n - s[i]。然后考虑剩下的数:有一些的答案由正变负,有一些有负变正。因此我们要开一个数组num[]记录当前有哪些数是负的(当然记录正的也行,反正一个就够了),num[x]表示 s[i] - i = -x的数有多少个,那也就是说,在第k次旋转后,num[k]的这些数就会有负变正,这样对于第 i 次旋转,big += num[i], sma -= num[i],于是对于big和sma的维护也完成了。
有趣的是,标程的代码竟然和Dukelv一样,反而题解的做法没有代码。
先发个30分吧
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<algorithm> 5 #include<cstring> 6 #include<cstdlib> 7 #include<cctype> 8 #include<vector> 9 #include<stack> 10 #include<queue> 11 using namespace std; 12 #define enter puts("") 13 #define space putchar(' ') 14 #define Mem(a, x) memset(a, x, sizeof(a)) 15 #define rg register 16 typedef long long ll; 17 typedef double db; 18 const int INF = 0x3f3f3f3f; 19 const db eps = 1e-8; 20 const int maxn = 2e6 + 5; 21 inline ll read() 22 { 23 ll ans = 0; 24 char ch = getchar(), last = ' '; 25 while(!isdigit(ch)) {last = ch; ch = getchar();} 26 while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();} 27 if(last == '-') ans = -ans; 28 return ans; 29 } 30 inline void write(ll x) 31 { 32 if(x < 0) x = -x, putchar('-'); 33 if(x >= 10) write(x / 10); 34 putchar(x % 10 + '0'); 35 } 36 void MYFILE() 37 { 38 #ifndef mrclr 39 freopen("a.in", "r", stdin); 40 freopen("a.out", "w", stdout); 41 #endif 42 } 43 44 int n, a[maxn]; 45 46 ll ans = INF; 47 48 int main() 49 { 50 MYFILE(); 51 n = read(); 52 for(rg int i = 1; i <= n; ++i) a[i] = read(); 53 for(rg int i = 1; i <= n; ++i) 54 { 55 ll Min = 0; 56 for(rg int j = i, k = 1; k <= n; ++k, ++j) 57 { 58 if(j > n) j -= n; 59 Min += abs(a[j] - k); 60 if(Min > ans) break; 61 } 62 ans = min(ans, Min); 63 } 64 write(ans); enter; 65 return 0; 66 }