2017北京国庆刷题Day4 morning

期望得分:0+40+30=70

实际得分:0+10+10=20

 

 题目修改:只能由0变1,只能用一次操作

大模拟

#include<cstdio>
#include<cstring>
using namespace std;
char s[2001];
int len,n;
int cnt[2001];
bool solve1()
{
    if(len!=n) return false;
    int num=0;
    for(int i=0;i<len;i++)
        if(s[i]=='1') num+=i+1;
    if(num%(n+1)==0)
    {
        puts(s);
        return true;
    }
    return false;
}
bool solve2()
{
    if(len!=n) return false;
    int num=0;
    for(int i=0;i<len;i++)
        if(s[i]=='1') num+=i+1;
    for(int i=0;i<len;i++)
        if(s[i]=='1')
        {
            num-=(i+1);
            if(num%(n+1)==0) 
            {
                for(int j=0;j<i;j++) putchar(s[j]);
                putchar('0');
                for(int j=i+1;j<len;j++) putchar(s[j]);
                printf("\n");
                return true;
            }
            num+=(i+1);
        }
    return false;
}
bool solve3()
{
    if(len<=n) return false;
    int num=0;
    for(int i=0;i<len;i++)
        if(s[i]=='1') num+=i+1,cnt[i+1]=cnt[i]+1;
        else cnt[i+1]=cnt[i];
    for(int i=0;i<len;i++)
        if(s[i]=='0')
        {
            num-=(cnt[len]-cnt[i]);
            if(num%(n+1)==0)
            {
                for(int j=0;j<i;j++) putchar(s[j]);
                for(int j=i+1;j<len;j++) putchar(s[j]);
                printf("\n");
                return true;
            }
            num+=(cnt[len]-cnt[i]);
        }
        else
        {
            num-=(cnt[len]-cnt[i+1]);
            num-=(i+1);
            if(num%(n+1)==0)
            {
                for(int j=0;j<i;j++) putchar(s[j]);
                for(int j=i+1;j<len;j++) putchar(s[j]);
                printf("\n");
                return true;
            }
            num+=(cnt[len]-cnt[i+1]);
            num+=(i+1);
        }
    return false;
}
bool solve4()
{
    if(len>=n) return false;
    int num=0;
    for(int i=0;i<len;i++)
        if(s[i]=='1') num+=(i+1),cnt[i+1]=cnt[i]+1;
        else cnt[i+1]=cnt[i];
    for(int i=0;i<len;i++)
    {
        num+=cnt[len]-cnt[i];
        if(num%(n+1)==0)
        {
            for(int j=0;j<i;j++) putchar(s[j]);
            putchar('0');
            for(int j=i;j<len;j++) putchar (s[j]);
            printf("\n");
            return true;
        }
        num+=i+1;
        if(num%(n+1)==0)
        {
            for(int j=0;j<i;j++) putchar(s[j]);
            putchar('1');
            for(int j=i;j<len;j++) putchar(s[j]);
            printf("\n");
            return true;
        }
        num-=(i+1);
        num-=(cnt[len]-cnt[i]);
    }
    if(num%(n+1)==0) { printf("%s",s); printf("0\n");return true; }
    if((num+len+1)%(n+1)==0) { printf("%s",s); printf("1\n"); return true; }
    return false;
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&n);
    while(scanf("%s",s)!=EOF)
    {
        len=strlen(s);
        if(solve1()) continue;
        if(solve2()) continue;
        if(solve3()) continue;
        if(solve4()) continue;
        printf("-1\n");
    }
}
View Code

 

题目中要求选的数 %m 互不相同

那么可以很自然的想到

先在<m的数里选k个 ,使他们的和 s%m=n%m,那么还剩下n-s

为了保证%m 不相同,所以 我们把剩下的n-s 分为 (n-s)/m 个 m,

给这k个数分 m

也就是说

如果要换掉x,只能用和x 模m属于同一剩余系 的数换,所以 只能加 m的整数倍

 

设f[i][j] 表示选了i个数,和为j,且选的i个数互不相同的方案数

设g[i][j]表示把i个m分给j个数的方案数

那么ans= Σ f[i][j] * g[(n-j)/m][i] * i !,  其中要求j%m=n%m

这里要乘i的阶乘,因为 1 2 和 2 1 算作不同的方案 

 

如何求f[i][j] ?

很容易想到 f[i][j][k] 表示 选了i个数,和为j,选的数字中最大的为k的方案数

这样是m^4,TLE

