【noip模拟题】[dp][二分][树链剖分][hdu5029][线段树]

4 篇文章 0 订阅
3 篇文章 0 订阅

T1

描述
给定3个字符串,求它们的最长公共子序列。
输入
第一行一个整数n,表示三个字符串的长度
接下来三行,每行是一个长度为n只包含小写字母的字符串。
输出
输出最长公共子序列的长度。
输入样例
4
abac
abbc
cbca
输出样例
2
提示
30% n<=10
100% n<=120

水水水

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
const int N=125;
int f[N][N][N],n;
char a[N],b[N],c[N];
int main()
{
    freopen("subq.in","r",stdin);
    freopen("subq.out","w",stdout);
    scanf("%d",&n);
    scanf("%s",a+1);scanf("%s",b+1);scanf("%s",c+1);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++)
        for(int k=1;k<=n;k++)
        if(a[i]==b[j]&&b[j]==c[k])
            f[i][j][k]=f[i-1][j-1][k-1]+1;
        else{
            int ma=max(f[i-1][j][k],f[i][j-1][k]);
            ma=max(ma,f[i][j][k-1]);
            ma=max(ma,f[i-1][j-1][k]);
            ma=max(ma,f[i-1][j][k-1]);
            ma=max(ma,f[i][j-1][k-1]);
            f[i][j][k]=ma;
        }
    printf("%d\n",f[n][n][n]);
    return 0;
}

T2

【问题描述】
定义两个素数是连续的当且仅当这两个素数之间不存在其他的素数(如 7,11 ,(23,29)。给定��,��,在不超过��的正整数中求能够分解为��个连续的素数的和的最大的那个是多少。
【输入格式】
第一行一个正整数��代表数据组数。
接下来��行每行两个正整数��,��代表一组询问。
【输出格式】
输出共��行,每行一个整数代表答案;如果找不到这样的数,输出−1。
【样例输入】
3
20 2
20 3
20 4
【样例输出】
18
15
17
【样例解释】
╭︿︿︿╮
{/ o o /}
( (oo) )
︶︶︶
【数据规模与约定】
对于20%的数据,1≤��≤100。
对于40%的数据,��=1。
对于60%的数据,所有的询问的��相等。
对于100%的数据,1≤��<2000,1≤��≤106

这道题,拿到之后第一个反应是找规律啊找规律,发现没有规律可以找…然后干脆写个暴力吧…嗯暴力时顺手一个前缀和….咦不如写二分?…..哇刚好能过诶!<-这就是我做这道题的全过程orz
代码:

#include<iostream>
#include<cstdio>
#define LL long long
using namespace std;
const int N=2005;
LL zhi[78500];
int t,tot,n,ans,k;
bool a[1000005];
struct node{
    int q,n;
};
void pre()
{
    for(int i=2;i<=1000000;i++)
    if(!a[i])
      for(LL j=(LL)i*i;j<=1000000;j+=i)
      a[j]=true;
    for(int i=2;i<=1000000;i++)
    if(!a[i])zhi[++tot]=zhi[tot-1]+i;

}
void sear(int le,int ri)
{
    ans=-1; 
    while(le<=ri)
    {
        int mid=(le+ri)>>1;
        LL sum=zhi[mid]-zhi[max(0,mid-k)];
        if(sum<=(LL)n)le=mid+1,ans=(int)sum;
        else ri=mid-1;
    }
}
void worrk()
{
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        scanf("%d%d",&n,&k);
        if(zhi[k]>n){printf("-1\n");continue;}
        sear(max(1,2*k-tot),tot);
        printf("%d\n",ans);
    }
}
int main()
{
    freopen("dun.in","r",stdin);
    freopen("dun.out","w",stdout);
    pre();
    worrk();
}

T3

发放粮食
描述
有一个村庄在闹饥荒,善良而土豪的YGH决定给他们发放救济粮,该村庄有 n 户人家,每两户人家之间只有一条路可以互相到达,即这些人家之间形成一棵树。现在 YGH 会以这样的形式给他们发放粮食,选择两户人家,然后对这两个户人家路径上的所有人家都发放一袋种类为 w 的救济粮。在完成一系列发放任务后,YGH 想知道每一户人家收到的粮食中数量最多的是哪一种。
输入
第一行两个数 n,q,其中 n 表示村庄共有几户人家,q 表示 YGH 一共发放了几次粮食。接下来 n-1 行,每行两个数 x y,表示编号为 x 和 y 的两户人家之间连有边。接下来 q 行,每行三个数 x y w,表示 YGH 选择了 x 到 y 的路径,对每户人家发放 1 袋种类为 w 的粮食。
输出
输出 n 行,第 i 行输出编号为 i 的人家收到的粮食中数量最多的种类号,如果有多个数量相同的粮食,输出其中最小的种类号,如果没有收到粮食,输出0
样例输入
[1]
2 4
1 2
1 1 1
1 2 2
2 2 2
2 2 1
[2]
5 3
1 2
3 1
3 4
5 3
2 3 3
1 5 2
3 3 3
样例输出
[1]
1
2
[2]
2
3
3
0
2
提示
对于 40% 的数据 n<=1000,q<=1000,1<=w<=1000
对于 100% 的数据 n<=100000,q<=100000,1<=w<=100000 1<=x,y<=n
原题请见HDU5029 orz

