vijos || bzoj 划船记

vijos1283 佳佳的魔杖
佳佳得到的这些树枝在属性上完全相同。每一个树枝都有n段(用1~n编号),给定了每段的长度L[i]和每段的魔力值M[i]。单独的一段是不可以从中间切开的,你可以做的就是选择一段或连续的几段,把它们作为一个整体切下来,再用来制作魔杖。但是一根魔杖的长度不能太长——不能大于给定的值hi;也不能太短——不能小于给定的值lo。

魔杖有一个奇怪的要求:如果某一根魔杖的制作材料是另一根魔杖的一部分,则这两根魔杖之间将发生冲突。比如说树枝有三段,从左到右的长度分别为4、 1、3,佳佳需要长度为4到5之间的魔杖。佳佳可以用一根树枝的前两段做出一个长度为5的魔杖,用一根树枝的后两段做出长度为4的魔杖;但他决不能用一根树枝的前两段做了魔杖后再单独使用另一根树枝的第一段做成魔杖,因为前者包含了后者的所有成分,这会导致冲突。

我们假设佳佳可以得到任意多这样的树枝。佳佳需要制作出若干个互不冲突的魔杖,使所有魔杖的魔力值之和最大。(魔杖的长度就是组成它的那些段的长度的总和,魔力值亦然)。

格式
输入格式

第一行有三个用空格隔开的正整数,分别表示n、lo、hi。

第二行的n个用空格隔开的正整数就是L[1]、L[2]……L[n]。

第三行的n个用空格隔开的正整数就是M[1]、M[2]……M[n]。

输入文件以一个回车/换行符结尾。
输出格式

只用输出一个整数,表示能够获得的魔力值的最大值。
样例:
Input
6 4 5
1 3 3 2 2 1
2 3 1 4 5 2
Output
21
取[1 3] [3 2] [2 2 1]做成魔杖。

得到最大权值2+3+1+4+4+5+2=21。

对于30%的数据,n<=10;
对于50%的数据,n<=100;
对于100%的数据,n<=1000,lo<=hi<=2^31-1,L[i],M[i]<=100 000

题解:
设f[i][j]为左端点不超过i,右端点不超过j的最大值,则有:
f[i][j] = max{f[i - 1][j],f[i][j - 1],f[i - 1][j - 1] + sum[j] - sum[i - 1]};
把不合法的删掉即可。
然而我(倒着推)的时候好像出了点问题,但是实际并不用倒着推。
嗯……把代码发一下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define RepD(i,n) for(int i = n;i > 0;i --)
#define RDD(i,l,r) for(int i = r;i >= l;i --)
int read()
{
    char ch = getchar();int x = 0;bool flag = 0;
    while((ch > '9' || ch < '0') && ch != '-')ch = getchar();
    if(ch == '-')flag = 1,ch = getchar();
    while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar();
    return flag ? -x : x;
}
using namespace std;
const int N = 1005;
int n,lm,rm;
long long L[N],M[N],f[N][N];
int main ()
{
    n = read(),lm = read(),rm = read();
    Rep(i,n)L[i] = read() + L[i - 1];
    Rep(i,n)M[i] = read() + M[i - 1];
    RepD(i,n)
        RDD(j,i,n)
        {
            if(L[j] - L[i - 1] < lm || L[j] - L[i - 1] > rm)
                f[i][j] = f[i][j + 1];
            else f[i][j] = max(f[i][j + 1],f[i + 1][j + 1] + M[j] - M[i - 1]);
            f[i][j] = max(f[i][j],f[i + 1][max(j,i + 1)]);
        }
    printf("%lld",f[1][1]);
    return 0;
}

vijos1264
题意描述:
身为拜月教的高级间谍,你的任务总是逼迫你出生入死。比如这一次,拜月教主就派你跟踪赵灵儿一行,潜入试炼窟底。

据说试炼窟底藏着五行法术的最高法术:风神,雷神,雪妖,火神,山神的咒语。为了习得这些法术,要付出艰辛的努力,但是回报同样十分丰厚。

拜月希望你告诉他咒语的长度为多少。(你:“请问您想知道咒语的具体内容吗?”拜月:“想,但是vijos不支持special judge。”-_-原来大人物也有大人物的悲哀。。。)
于是你偷偷躲在一边,想乘机看看咒语究竟是什么。突然,天空(??试炼窟底看的到天空??)出现了两条非常长的数字串,你抓狂了。究竟哪个才是真正的咒语呢?你突然想到,这两个都不是咒语(不妨称之为伪咒语),而真正的咒语却与他们有着密切的联系。于是你打开拜月亲手给你写的纸条:”The Real Incantation is Their Common Increasing Subsequence of Maximal Possible Length”
“该死的拜月,居然还会E文!”你咒骂着,但为了一家老小的生命,又不得不卖命地算着咒语的长度。
题解:
看了看发现是个LCIS。
然后……就不会做了……
不会做了……
你让我冷静下……
设f[i][j]表示a串到i,b串到j的LCIS长度。

