Problem A 紫色激情
一个序列${a_n}$,求出方差最大的子序列。
其中方差 [l,r] 的定义是$S^2 = frac{1}{n} sumlimits_{i=l}^{r} (x_i-ar{x})^2$
对于100%的数据满足$n leq 10^3$
Sol : 直接推一波公式就可以前缀和优化了。
${ S_{l,r} }^2 = -ar{x}^2 +frac{sum_{i=l}^r {x_i}^2}{n}$
时间复杂度$O(n^2)$
# include# 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(' ');return 0; }
Problem B 克罗地亚狂想曲
共有$n$个节点,从每个节点可以向前面一个节点连一条边,而如果和后面一个节点的连边将被忽略。
两个相连节点之间有一条连边,可以直接经过,找出一条访问的路径,使得每个节点被经过最多2次,
经过路径上点值之和最大。
对于100%的数据 , $n leq 3 imes 10^5 $
Sol: 显然,向前连边的这个过程是没有后效性的,所以可以进行动态规划。
而每个节点最多只能被经过2次保证了一次转移的合法性。
设$f_i$表示走到节点$i$时候最大值。
如果当前元素没有向前连边,那么答案就从上一节点走来,$f_i = f_{i-1} + a_i$
如果当前元素有向前连边到$j$那么可以考虑从$j$过来在经过$j$,在走到$i$,再接下去走。
这等价于$j->i$的路径被累加了$2$次,那么转移方程就是$f_i = f_{j-1} + 2 sumlimits_{k=j}^{i} a_k$
然后那个$sum$累加可以用前缀和优化掉,这样这个DP就是线性的了。
复杂度$O(n)$
# include# 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(' '); return 0; }
Problem C 花之舞
给出一个字符串中,求该字符串中最大不重复双倍回文串覆盖。
对于100%的数据,$ len(s) leq 10^3 $
Sol : 首先我们可以通过字符串Hash的做法判定一个串是不是回文串,这样只需要一遍$O(n)$的预处理,再$O(1)$判定即可。
然后我们可以$O(n^2)$枚举从$i$开始的所有双倍回文串,然后可以使用类似线段覆盖的DP做出最大不重复双倍回文串覆盖。
最终的复杂度是$O(n^2)$的。
# include# 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 )f[i]=max(f[i],f[v[i][j]+1]+(v[i][j]-i+1));}write(f[1]); putchar(' ');return 0; }