[IOI1998]Polygon

这都是RLD二十年前就AK的题了
我今天才来写

题目都要求先断一条边了
就直接倍长,在链上算

显然是一个区间dp
加法可以枚举中间点直接转移
但是因为有负数,乘法转移时不一定有最大值转移来
所以同时转移最小值

#include<cstdio>
#include<iostream>

using namespace std;

#define gc c=getchar()
#define r(x) read(x)

template<typename T>
inline void read(T&x){
    x=0;T k=1;char gc;
    while(!isdigit(c)){if(c=='-')k=-1;gc;}
    while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
}

const int INF=32768;
const int N=105;

inline char GetChar(){
    char gc;
    while(c!='t'&&c!='x')gc;
    return c;
}

int a[N],mx[N][N],mi[N][N];
char op[N];

int main(){
    int n;r(n);
    for(int i=1;i<=n;++i){
        op[i]=GetChar(),r(a[i]);
        op[i+n]=op[i],a[i+n]=a[i];
    }
    n<<=1;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            mx[i][j]=-INF;
            mi[i][j]=INF;
        }
    }
    for(int i=1;i<=n;++i)mx[i][i]=mi[i][i]=a[i];
    for(int len=2;len<=n;++len){
        for(int l=1,r=len;r<=n;++l,++r){
            for(int i=l;i<r;++i){
                if(op[i+1]=='t'){
                    mx[l][r]=max(mx[l][r],mx[l][i]+mx[i+1][r]);
                    mi[l][r]=min(mi[l][r],mi[l][i]+mi[i+1][r]);
                }
                else {
                    mx[l][r]=max(mx[l][r],mx[l][i]*mx[i+1][r]);
                    mx[l][r]=max(mx[l][r],mi[l][i]*mi[i+1][r]);
                    mi[l][r]=min(mi[l][r],mi[l][i]*mi[i+1][r]);
                    mi[l][r]=min(mi[l][r],mx[l][i]*mi[i+1][r]);
                    mi[l][r]=min(mi[l][r],mi[l][i]*mx[i+1][r]);
                }
            }
        }
    }
    n>>=1;
    int ans=-INF;
    for(int i=1;i<=n;++i){
        ans=max(ans,mx[i][i+n-1]);
    }
    printf("%d\n",ans);
    for(int i=1;i<=n;++i){
        if(mx[i][i+n-1]==ans){
            printf("%d ",i);
        }
    }
}

posted @ 2018-10-25 14:05  NamelessOIer  阅读(196)  评论(1编辑  收藏  举报