hgoi#20190303

T1-最长公共前缀(lcp)

定义两个字符串S,T的最长公共前缀lcp(S,T)为最长的字符串R,满足R既是S的前缀又是T的前缀。
给定一个字符串S,下标从1开始,每次询问给出四个正整数a,b,c,d,你需要输出[a,b]这个子串与[c,d]这个子串的lcp的长度。

解法

我会说我考试的时候打了个暴力吗(:з」∠)
正解是二分答案加哈希,那么肿么处理捏
读进来abcd的时候取一下短的字符串,二分答案(这应该知道吧),check用哈希值是否相同判断

ac代码

#include<bits/stdc++.h>
using namespace std;
int n,m,a,b,c,d,po[100010],f[100010],P=31,D=1000173169;
char s[100010];
int hash(int l,int r){return(long long)(f[r]-(long long)f[l-1]*po[r-l+1]%D+D)%D;}
//哈希一下
int ask(int a,int b,int c,int d)
{
    int l=1,r=b-a+1,mid,t=0;
    if(d-c+1<r)r=d-c+1;
    while(l<=r){mid=(l+r)>>1;if(hash(a,a+mid-1)==hash(c,c+mid-1))l=(t=mid)+1;else r=mid-1;}
    return t;
}
//二分答案啦_(:з」∠)_
int main()
{
    freopen("lcp.in","r",stdin),freopen("lcp.out","w",stdout),scanf("%d%d",&n,&m);
    for(register int i=1;i<=n;++i)cin >> s[i];
    int i;
    for(po[0]=i=1;i<=n;i++)po[i]=(long long)po[i-1]*P%D;
    for(i=1;i<=n;i++)f[i]=(long long)((long long)f[i-1]*P+s[i])%D;
    //预处理一下哈希时要用的东西
    for(i=1;i<=m;++i)scanf("%d%d%d%d",&a,&b,&c,&d),printf("%d\n",ask(a,b,c,d));
    return 0;
}

T2-抢救粮仓(save)

查尔明的家着火啦!他的粮仓正面临着被烧毁的危险!
查尔明在山上一共建有n个粮仓,按海拔从高到低依次被标记上编号1到n,且没有任意两个粮仓的海拔是一样的。第i个粮仓里藏有a[i]吨粮食,第i 个粮仓与第i-1 个粮仓之间的距离为1。
查尔明立马掏出水枪进行灭火,每次灭火他可以指定一个粮仓进行抢救,由于不同粮仓的火势不同,需要喷的水量也不同,抢救第i 个粮仓需要消耗b[i]吨水。查尔明的手速实在是太快了,你可以理解成灭火是瞬间完成的。
与此同时,查尔明的助手们开着货车去转移粮食,因为他们认为越低的地方越安全,所以他们只会把粮食从高处运往低处,最终运到某个被灭过火的粮仓。查尔明的粮食很奇怪,每个粮仓都有一个共享值d[i],表示第i 个粮仓只能容纳与距自己距离不超过d[i]的粮仓的粮食,否则就会发生爆炸,所以在运输的过程中务必要避免发生爆炸。
已知水费和粮食运输费都为1元/吨,查尔明想知道,自己最少需要花费多少钱来抢救全部粮食?

解法

考试的时候想了个很奇怪的贪心QwQ
容易得到一个结论是灭火消耗小于等于运输消耗的一定灭火所以我们先把能灭火的求出来,之后不能灭火的如果能直接运输到某一个灭了火的粮仓,就直接运输
剩下的处理就是贪(xuan)心(xue)了
我们把还没有抢救的粮食从后往前搜出一个同样没有抢救的粮食,使a[i]+b[j]<=b[i]+a[j]
至于证明吗emmm 就不要了吧QwQ 反正这是个错误的贪心结论(:з」∠)
正解是dp,用f[i]表示抢救前i个仓库的最小代价,转移方程就是f[i]=min(f[j]−sum[j])+sum[i−1]+b[i] (j>=i−d[i]−1)
意思就是找一个在i粮仓可以运输范围的最小的j,这个最小值用单调队列来维护

ac代码

#include<bits/stdc++.h>
using namespace std;
int n,a[100010],b[100010],d[100010],q[100010],f[100010],sum[100010],tail=0,head=0;
int main()
{
    freopen("save.in","r",stdin),freopen("save.out","w",stdout),scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i];
    for(int i=1;i<=n;i++)scanf("%d",&b[i]);
    for(int i=1;i<=n;i++)scanf("%d",&d[i]);
    q[tail++]=0;
    for(int i=1;i<=n;i++)
    {
        int p=q[lower_bound(q+head,q+tail,i-d[i]-1)-q];
        //用二分从单调队列中直接找出p
        f[i]=f[p]-sum[p]+b[i]+sum[i-1];
        //状态转移
        while(head<tail&&f[q[tail-1]]-sum[q[tail-1]]>=f[i]-sum[i])tail--;
        q[tail++]=i;
        //再把新的数压进去QwQ
    }
    printf("%d",f[n]);
    return 0;
}

T3-幸运7(seven)

查尔明为了锻炼自己的观察力,在纸上画了一棵有n个节点的树,点与点之间通过边连接,每条边有一个正整数权值。查尔明想要用肉眼观察出有多少组(i,j)满足1<=i<j<=n,且i到j的距离是他的幸运数7的倍数。
查尔明不一会儿就观察出来了,但是他不知道自己的答案是否正确,你能写一个程序帮
助他检验自己的答案吗?

解法

这道题是有个原题的,聪聪可可,只是把%3改成了%7
我来说一下做法吧QwQ 可以用点分治或者树形dp
我讲一下树形dp 定义f[x][0…6]表示点x及其子树中到点x的距离在%7意义下为0…6的点的个数
统计答案在状态转移之前,就是个卷积,初值把f[x][0]设为1

ac代码

#include<bits/stdc++.h>
using namespace std;
int s[100010],t[200010],nxt[200010],l[200010],f[100010][7],n,ans,g,a,b,ll,cnt=0;
inline void addedge(int from,int to,int len){t[++cnt]=to,l[cnt]=len,nxt[cnt]=s[from],s[from]=cnt;}
inline int mod(int x){return (x%7+7)%7;}
inline void dfs(int u,int fa)
{
    f[u][0]=1;
    for(int at=s[u];at!=-1;at=nxt[at])
    {
        if(t[at]==fa)continue;
        dfs(t[at],u);
        for(int i=0;i<7;i++)ans+=f[t[at]][i]*f[u][mod(-i-l[at])];
        for(int i=0;i<7;i++)f[u][mod(i+l[at])]+=f[t[at]][i];
    }
}
int main()
{
    freopen("seven.in","r",stdin),freopen("seven.out","w",stdout),memset(s,-1,sizeof(s)),scanf("%d",&n);
    for(int i=1;i<n;i++)scanf("%d%d%d",&a,&b,&ll),addedge(a,b,ll),addedge(b,a,ll);
    dfs(1,-1),printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值