noip模拟题11.9

T1 子序列

描述

给定3个字符串,求它们的最长公共子序列。

输入

第一行一个整数n,表示三个字符串的长度
接下来三行,每行是一个长度为n只包含小写字母的字符串。

输出

输出最长公共子序列的长度。

输入样例

4
abac
abbc
cbca

输出样例

2

提示

30% n<=10
100% n<=120

简单dp,三个循环,如果当前的ABC串的字符相等,f[i][j][k]=f[i-1][j-1][k-1]+1,否则f[i][j][k]=max(f[i-1][j][k],f[i][j-1][k],f[i][j][k-1]。
代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int f[125][125][125];
char c[4][125];
int main()
{
    freopen("subq.in","r",stdin);
    freopen("subq.out","w",stdout);
    int n;scanf("%d",&n);
    for(int i=1;i<=3;i++)
    {
        getchar();
        for(int j=1;j<=n;j++)
            scanf("%c",&c[i][j]);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                if(c[1][i]==c[2][j]&&c[1][i]==c[3][k])
                    f[i][j][k]=f[i-1][j-1][k-1]+1;
                else f[i][j][k]=max(f[i-1][j][k],max(f[i][j-1][k],f[i][j][k-1]));
    printf("%d",f[n][n][n]);
    return 0;
}

T2 dun

【问题描述】

定义两个素数是连续的当且仅当这两个素数之间不存在其他的素数如(7,11) , (23,29)。给定N、K,在不超过N的正整数中求能够分解为K个连续的素数的和的最大的那个是多少。

【输入格式】

第一行一个正整数��代表数据组数。
接下来T行每行两个正整数N、K,代表一组询问。

【输出格式】

输出共��行,每行一个整数代表答案;如果找不到这样的数,输出−1。

【样例输入】

3
20 2
20 3
20 4

【样例输出】

18
15
17

【样例解释】

╭︿︿︿╮
{/ o o /}
( (oo) )
︶︶︶

【数据规模与约定】

对于20%的数据,1 ≤N≤ 100。
对于40%的数据,T=1。
对于60%的数据,所有的询问的N相等。
对于100%的数据,1 ≤ T < 2000,1 ≤ N ≤ 10^6。

用线性筛法打一个10^6的素数表,然后用前缀和的形式保存下来。每次询问只要从最后一个大于N的素数开始往下循环。如果sum[i]-sum[i-k]≤N,输出sum[i]-sum[i-k],否则输出-1。

代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#ifdef WIN32
#define AUTO "%I64d"
#else
#define AUTO "%lld"
#endif
using namespace std;
int judge[1000005];
long long prime[78500],sum[78500];
inline void solve(int n,int k,int last)
{
    while(prime[last]>n||prime[last]==0)
        last--;
    for(int i=last;i>=k;i--)
        if(sum[i]-sum[i-k]<=n)
        {
            printf(AUTO,sum[i]-sum[i-k]);
            printf("\n");
            return ;
        }
    printf("-1\n");
}
int main()
{
    freopen("dun.in","r",stdin);
    freopen("dun.out","w",stdout);
    int T,tot=0;scanf("%d",&T);
    for(long long i=2;i<=1000000;i++)
    {
        if(!judge[i])
            prime[++tot]=i;
        for(int j=1;j<=tot;j++)
        {
            if(i*prime[j]>1000000)break;
            judge[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
    for(int i=1;i<=tot;i++)
        sum[i]=sum[i-1]+prime[i];
    while(T--)
    {
        int n,k;scanf("%d%d",&n,&k);
        int last=lower_bound(prime+1,prime+1+tot,n)-prime;
        solve(n,k,last);
    }
    return 0;
}

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

前4组数据范围很小,想怎样暴力都行。
要A掉就有点麻烦了,要用到树链剖分。

AC代码(标程):

#include <cstdio>

#include <cstring>

#include <vector>

#define lson rt<<1

#define rson rt<<1|1

#define maxn 100010

using namespace std;



struct Pair {

    int ind, val;

    Pair(){}

    Pair( int ii, int vv ) : ind(ii), val(vv) {}

};



int n, q;

vector<int> g[maxn], flag[maxn];

int siz[maxn], son[maxn], dep[maxn], pre[maxn], top[maxn], vid[maxn], id_clock;

int maxw, ans[maxn];

Pair seg[maxn<<2]; 



void input() {

    scanf( "%d%d", &n, &q );

    for( int i=1,u,v; i<n; i++ ) {

        scanf( "%d%d", &u, &v );

        g[u].push_back(v);

        g[v].push_back(u);

    }

}

void update( int w, int delta, int rt, int l, int r ) {

    if( l==r ) {

        seg[rt].val += delta;

        seg[rt].ind = l;

        return;

    }

    int mid = (l+r)>>1;

    if( mid>=w ) update( w, delta, lson, l, mid );

    else update( w, delta, rson, mid+1, r );

    seg[rt] = seg[lson].val < seg[rson].val ? seg[rson] : seg[lson];

}

void dfs1( int u ) {

    siz[u] = 1, son[u] = 0;

    for( int t=0; t<(int)g[u].size(); t++ ) {

        int v=g[u][t];

        if( v==pre[u] ) continue;

        dep[v] = dep[u]+1;

        pre[v] = u;

        dfs1(v);

        siz[u] += siz[v];

        son[u] = siz[v]>siz[son[u]] ? v : son[u];

    }

}

void dfs2( int u, int tp ) {

    top[u] = tp, vid[u] = ++id_clock;

    if( son[u] ) dfs2( son[u], tp );

    for( int t=0; t<(int)g[u].size(); t++ ) {

        int v=g[u][t];

        if( v==pre[u] || v==son[u] ) continue;

        dfs2( v, v );

    }

}

void build() {

    dep[1] = pre[1] = 1;

    dfs1(1);

    id_clock = 0;

    dfs2(1,1);

}

void modify( int u, int v, int w ) {

    while( top[u] != top[v] ) {

        if( dep[top[u]]<dep[top[v]] ) swap(u,v);

        flag[vid[top[u]]].push_back(w);

        flag[vid[u]+1].push_back(-w);

        u = pre[top[u]];

    }

    if( dep[u]<dep[v] ) swap(u,v);

    flag[vid[v]].push_back(w);

    flag[vid[u]+1].push_back(-w);

}

void answer() {

    for( int i=1; i<=q; i++ ) {

        int u, v, w;

        scanf( "%d%d%d", &u, &v, &w );

        modify( u, v, w );

        maxw = max( maxw, w );

    }

    for( int i=1; i<=id_clock; i++ ) {

        for( int t=0; t<(int)flag[i].size(); t++ ) {

            if( flag[i][t]>0 ) update( flag[i][t], 1, 1, 1, maxw );

            if( flag[i][t]<0 ) update( -flag[i][t], -1, 1, 1, maxw );

        }

        if( seg[1].val==0 ) ans[i] = 0;

        else ans[i] = seg[1].ind;

    }

    for( int i=1; i<=n; i++ )

        printf( "%d\n", ans[vid[i]] );

}

int main() {

    input();

    build();

    answer();

}

void pv( vector<int> &v ) {

    for( int t=0; t<(int)v.size(); t++ )

        fprintf( stderr, "%d ", v[t] );

    fprintf( stderr, "\n" );

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值