浴谷八连测R6题解(收获颇丰.jpg)

  这场的题都让我收获颇丰啊QWQ 感谢van♂老师

  T1 喵喵喵!当时以为经典题只能那么做, 思维定势了...

  因为DP本质是通过一些条件和答案互相递推的一个过程, 实际上就是把条件和答案分配在DP的状态和结果中, 所以当条件数值非常大, 而答案比较小的时候, 完全可以将答案放在DP数组的状态中,用来递推条件, 如果这个条件合法, 那么表明这个答案是可行的。

  在这题里面, 答案不会超过b串的长度, 而a串的长度可以非常长, 所以可以设f[i][j]为b串中前i个字符, 匹配了j为在a串中的最前位置是什么, 用序列自动机处理出nxt[i][x]为a串第i位的下一个x字符的位置,就很好转移了。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=1010,inf=1e9;
int n1, n2, ans;
int f[maxn][maxn], last[26], nxt[1000010][26];
char s1[1000010], s2[maxn];
void read(int &k)
{
    int f=1;k=0;char c=getchar();
    while(c<‘0‘||c>‘9‘)c==‘-‘&&(f=-1),c=getchar();
    while(c<=‘9‘&&c>=‘0‘)k=k*10+c-‘0‘,c=getchar();
    k*=f;
}
inline int max(int a, int b){return a>b?a:b;}
inline int min(int a, int b){return a<b?a:b;}
int main()
{
    scanf("%s", s1+1); scanf("%s", s2+1);
    int n1=strlen(s1+1), n2=strlen(s2+1);
    memset(f,32,sizeof(f)); f[0][0]=0;
    for(int i=0;i<26;i++) last[i]=inf;
    for(int i=n1;i>=0;i--)
    {
        for(int j=0;j<26;j++) nxt[i][j]=last[j];
        if(i) last[s1[i]-‘a‘]=i;
    }
    for(int i=0;i<=n2;i++)
        for(int j=0;j<=i;j++)
        if(f[i][j]<=n1)
        {
            f[i+1][j]=min(f[i+1][j], f[i][j]);
            f[i+1][j+1]=min(f[i+1][j+1], nxt[f[i][j]][s2[i+1]-‘a‘]);
            ans=max(ans, j);
        }
    printf("%d\n", ans);
    return 0;
}

  T2 也是道非常好的DP...

  可以发现一个点至少被一个区间覆盖, 至多被两个区间覆盖,且区间必须相互覆盖, 因为是树不是森林。考虑区间覆盖的样子, 实际上是一些大区间下面覆盖着一些小区间, 所以可以考虑分类来转移。

  先预处理出第i个位置的下一个左端点大于i的区间nxt[i], 设f[i][j]为前i个区间, 选择的区间最右端点为j的方案数, 如果当前区间左端点在j或j左边才可转移, 若当前区间为大区间(a[i].r>=j), 则有

  若当前区间为小区间(a[i].r<j), 则有

  注意刷表法如果是用i这个位置更新答案的话最后的答案应该为f[n+1][]。

  喵啊, 真的喵喵

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define MOD(x) ((x)>=mod?(x)-mod:(x))
using namespace std;
const int maxn=2010, mod=1e9+7;
struct poi{int l,r;}a[maxn];
int n, ans;
int f[maxn][4010], nxt[4010];
inline void read(int &k)
{
    int f=1; k=0; char c=getchar();
    while(c<‘0‘ || c>‘9‘) c==‘-‘&&(f=-1), c=getchar();
    while(c<=‘9‘ && c>=‘0‘) k=k*10+c-‘0‘, c=getchar();
    k*=f;
}
bool cmp(poi a, poi b){return a.l<b.l;}
int main()
{
    read(n);
    for(int i=1;i<=n;i++) read(a[i].l), read(a[i].r);
    sort(a+1, a+1+n, cmp);
    for(int i=1, j=1;i<=4000;nxt[i++]=j)
        while(j<=n && a[j].l<=i) j++;
    a[n+1].r=n+1;
    for(int i=1;i<=n;i++) f[i+1][a[i].r]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=4000;j++)
        {
            f[i+1][j]+=f[i][j]; f[i+1][j]=MOD(f[i+1][j]);
            if(a[i].l<=j)
            {
                if(a[i].r<j) f[nxt[a[i].r]][j]+=f[i][j], f[nxt[a[i].r]][j]=MOD(f[nxt[a[i].r]][j]);
                    else f[nxt[j]][a[i].r]+=f[i][j], f[nxt[j]][a[i].r]=MOD(f[nxt[j]][a[i].r]);
            }
        }
    for(int i=1;i<=4000;i++) ans=MOD(ans+f[n+1][i]);
    printf("%d\n", ans);
    return 0;
}

  T3 也喵喵喵...第一次见到这种姿势!

  分阈值分块做!设一个阈值B, 分公差x<B和>=B两种情况

  如果公差>=B, 可以直接暴力修改。因为当公差较大的时候, 修改复杂度会非常小, 这时候可以用O(n/B)修改, O(sqrt(n))查询的分块来做, 如果B定为sqrt(n),那肯定是可以承受的复杂度。

  如果公差<B, 先说说80分的做法。

  80分算法1:直接记录首项为y, 公差为x的被加了多少, 我们知道这些标记之后, 完全可以用数学方法O(1)算出区间[l, r]种这个标记出现了几次, 下面会提到怎么计算。这样的话修改O(1), 因为最多有B^2种标记, 所以查询是O(B^2)的。考虑平衡一下<B和>=B的情况, 应该尽量让它们相等, 即n/B=B^2, 所以将B定为n^(1/3), 复杂度为O(n^(5/3))的, 可以通过80分的数据。

  80分算法2:可以发现一个公差为y的标记, 无论首项是多少, 在区间[l,r]最多只有两种取值, 因为考虑首项+1, 最多会有一个数被移出去, 而在接下来的移动中, 下一个数被移出去之前必定有一个新的从前面加入。考虑使用树状数组来维护区间和这时候我们用前面提到的数学方法来计算这些区间,具体方法下面再细说...将B定为sqrt(n)这样复杂度为O(nsqrtnlogn)

  100分做法...

  发现我们前两种做法的修改复杂度都较低, 而主要复杂度在查询上, 可以考虑提高修改复杂度使得查询复杂度降低, 有什么做法是修改复杂度高而查询复杂度低的呢?对了, 前缀和!tag[x][y]表示公差为x, 首项y~x的标记和, 这样每次修改的复杂度为O(B), 查询的时候枚举公差, 用数学方法O(1)算出这个公差的答案, 复杂度O(B), 考虑上<B时的常数比较大, 所以将B定为sqrt(n/5)就可以过了...现在终于来说说所谓的数学方法...考虑80分算法1所提到的一个区间中同个公差的标记只有2种, 而取值较小的那种用r/i和(l-1)/i相减就可以求得了, 这个还是很好推的。思考一下什么时候取值会变大,就是当新的一个数进入区间开始, 到下一个数离开区间结束, 中间这一段会比另一个取值多1, 而新的一个数进入区间就是首项为x+k*i和l重合开始, 末项y+k*i和r重合结束, 也就是首项在[l%i, r%i]的时候。所以我们只需要加上所有首项的答案, 最后再加上一次首项在[l%i, r%i]这一段的答案就好了。即tag[i][i]*(r/i-(l-1)/i)+tag[i][r]-tag[i][l-1]即为答案。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define MOD(x) ((x)>=mod?(x)-mod:(x))