其实可以压去第三维

两维的缺陷是 如果顺序枚举i,枚举j,再枚举现在选哪个数,不能保证选的数%m互不相同

因为对于同一个数k,可能f[i+1][j] 由 已经选了k的f[i][j-k]再选k转移而来

解决的方法是

先枚举当前选哪个数,再倒序枚举选了多少个数,再枚举这些数的和

这样就保证了对于同一个数k,不会让 i+1 由 i 转移

 

如何求g[i][j]?

把i个东西分给j个人,显然是组合数

C(i+j-1,j-1) 

难道 还要逆元?

其实不用 ,这个C 化开为:

    1*2* …… i+j-1

---------------------------------(这是分数线) 

(1*2……j-1)*(1*2……*i)

 

= ((i+1)*(i+2)……*(i+j-1))/((j-1)!)

 

不要漏了 统计答案的时候还要乘上j! 所以分母消去只剩下个j

 

求 (i+1)*(i+2)……*(i+j-1) 的时候,i 本身可能是 long long ,不要忘了先取模,防止 乘爆

#include<cstdio>

#define M 101

using namespace std;

typedef long long LL;

const LL mod=905229641;

int f[M][M*M/2];

LL mul(LL a,LL b)
{
    LL res=1;
    for(LL i=a;i<=b;i++) res*=i,res%=mod;
    return res;
} 

int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    LL n; int m;
    scanf("%I64d%d",&n,&m);
    int mx=m*(m-1)/2;
    f[0][0]=1;
    for(int k=0;k<m;k++)
        for(int i=m;i;i--)
            for(int j=k;j<=mx;j++)
                f[i][j]+=f[i-1][j-k],f[i][j]%=mod;
    int x=n%m; LL res,ans=0,y;
    for(int i=x;i<=mx && i<=n;i+=m)
    {
        y=(n-i)/m;
        for(int j=1;j<=m;j++)
            if(f[j][i])
            {
                res=mul((y+1)%mod,(y+j-1)%mod);
                res*=f[j][i]; res%=mod;
                res*=j; res%=mod;
                ans+=res,ans%=mod;
            }
    }
    printf("%I64d",ans);
}
View Code

 

 

解决本题的突破点:

哪一年种哪些地可以随意定

 

dp[i] 表示 种过的最右边的地为i时的最大收益

将所有的种地区间按左端点从左到右排序

那么对于当前这个种地区间[l[i],r[i]]

它的上一个种地区间 [l[j],r[j]],

l[j]一定<=l[i]

r[j]可能有三种情况

① r[j] < l[i] 此时第i块地 有全部的垦荒代价

② r[j]∈[l[i],r[i]) 此时第i块地 有 右边部分的垦荒代价

③ r[j]>=r[i] 此时第i块地 没有垦荒代价

令sum[i]表示垦荒代价的前缀和,val[i]表示第i次垦荒的收益

那么对应的状态转移方程:

① dp[r[i]]=max(dp[r[j]]+val[i]-(sum[r[i]]-sum[l[i]-1]))

② dp[r[i]]]=max(dp[r[j]]+val[i]-(sum[r[i]]-sum[r[j]]))

③ dp[r[j]]+=val[i]

朴素的dp为O(n^2)

可用线段树优化至nlogn 

 

在 r[j]==r[i] 时,有很多细节

线段树的没调出来

 

30分 n^2代码

#include<cstdio>
#include<cstring>
#include<iostream> 
#include<algorithm>

#define N 200001

using namespace std;

typedef long long LL;

LL sum[N],dp[N];
int vis[N];

struct node
{
    int l,r,p;
}e[N];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar();  }
} 

bool cmp(node p,node q)
{
    if(p.l!=q.l) return p.l<q.l;
    return p.r<q.r;
}

