POJ 3276 Face The Right Way(前缀和优化)

题意:有长度为N的01串,有一个操作可以选择连续K个数字取反,求最小的操作数和最小的K使得最后变成全1串.(N<=5000)

由于K是不定的,无法高斯消元。

考虑枚举K,求出最小的操作数。

显然同样的区间操作两次是没必要的,且区间长度是一定的。从左到右依次考虑每个左端点i。如果为0,则需要改变区间[i,i+K-1].

此外,还需要考虑之前的操作对这个端点的影响。

复杂度O(n^3).

令f[i]为区间左端点为i时是否操作,那么可以发现我们考虑一个端点是否该取反时,前面影响它的是一个连续的区间。前缀和维护即可。

复杂度O(n^2).

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-3
# define MOD 1000000007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int res=0, flag=0;
    char ch;
    if((ch=getchar())=='-') flag=1;
    else if(ch>='0'&&ch<='9') res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')  res=res*10+(ch-'0');
    return flag?-res:res;
}
void Out(int a) {
    if(a<0) {putchar('-'); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+'0');
}
const int N=5005;
//Code begin...

char temp[3];
int sum[N], s[N];

int main ()
{
    int n, cnt, ansx=INF, ansy;
    scanf("%d",&n);
    FOR(i,1,n) scanf("%s",temp), s[i]=(temp[0]=='B');
    FOR(i,1,n) {
        mem(sum,0);
        cnt=0;
        FOR(j,1,n) {
            int z=s[j]+(sum[j-1]-sum[j>=i?j-i:0]);
            if (z&1) {
                ++cnt;
                if (j>n-i+1) {cnt=INF; break;}
                sum[j]=sum[j-1]+1;
            }
            else sum[j]=sum[j-1];
        }
        if (ansx>cnt) ansx=cnt, ansy=i;
    }
    printf("%d %d
",ansy,ansx);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/lishiyao/p/6345027.html