using namespace std;
const int maxn=200010, mod=1e9+7;
int n, m, ty, x, y, z, blo;
int tag[510][maxn], a[maxn], s[maxn], bl[maxn];
inline void read(int &k)
{
    int f=1; k=0; char c=getchar();
    while(c<‘0‘ || c>‘9‘) c==‘-‘&&(f=-1), c=getchar();
    while(c<=‘9‘ && c>=‘0‘) k=k*10+c-‘0‘, c=getchar();
    k*=f;
}
void update(int x, int y, int z)
{
    if(x>=blo)
        for(int i=y;i<=n;i+=x) a[i]+=z, a[i]=MOD(a[i]), s[bl[i]]+=z, s[bl[i]]=MOD(s[bl[i]]);
    else
        for(int i=y;i<=x;i++) tag[x][i]+=z, tag[x][i]=MOD(tag[x][i]);
}
inline int min(int a, int b){return a<b?a:b;}
inline int query(int l, int r)
{
    int up=min(bl[l]*blo, r), ans=0;
    for(int i=l;i<=up;i++) ans+=a[i], ans=MOD(ans);
    if(bl[l]!=bl[r])
        for(int i=(bl[r]-1)*blo+1;i<=r;i++) ans+=a[i], ans=MOD(ans);
    for(int i=bl[l]+1;i<=bl[r]-1;i++) ans+=s[i], ans=MOD(ans);
    for(int i=1;i<=blo;i++)
    {
        int x=r/i-(l-1)/i;
        ans=(ans+1ll*x*tag[i][i]+tag[i][r%i]-tag[i][(l-1)%i]+mod)%mod;
    }
    return ans;
}
int main()
{
    read(n); read(m); blo=sqrt(n/5);
    for(int i=1;i<=n;i++) bl[i]=(i-1)/blo+1;
    for(int i=1;i<=n;i++) read(a[i]);
    for(int i=1;i<=n;i++) s[bl[i]]+=a[i], s[bl[i]]=MOD(s[bl[i]]);
    for(int i=1;i<=m;i++)
    {
        read(ty); read(x); read(y);
        if(ty==1) read(z), update(x, y, z);
            else printf("%d\n", query(x, y));
    }
}

时间: 2024-07-29 19:20:16

浴谷八连测R6题解(收获颇丰.jpg)的相关文章

【洛谷八连测R7】nzhtl1477-我回来了