int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);
    int n,m,x; 
    read(n); read(m);
    for(int i=1;i<=n;++i) read(x),sum[i]=sum[i-1]+x;
    for(int i=1;i<=m;++i) read(e[i].l),read(e[i].r),read(e[i].p);
    sort(e+1,e+m+1,cmp);
    memset(dp,-127,sizeof(dp));
    for(int i=1;i<=m;i++)
    {
        LL tmp=-1e18; bool ok=false;
        dp[e[i].r]=max(dp[e[i].r],e[i].p-(sum[e[i].r]-sum[e[i].l-1]));
        tmp=dp[e[i].r];
        for(int j=i-1;j;j--)
            if(e[j].r<e[i].l) dp[e[i].r]=max(dp[e[i].r],dp[e[j].r]+e[i].p-(sum[e[i].r]-sum[e[i].l-1]));
            else if(e[j].r<e[i].r) dp[e[i].r]=max(dp[e[i].r],dp[e[j].r]+e[i].p-(sum[e[i].r]-sum[e[j].r]));
            else if(vis[e[j].r]!=i) // 一个e[i].p只能累计一次到一个e[j].r 里 
            {
                if(e[j].r==e[i].r) ok=true;//e[i].r==e[j].r 时,r 可能被 前两种情况修改过,但累计的话只能是用没有修改过的 
                else dp[e[j].r]+=e[i].p,vis[e[j].r]=i;
            }
         if(ok) dp[e[i].r]=max(dp[e[i].r],tmp+e[i].p);
    }
    LL ans=0;
    for(int i=1;i<=n;i++) ans=max(ans,dp[i]);
    printf("%I64d",ans);
} 
View Code

 

std

#include <cstdio>
#include <cctype>
#include <memory.h>
#include <algorithm>

using namespace std;

typedef long long qw;

#ifdef WIN32
#define lld "%I64d"
#else
#define lld "%lld"
#endif

int nextInt() {
    int s = 0, d;
    bool nag = 0;
    do {
        d = getchar();
        if (d == '-')
            nag = 1;
    } while (!isdigit(d));
    do
        s = s * 10 + d - 48, d = getchar();
    while (isdigit(d));
    return nag ? -s : s;
}

struct seg {
    int l, r;
    qw v, z;
    seg *ls, *rs;
};
struct obj {
    int l, r, v;
};

inline bool cmpObj(const obj& a, const obj& b) {
    return (a. l < b. l) || (a. l == b. l && a. r < b. r);
}

const int maxn = 200009;
const qw inf = 0x3f3f3f3f3f3f3f3fLL;

int n, m;
obj q[maxn];
qw s[maxn], ans;
seg *rtf, *rtc, *sp;

#define mid(p) ((p->l+p->r)>>1)

seg *sgtBuild(int l, int r) {
    seg *p = sp ++;
    p-> v = - inf;
    p-> z = 0;
    p-> l = l;
    p-> r = r;
    if (l + 1 < r) {
        p-> ls = sgtBuild(l, mid(p));
        p-> rs = sgtBuild(mid(p), r);
    }
    return p;
}

void sgtChg(seg* p, int p0, qw v0) {
    if (p-> l + 1 == p-> r)
        p-> v = max(p-> v, v0);
    else {
        if (p0 < mid(p))
            sgtChg(p-> ls, p0, v0 - p-> z);
        else
            sgtChg(p-> rs, p0, v0 - p-> z);
        p-> v = max(p-> ls-> v, p-> rs-> v) + p-> z;
    }
}

qw sgtQry(seg* p, int l, int r) {
    if (l >= r)
        return -inf;
    else if (p-> l == l && p-> r == r)
        return p-> v;
    else if (r <= mid(p))
        return sgtQry(p-> ls, l, r) + p-> z;
    else if (l >= mid(p))
        return sgtQry(p-> rs, l, r) + p-> z;
    else
        return max(sgtQry(p-> ls, l, mid(p)), sgtQry(p-> rs, mid(p), r)) + p-> z;
}

void sgtLazy(seg* p, int l, qw z0) {
    if (p-> v == -inf)
        return;
    else if (p-> l == l)
        p-> v += z0, p-> z += z0;
    else {
        if (l < mid(p)) {
            sgtLazy(p-> ls, l, z0);
            sgtLazy(p-> rs, mid(p), z0);
        }
        else
            sgtLazy(p-> rs, l, z0);
        p-> v = max(p-> ls-> v, p-> rs-> v) + p-> z;
    }
}

