HGOI 20190709 题解

Problem A 紫色激情

一个序列$\{a_n\}$,求出方差最大的子序列。

其中方差 [l,r] 的定义是$S^2 = \frac{1}{n} \sum\limits_{i=l}^{r} (x_i-\bar{x})^2$

对于100%的数据满足$n \leq 10^3$

Sol : 直接推一波公式就可以前缀和优化了。

  ${ S_{l,r} }^2 = -\bar{x}^2 +\frac{\sum_{i=l}^r {x_i}^2}{n}$

      时间复杂度$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$个节点,从每个节点可以向前面一个节点连一条边,而如果和后面一个节点的连边将被忽略。

两个相连节点之间有一条连边,可以直接经过,找出一条访问的路径,使得每个节点被经过最多2次,

经过路径上点值之和最大。

对于100%的数据 , $n \leq 3\times 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 \sum\limits_{k=j}^{i} a_k$

  然后那个$\sum$累加可以用前缀和优化掉,这样这个DP就是线性的了。

  复杂度$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 花之舞

给出一个字符串中,求该字符串中最大不重复双倍回文串覆盖。

对于100%的数据,$ len(s) \leq 10^3 $ 

Sol :  首先我们可以通过字符串Hash的做法判定一个串是不是回文串,这样只需要一遍$O(n)$的预处理,再$O(1)$判定即可。

然后我们可以$O(n^2)$枚举从$i$开始的所有双倍回文串,然后可以使用类似线段覆盖的DP做出最大不重复双倍回文串覆盖。

最终的复杂度是$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

 

posted @ 2019-07-09 14:06  ljc20020730  阅读(282)  评论(0编辑  收藏  举报