HGOI 20190709 题解
Problem A 紫色激情
一個序列$\{a_n\}$,求出方差最大的子序列。
其中方差 [l,r] 的定義是$S^2 = \frac{1}{n} \sum\limits_{i=l}^{r} (x_i-\bar{x})^2$
對于100%的數(shù)據(jù)滿足$n \leq 10^3$
Sol : 直接推一波公式就可以前綴和優(yōu)化了。
${ S_{l,r} }^2 = -\bar{x}^2 +\frac{\sum_{i=l}^r {x_i}^2}{n}$
? ? ? 時間復(fù)雜度$O(n^2)$
# include<bits/stdc++.h> # define int long long using namespace std; const int N=2e3+10; int s1[N],s2[N];int n; inline int read() {int X=0,w=0; char c=0;while(c<'0'||c>'9') {w|=c=='-';c=getchar();}while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();return w?-X:X; } inline void write(int x) {if (x<0) x=-x,putchar('-'); if (x>9) write(x/10);putchar(x%10+'0'); } signed main() {n=read();for (int i=1;i<=n;i++) {int t=read(); s1[i]=s1[i-1]+t;s2[i]=s2[i-1]+t*t;}double ans=-1e9; int ansl=0,ansr=0;for (int l=1;l<=n;l++)for (int r=l;r<=n;r++) {double t=-(double)(s1[r]-s1[l-1])*(s1[r]-s1[l-1])/(double)(r-l+1)/(double)(r-l+1);double w=(double)(s2[r]-s2[l-1])/(double)(r-l+1); if (t+w>ans) { ans=t+w; ansl=l; ansr=r;}}write(ansl);putchar(' ');write(ansr); putchar('\n');return 0; }passion.cpp
Problem B?克羅地亞狂想曲
共有$n$個節(jié)點,從每個節(jié)點可以向前面一個節(jié)點連一條邊,而如果和后面一個節(jié)點的連邊將被忽略。
兩個相連節(jié)點之間有一條連邊,可以直接經(jīng)過,找出一條訪問的路徑,使得每個節(jié)點被經(jīng)過最多2次,
經(jīng)過路徑上點值之和最大。
對于100%的數(shù)據(jù) , $n \leq 3\times 10^5 $
Sol: 顯然,向前連邊的這個過程是沒有后效性的,所以可以進行動態(tài)規(guī)劃。
而每個節(jié)點最多只能被經(jīng)過2次保證了一次轉(zhuǎn)移的合法性。
設(shè)$f_i$表示走到節(jié)點$i$時候最大值。
如果當前元素沒有向前連邊,那么答案就從上一節(jié)點走來,$f_i = f_{i-1} + a_i$
如果當前元素有向前連邊到$j$那么可以考慮從$j$過來在經(jīng)過$j$,在走到$i$,再接下去走。
這等價于$j->i$的路徑被累加了$2$次,那么轉(zhuǎn)移方程就是$f_i = f_{j-1}? + 2 \sum\limits_{k=j}^{i} a_k$
然后那個$\sum$累加可以用前綴和優(yōu)化掉,這樣這個DP就是線性的了。
復(fù)雜度$O(n)$
# include <bits/stdc++.h> # define int long long using namespace std; const int N=3e5+10; int from[N],f[N],s[N],n; inline int read() {int X=0,w=0; char c=0;while(c<'0'||c>'9') {w|=c=='-';c=getchar();}while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();return w?-X:X; } inline void write(int x) {if (x<0) x=-x,putchar('-'); if (x>9) write(x/10);putchar(x%10+'0'); } signed main() {n=read();for (int i=1;i<=n;i++) {int t=read();if (t>i) continue;from[i]=t;}for (int i=1;i<=n;i++) {int t=read();s[i]=s[i-1]+t;}for (int i=1;i<=n;i++) {f[i]=f[i-1]+s[i]-s[i-1];if (from[i]!=0) f[i]=max(f[from[i]-1]+2*(s[i]-s[from[i]-1]),f[i]);}write(f[n]);putchar('\n'); return 0; }rhapsody.cpp
Problem C 花之舞
給出一個字符串中,求該字符串中最大不重復(fù)雙倍回文串覆蓋。
對于100%的數(shù)據(jù),$ len(s) \leq 10^3 $?
Sol :? 首先我們可以通過字符串Hash的做法判定一個串是不是回文串,這樣只需要一遍$O(n)$的預(yù)處理,再$O(1)$判定即可。
然后我們可以$O(n^2)$枚舉從$i$開始的所有雙倍回文串,然后可以使用類似線段覆蓋的DP做出最大不重復(fù)雙倍回文串覆蓋。
最終的復(fù)雜度是$O(n^2)$的。
# include<bits/stdc++.h> # define int long long using namespace std; const int mo=1e9+7; const int base=131; const int N=1e3+10; int p[N],a[N],hash1[N],hash2[N],f[N]; int n; vector<int>v[N]; inline int read() {int X=0,w=0; char c=0;while(c<'0'||c>'9') {w|=c=='-';c=getchar();}while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();return w?-X:X; } inline void write(int x) {if (x<0) x=-x,putchar('-'); if (x>9) write(x/10);putchar(x%10+'0'); } int gethash(int num,int l,int r) {if (num==1) return ((hash1[r]-hash1[l-1]*p[r-l+1]%mo)%mo+mo)%mo;else return ((hash2[r]-hash2[l-1]*p[r-l+1]%mo)%mo+mo)%mo; } bool check(int l,int r) {if ((r-l+1)&1) {int pos=(l+r)/2,len=(r-l)/2; if (gethash(1,pos,pos+len)==gethash(2,n+1-pos,n+1-pos+len)) return 1;else return 0;} else {int pos1=(l+r)/2,pos2=pos1+1,len=(r-l-1)/2;if (gethash(1,pos2,pos2+len)==gethash(2,n+1-pos1,n+1-pos1+len)) return 1;else return 0;} } void fun(int pos) {for (int mid=(n+1-pos)/2;mid>=1;mid--) if (check(pos,pos+mid-1)&&check(pos+mid,pos+2*mid-1)&&gethash(1,pos,pos+mid-1)==gethash(1,pos+mid,pos+2*mid-1)) v[pos].push_back(pos+2*mid-1); } signed main() { // freopen("dance.in","r",stdin); // freopen("dance.out","w",stdout);n=read(); p[0]=1;for (int i=1;i<=n;i++) a[i]=read(),p[i]=p[i-1]*base%mo; for (int i=1;i<=n;i++) hash1[i]=(hash1[i-1]*base%mo+a[i])%mo;int u=0; for (int i=n;i>=1;i--) u++,hash2[u]=(hash2[u-1]*base%mo+a[i])%mo;for (int i=1;i<=n;i++) fun(i); for (int i=n;i>=1;i--) {f[i]=f[i+1];for (int j=0;j<v[i].size();j++)f[i]=max(f[i],f[v[i][j]+1]+(v[i][j]-i+1));}write(f[1]); putchar('\n');return 0; }dance.cpp
?
轉(zhuǎn)載于:https://www.cnblogs.com/ljc20020730/p/11156950.html
總結(jié)
以上是生活随笔為你收集整理的HGOI 20190709 题解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 10个有趣的javascript和css
- 下一篇: 去西藏旅游需要多少钱?