【GDOI2018 Day1】密碼鎖 題解
阿新 • • 發佈:2021-07-10
一道有點思維難度的差分題
這題乍一看似乎沒什麼思路,但我們換個方向來想,如果把\(n\)個數在模\(m\)的意義下差分一下(再最後補一個\(a[n+1]\)與\(a[n]\))差分,那麼如果我們想要把\(a\)陣列全部變成0,也就相當於把差分陣列(假設是\(cf[i]\))全部變成0
然後我們又知道,在差分陣列修改一段區間\([l,r]\)時(假設是加1),是將\(cf[l]+1,cf[r+1]-1\),然後減1就是反過來將\(cf[l]-1,cf[r+1]+1\),又因為本題的\(cf[i]\)是在模\(m\)的意義下的,所以一個\(cf[i]\)加到\(m\)或者減到0的結果都是0
於是想法就很顯然了,將差分陣列排序,前半段的顯然減到0更優,後半段的顯然加到\(m\) 更優,所以我們可以維護\(cf[i]\)的字首和\(sum1[i]\),維護\(m-cf[i]\)的字首和\(sum2[i]\),然後列舉分割加和減的點,如果這個點的字首和這個點下一個點和字尾和相同,那麼也就是說前半段減和後半段加可以抵消,將所有差分陣列在模\(m\)意義下修改到0,那麼這個點的字首和或者這個點下一個點的字尾和即為答案
【GDOI2018 Day1】密碼鎖
Description
Input
Output
Sample Input
Sample Input1
4 3
1 2 1 0
Sample Input2
11 8
1 2 3 4 5 0 5 4 3 2 1
Sample Input3
20 100
30 91 15 72 61 41 10 37 98 41 94 80 26 96 10 88 59 5 84 14
Sample Output
Sample Output1
2
Sample Output2
8
Sample Output3
313
Data Constraint
題解
考場這題我打了100行的暴力,記憶化搜尋加剪枝 (鬼知道我怎麼做得到的)
這題乍一看似乎沒什麼思路,但我們換個方向來想,如果把\(n\)個數在模\(m\)的意義下差分一下(再最後補一個\(a[n+1]\)與\(a[n]\))差分,那麼如果我們想要把\(a\)陣列全部變成0,也就相當於把差分陣列(假設是\(cf[i]\))全部變成0
然後我們又知道,在差分陣列修改一段區間\([l,r]\)時(假設是加1),是將\(cf[l]+1,cf[r+1]-1\),然後減1就是反過來將\(cf[l]-1,cf[r+1]+1\),又因為本題的\(cf[i]\)是在模\(m\)的意義下的,所以一個\(cf[i]\)加到\(m\)或者減到0的結果都是0
於是想法就很顯然了,將差分陣列排序,前半段的顯然減到0更優,後半段的顯然加到\(m\)
CODE
#include<cstdio> #include<string> #include<algorithm> #define max(a,b) (((a)>(b))?(a):(b)) #define min(a,b) (((a)<(b))?(a):(b)) #define R register int #define N 1000005 #define ll long long using namespace std; ll n,m,a[N],cf[N],sum1[N],sum2[N]; inline void read(ll &x) { x=0;ll f=1;char ch=getchar(); while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();x*=f; } int main() { freopen("lock.in","r",stdin); freopen("lock.out","w",stdout); read(n);read(m); for (R i=1;i<=n;++i) read(a[i]),cf[i]=(a[i]-a[i-1]+m)%m; cf[n+1]=(a[n+1]-a[n]+m)%m; sort(cf+1,cf+n+2); for (R i=1;i<=n+1;++i) sum1[i]=sum1[i-1]+cf[i]; for (R i=n+1;i;--i) { sum2[i]=sum2[i+1]+m-cf[i]; if (sum2[i]==sum1[i-1]) {printf("%lld\n",sum2[i]);return 0;} } return 0; }