这道题处于习惯,来了个倍增,于是乎超时超时妥妥,还写了130多行orz后来在网上艰难地学习了树链剖分,发现真的很神奇!!!

其实重点不是树链剖分,而是这道题巧妙的处理:
1.将操作先全部叉分在剖分后的链上。
2.从左往右扫描链的时候,用线段树对于颜色(粮食)进行维护,在存点的颜色(粮食)时用链表存,我大概多开了个几倍免得不够用因为不会用vector。扫描的时候只需将颜色update就可以了,复杂度最大大概是q*logn*logm?
代码代码:

#include<iostream>     
#include<cstdio>
#include<cstring>
#define L(x)(x<<1)
#define R(x)(x<<1|1)
using namespace std;
const int N=1e5+5,M=20000005;
int n,q,x,y,z,ans[N],gx[N];
int head[N],nexxt[N*2],tov[N*2],tot;//int head_[N],nexxt_[M],tov_[M],tot2;//颜色 
int dp[N],son[N],fa[N],w[N],gen,siz[N],top[N],ad;//树链 
struct node{
    int l,r,ma,cc;
}cl[N*4];
void build1(int a,int b)
{tot++;nexxt[tot]=head[a];tov[tot]=b;head[a]=tot;}
void build2(int a,int b)
{tot2++;nexxt_[tot2]=head_[a];tov_[tot2]=b;head_[a]=tot2;}
void dfs1(int k)
{
    siz[k]=1;son[k]=0; 
    int u=head[k],v=tov[u];
    int ma=0;
    while(u)
    {
        if(v!=fa[k])
        {
            fa[v]=k;dp[v]=dp[k]+1;
            dfs1(v);
            siz[k]+=siz[v];
            if(siz[v]>ma)son[k]=v,ma=siz[v];
        }
        u=nexxt[u];v=tov[u];
    }
}
void dfs2(int k,int b)
{
    w[k]=++ad;top[k]=b;
    int u=head[k],v=tov[u];
    if(son[k])dfs2(son[k],b); //while(u)
    {
        if(v!=fa[k]&&v!=son[k])
            dfs2(v,v); //轻 
        u=nexxt[u];v=tov[u];
    }
}
void pre()
{
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&x,&y);
        build1(x,y);build1(y,x);
    }
    gen=(1+n)>>1;
    dp[gen]=1;fa[gen]=0;
    dfs1(gen);
    dfs2(gen,gen);
}//预处理树链剖分  

void upda(int v)
{
    int u=v>>1;
    if(cl[R(u)].ma>cl[L(u)].ma)cl[u].ma=cl[R(u)].ma,cl[u].cc=cl[R(u)].cc;
    else cl[u].ma=cl[L(u)].ma,cl[u].cc=cl[L(u)].cc;
}
void build_tree(int u,int le,int ri)
{
    cl[u].l=le;cl[u].r=ri;
    if(le==ri){gx[le]=u;cl[u].cc=le;cl[u].ma=0;return;}
    int mid=(le+ri)>>1;
    build_tree(L(u),le,mid);
    build_tree(R(u),mid+1,ri);
    upda(u>>1); 
}
void update(int u)
{
    if(!u)return;
    int mm,ty;
    if(cl[R(u)].ma>cl[L(u)].ma)mm=cl[R(u)].ma,ty=cl[R(u)].cc;
    else mm=cl[L(u)].ma,ty=cl[L(u)].cc;
    if(!mm)ty=0;
    if(cl[u].ma!=mm||cl[u].cc!=ty)
    {
        cl[u].ma=mm;cl[u].cc=ty;
        update(u>>1);
    }
}
void devi(int a,int b,int c)
{
    int f1=top[a],f2=top[b];
    while(f1!=f2)
    {
        if(dp[f1]<dp[f2])swap(f1,f2),swap(a,b);
        build2(w[f1],c);
        build2(w[a]+1,-c);
        a=fa[f1];f1=top[a];
    }
    if(dp[a]>dp[b])swap(a,b);
    build2(w[a],c);build2(w[b]+1,-c);
}
void worrk()
{
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        devi(x,y,z);
    }
    build_tree(1,1,100000);
    for(int i=1;i<=n;i++)
    {
        int u=head_[i],v=tov_[u];
        while(u)
        {
            int k=1;
            if(v<0)k=-1,v=-v;
            int hh=gx[v];
            cl[hh].ma+=k;
            update(hh>>1);
            u=nexxt_[u];v=tov_[u];
        }
        ans[i]=cl[1].cc;
    }
    for(int i=1;i<=n;i++)
        printf("%d\n",ans[w[i]]);
}
int main()
{
    freopen("rice.in","r",stdin);
    freopen("rice.out","w",stdout);
    scanf("%d%d",&n,&q);
    pre();
    worrk();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值