int main() {
//    freopen("c.in","r",stdin);
//    freopen("c.out","w",stdout);
    sp = new seg[maxn * 6];
    n = nextInt();
    m = nextInt();
    rtf = sgtBuild(0, n + 2);
    rtc = sgtBuild(0, n + 2);
    s[0] = 0;
    for (int i = 1; i <= n; i ++)
        s[i] = s[i - 1] + nextInt();
    for (int i = 0; i < m; i ++) {
        q[i]. l = nextInt();
        q[i]. r = nextInt();
        q[i]. v = nextInt();
    }
    sort(q, q + m, cmpObj);
    ans = 0;
    for (int i = 0; i < m; i ++) {
        qw res0 = max(sgtQry(rtf, 0, q[i]. l), 0LL) - s[q[i]. r] + s[q[i]. l - 1];
        qw res1 = sgtQry(rtc, q[i]. l, q[i]. r + 1) - s[q[i]. r];
        qw res = max(max(res0, res1), sgtQry(rtf, q[i]. r, n + 1)) + q[i]. v;
        sgtLazy(rtf, q[i]. r, q[i]. v);
        sgtLazy(rtc, q[i]. r, q[i]. v);
        sgtChg(rtf, q[i]. r, res);
        sgtChg(rtc, q[i]. r, res + s[q[i]. r]);
        ans = max(ans, res);
        printf("%I64d %I64d\n",res,ans);
    }
    printf(lld "\n", ans);
}
View Code

 

没跳出来&&不想调的线段树

#include<cstdio>
#include<cstring>
#include<iostream> 
#include<algorithm>

#define N 200001

using namespace std;

typedef long long LL;

LL sum[N];

LL mx1[N<<2],mx2[N<<2],f[N<<2];

LL t1,t2;

struct node
{
    int l,r,p;
}e[N];

void read(int &x)
{
    x=0; char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) { x=x*10+c-'0'; c=getchar();  }
} 

bool cmp(node p,node q)
{
    if(p.l!=q.l) return p.l<q.l;
    return p.r<q.r;
}

void change(int k,int l,int r,int pos,LL w,int ty)
{
    if(l==r)
    {
        if(ty==1) mx1[k]=max(mx1[k],w);
        else mx2[k]=max(mx2[k],w);
        return;
    } 
    int mid=l+r>>1;
    if(pos<=mid) change(k<<1,l,mid,pos,w,ty);
    else change(k<<1|1,mid+1,r,pos,w,ty);
    mx1[k]=max(mx1[k<<1],mx1[k<<1|1])+f[k];
    mx2[k]=max(mx2[k<<1],mx2[k<<1|1])+f[k];
}

int query(int k,int l,int r,int opl,int opr,int ty)
{
    if(l>=opl && r<=opr)
    {
        if(ty==1) return max(t1,mx1[k]);
        return max(t2,mx2[k])a;
    }
    int mid=l+r>>1;
    if(opr<=mid) return query(k<<1,l,mid,opl,opr,ty)+f[k];
    if(opr>mid) return query(k<<1|1,mid+1,r,opl,opr,ty)+f[k];
    return max(query(k<<1,l,mid,opl,opr,ty),query(k<<1|1,mid+1,r,opl,opr,ty))+f[k];
}

void add(int k,int l,int r,int pos,int w)
{
    if(l==pos) 
    {
        f[k]+=w;
        mx1[k]+=w;
        mx2[k]+=w;
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid) add(k<<1,l,mid,pos,w),add(k<<1|1,mid+1,r,mid+1,w);
    else add(k<<1|1,mid+1,r,pos,w);
    mx1[k]=max(mx1[k<<1],mx1[k<<1|1])+f[k];
    mx2[k]=max(mx2[k<<1],mx2[k<<1|1])+f[k];
}

int main()
{
//    freopen("8.in","r",stdin);
//    freopen("c.out","w",stdout);
    int n,m,x; LL ans=0;
    read(n); read(m);
    for(int i=1;i<=n;++i) read(x),sum[i]=sum[i-1]+x;
    for(int i=1;i<=m;++i) 
     read(e[i].l),read(e[i].r),read(e[i].p);
    sort(e+1,e+m+1,cmp);
    memset(mx2,-127,sizeof(mx2));
    memset(mx1,-127,sizeof(mx1));
    LL now;
    for(int i=1;i<=m;i++)
    {
        t1=query(1,1,n,e[i].l,e[i].r,1);//2 
        t2=query(1,1,n,e[i].r,n,2);
        now=t1+e[i].p-sum[e[i].r];//2
        now=max(now,t2+e[i].p);
        t2=query(1,1,n,1,e[i].l,2);
        now=max(now,t2+e[i].p-sum[e[i].r]+sum[e[i].l-1]);//1
        ans=max(ans,now);
        add(1,1,n,e[i].r,e[i].p);
        change(1,1,n,e[i].r,now+sum[e[i].r],1);
        change(1,1,n,e[i].r,now,2);
        
        printf("%I64d %I64d\n",now,ans);
    } 
    printf("%I64d",ans); 
} 
View Code

 

posted @ 2017-10-11 16:56  TRTTG  阅读(328)  评论(2编辑  收藏  举报