[洛谷八连测R7]nzhtl1477-我回来了 题目描述 68号岛有n个商店,有的商店直接有小路连接,小路的长度都为1,格里克告诉了你哪些地方可能有做黄油蛋糕的原料. 但是那个人是个坑货,所以他会告诉你一些商店,然后告诉你距离这些商店距离 <= k的商店中都是可能有原料的. 然后你要把这些可能的商店每个都去一遍,你想知道你要去多少个商店. 由于你是勇者,所以有m次询问 简洁题意: 给你一个图,每次查询的时候给一堆特殊点以及一个数k,求图中有多少点距离至少一个特殊点距离不超过k,边是无向的. 输入

洛谷八连测2017R7-nzhtl1477-いずれその陽は落ちるとしても

题目描述 珂朵莉有一个序列 在威廉不在的时候,珂朵莉没事情干 所以她数出了这个序列所有子区间的逆序对个数和 然而珂朵莉开始丧失记忆了,于是忘掉了这个根本没啥意义的值 所以给你一个序列,求出所有子区间的逆序对个数和 子区间: 一个序列有n*(n+1)/2个子区间 即所有满足1 <= l <= n , 1 <= r <= n , l <= r的[l , r]都是一个子区间 闭区间... 逆序对: 在一个区间中,如果有 i < j , a[i] > a[j]则( i ,

北漂职业生涯中心酸而又收获颇丰的一年

每年的年初年末的惯例,只不过今年把这篇文章搬家到这里来了. 总结2016 2015年底跳槽到现在的公司,从那时开始便经历了一段从未经历的职业生涯.整个过程中多次想放弃,由于种种原因又坚持了下来.现在站在一个更高的的思想层面,回头来看过往的一年,对其的评判或许更加客观和理性. 这是悲催的一年.从原来的中等程度的加班一下子变成高强度的加班,让人倍感身心疲惫.上一家公司,一般情况也就加班到八九点,偶尔会通宵一次.这份工作却不同,最开始那几个月十一点到家算是早的.连续一个月能只能休息一天也是常态.工作强

浴谷夏令营例题Codeforces827DBest Edge Weight(三个愿望,一次满足~(大雾

这题在浴谷夏令营wyx在讲的最小生成树的时候提到过,但并没有细讲怎么写... 这题可以用三种写法写,虽然只有两种能过...(倍增/倍增+并查集/树链剖分 先跑出最小生成树,分类讨论,在MST上的边,考虑用可以对这条边有影响的(判断是否有影响同后面)不在MST上的边的最小值-1来更新,不在MST上的边u->v,考虑用MST上u到v的路径上的边的最大值-1来更新. 显然用倍增就可以了,细节看代码. #include<iostream> #include<cstdlib> #inc

洛谷P2832 行路难 分析+题解代码【玄学最短路】

洛谷P2832 行路难 分析+题解代码[玄学最短路] 题目背景: 小X来到了山区,领略山林之乐.在他乐以忘忧之时,他突然发现,开学迫在眉睫 题目描述: 山区有n座山.山之间有m条羊肠小道,每条连接两座山,只能单向通过,并会耗费小X一定时间. 小X现在在1号山,他的目的是n号山,因为那里有火车站. 然而小X的体力是有限的.他每通过一条羊肠小道,就会变得更疲劳,导致他通过任意一条羊肠小道的时间都增加1. 输入格式: 第一行两个数,n,m 第2行到第m+1行,每行3个数A,B,C,表示A.B之间有一条

洛谷 P1219 八皇后 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1219 题目描述 检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行.每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子. 上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下: 行号 1 2 3 4 5 6 列号 2 4 6 1 3 5 这只是跳

洛谷 P1311 选择客栈 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1311 题目描述 丽江河边有n 家很有特色的客栈,客栈按照其位置顺序从 1 到n 编号.每家客栈都按照某一种色调进行装饰(总共 k 种,用整数 0 ~ k-1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费. 两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中.晚上,

洛谷 P1855 榨取kkksc03 题解

此文为博主原创题解,转载时请通知博主,并把原文链接放在正文醒目位置. 题目链接:https://www.luogu.org/problem/show?pid=1855 题目描述 洛谷的运营组决定,如果一名oier向他的教练推荐洛谷,并能够成功的使用(成功使用的定义是:该团队有20个或以上的成员,上传10道以上的私有题目,布置过一次作业并成功举办过一次公开比赛),那么他可以浪费掉kkksc03的一些时间的同时消耗掉kkksc03的一些金钱以满足自己的一个愿望. Kkksc03的时间和金钱是有限的,

互测六 题解 9.18 圆润的多边形&amp;Takuji与信徒

圆润的多边形: 我们可将所求图形划分如下: 绿色部分是一凸多边形,红色部分为若干个长方形,蓝色部分为若干个扇形. 易证:1.蓝色部分的总和是一个半径为r的整圆:2.红色部分的总和=凸多边形周长*r. 那么现在就只剩下如何计算的问题. 1.对于前12.5%数据,r=0且点坐标成顺时针排列,那么直接作为凸多边形来计算即可: 2.对于前50%数据,点坐标成顺时针排列,这个部分分的做法...我也没想出来... 这是为什么呢...因为这个题,是我受了洛谷P1513的启发而脑补出来的,这道题中有"点坐标成顺