if(a[i]==b[j])f[i][j]=max(f[i1][k])+1(k(1...j1) andb[k]<b[j])

if(a[i]!=b[j])f[i][j]=f[i1][j]

这样做,在相等时转移复杂度为 O(n3) 我们可以优化:
我们在a串到i的时候其实已经知道了b[j]也等于a[i],所以我们在之前的b[j] != a[i]中,把b[j] < a[i]的值都找出来,找出它们最大的f[i][j],用一个临时变量存储即可,并且整个方程可以滚动数组优化。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
int read()
{
    char ch = getchar();int x = 0;bool flag = 0;
    while((ch > '9' || ch < '0') && ch != '-')ch = getchar();
    if(ch == '-')flag = 1,ch = getchar();
    while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar();
    return flag ? -x : x;
}
using namespace std;
int n,m;
int f[505];
int a[505],b[505];
int main ()
{
    m = read();
    while(m --)
    {
        int len = read();
        for(int i = 1;i <= 500;i ++)f[i] = 0;
        Rep(i,len)
            a[i] = read();
        int len2 = read();
        Rep(i,len2)
            b[i] = read();  
        int ans = 0;
        Rep(i,len){
            int Maxn = 0;
            Rep(j,len2)
            {
                if(b[j] < a[i])Maxn = max(f[j],Maxn);
                if(a[i] == b[j])ans = max(f[j] = Maxn + 1,ans);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

bzoj1616: [Usaco2008 Mar]Cow Travelling游荡的奶牛
奶牛们在被划分成N行M列(2 <= N <= 100; 2 <= M <= 100)的草地上游走,试图找到整块草地中最美味的牧草。Farmer John在某个时刻看见贝茜在位置 (R1, C1),恰好T (0 < T <= 15)秒后,FJ又在位置(R2, C2)与贝茜撞了正着。 FJ并不知道在这T秒内贝茜是否曾经到过(R2, C2),他能确定的只是,现在贝茜在那里。 设S为奶牛在T秒内从(R1, C1)走到(R2, C2)所能选择的路径总数,FJ希望有一个程序来帮他计算这个值。每一秒内,奶牛会水平或垂直地移动1单位距离(奶牛总是在移动,不会在某秒内停在它上一秒所在的点)。草地上的某些地方有树,自然,奶牛不能走到树所在的位置,也不会走出草地。 现在你拿到了一张整块草地的地形图,其中’.’表示平坦的草地,’*’表示挡路的树。你的任务是计算出,一头在T秒内从(R1, C1)移动到(R2, C2)的奶牛可能经过的路径有哪些。
嗯……
这题就是搜一下就完了……
然后还有一种比较优美的写法:
F[i][j][k]表示:在i时刻到(j,k)的方案数,每次类似于BFS一样去扩散就好啦(只不过比较好写而已啊)
嗯代码很好写就不写了。

F[i][j][k]=F[i1][p][q]((p,q)(j,k))

然后就没有了……
某神题:大地的秘密
题意:已知一个初始序列和结束序列,每次可以把初始序列的元素k放到某一个位置,求最少步数。(n <= 100000)
引入知识:
求一个序列的LCS:
然而……暴力 O(n2) 不可取……
我们考虑一个数组c[],其中c[a[i]] = i,即c的数组存的是初始序列的某个数字在数组中的位置。
现在我们只需要考虑第二个序列b[]的每个元素在a[]中的位置即可。
我们把c[b[i]]的值排成一个序列。
现在我们只需要求这个序列的LIS(最长上升子序列)即可。
树状数组可以优化到nlogn。
好了,我们继续来看这道题目。
我们发现,这道题目实际上的移动次数等于n - LCS.
用之前的做法,就搞完了。
代码应该是这样TAT

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define Rep(i,n) for(int i = 1;i <= n;i ++)
#define Repd0(i,n) for(int i = n;i >= 0;i --)
#define RDD0(i,l,r) for(int i = r;i >= l;i --) 
int read()
{
    char ch = getchar();int x = 0;bool flag = 0;
    while((ch > '9' || ch < '0') && ch != '-')ch = getchar();
    if(ch == '-')flag = 1,ch = getchar();
    while(ch >= '0' && ch <= '9')x = 10 * x + ch - '0',ch = getchar();
    return flag ? -x : x;
}
using namespace std;
const int N = 100005;
int f[N],c[N],d[N],t[N];
int Qry(int x)
{
    int Maxn = 0;
    for(;x;x -= x & -x)Maxn = max(Maxn,t[x]);
    return Maxn;
}
int main ()
{
    int n = read();
    Rep(i,n)c[read()] = i;
    Rep(i,n)d[i] = c[read()];
    int ans = 0;
    Rep(i,n)
    {
        f[i] = Qry(d[i]) + 1;
        for(int x = d[i];x <= n;x += x & -x)t[x] = max(f[i],t[x]);
        ans = max(f[i],ans);
    }
    printf("%d\n",n - ans);
    return 0;
}

//下午好像去看FFT了所以划船到此结束

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值