51Nod比赛3总结

A - n^n的末位数字 51Nod - 1004

题意

给出一个整数N,输出N^N(N的N次方)的十进制表示的末位数字。
Input
一个数N(1 <= N <= 10^9)
Output
输出N^N的末位数字
Sample Input
13
Sample Output
3

分析

快速幂
(对你没有看错只有这三个字)

代码
#include<cstdio>
#include<algorithm>
using namespace std;
int n;
int ksm(int x,int m)
{
    if(m==0) return 1;
    int next=ksm(x,m/2);
    if(m%2==1) return ((next*next)%10*x)%10;
    else return next*next%10;
}
int main()
{
    scanf("%d",&n);
    printf("%d",ksm(n%10,n));
}

B - 蜥蜴和地下室 51Nod - 1489

题意

哈利喜欢玩角色扮演的电脑游戏《蜥蜴和地下室》。此时,他正在扮演一个魔术师。在最后一关,他必须和一排的弓箭手战斗。他唯一能消灭他们的办法是一个火球咒语。如果哈利用他的火球咒语攻击第i个弓箭手(他们从左到右标记),这个弓箭手会失去a点生命值。同时,这个咒语使与第i个弓箭手左右相邻的弓箭手(如果存在)分别失去b(1 ≤ b < a ≤ 10)点生命值。

因为两个端点的弓箭手(即标记为1和n的弓箭手)与你相隔较远,所以火球不能直接攻击他们。但是哈利能用他的火球攻击其他任何弓箭手。

每个弓箭手的生命值都已知。当一个弓箭手的生命值小于0时,这个弓箭手会死亡。请求出哈利杀死所有的敌人所需使用的最少的火球数。

如果弓箭手已经死亡,哈利仍旧可以将他的火球扔向这个弓箭手。

Input
第一行包含3个整数 n, a, b (3 ≤ n ≤ 10; 1 ≤ b < a ≤ 10),第二行包含n个整数――h1,h2,…,hn (1 ≤ hi ≤ 15), hi 是第i个弓箭手所拥有的生命力。
Output
以一行输出t――所需要的最少的火球数。
Sample Input
3 2 1
2 2 2
Sample Output
3

分析

DP或者暴力
暴力的话就是枚举每一个地方扔多少个火球,考场上我真的就是无脑枚举最后O(n)判断,然后愉快T掉,改成快一些的暴力才过,就是要求出当前位置放火球的上限和下限,会快很多。

代码
#include<cstdio>
#include<algorithm>
#define MAXN 26
using namespace std;
int d[MAXN],a,b,n,ans=9999999;
int cont(int x)
{
    int lm=d[x-1]/b+1;
    int rm=(x==n-1)?d[x+1]/b+1:0;
    int mm=d[x]/a+1;
    return max(mm,max(lm,rm));
}
int contt(int x)
{
    int re=0;
    if(d[x-1]>=0) re=d[x-1]/b+1;
    if(x==n-1) re=max(d[x+1]/b+1,re);
    return re;
}
void DFS(int x,int step)
{
    if(step>=ans) return;
    if(x==n)
    {
        ans=min(ans,step);
        return;
    }
    int ma=cont(x);
    int mi=contt(x);
    for(int i=mi;i<=ma;i++)
    {
        d[x]=d[x]-i*a;
        d[x+1]=d[x+1]-i*b;
        DFS(x+1,step+i);
        d[x]+=i*a;
        d[x+1]+=i*b;
    }
}
int main()
{
    scanf("%d%d%d",&n,&a,&b);
    for(int i=1;i<=n;i++)
        scanf("%d",&d[i]);
    DFS(2,0);
    printf("%d",ans);
}

C - 前缀后缀集合 51Nod - 1280

题意

一个数组包含N个正整数,其中有些是重复的。一个前缀后缀集是满足这样条件的下标对(P,S), 0<= P,S < N 满足数组元素A0..P0..P的值也在AS..N−1S..N−1的值中出现,并且AS..N−1S..N−1中的值也再A0..P0..P中出现。换句话说前缀的集合A0..P0..P与后缀集合AS..N−1S..N−1包含完全相同的值。求这样的前缀后缀集合的数量。

例如:3 5 7 3 3 5,共有14个集合符合条件:(1, 4), (1, 3), (2, 2), (2, 1), (2, 0), (3, 2), (3, 1), (3, 0), (4, 2), (4, 1), (4, 0), (5, 2), (5, 1), (5, 0)
本题由 @javaman 翻译。

Input
第1行:一个数N, 表示数组的长度(1 <= N <= 50000)。
第2 - N + 1行:每行1个数,对应数组中的元素Ai。(1 <= Ai <= 10^9)
Output
输出符合条件的集合数量。
Sample Input
6
3
5
7
3
3
5
Sample Output
14

分析

可以用类似于滑窗的做法吧,考虑到只有前缀和后缀包含的不同元素个数一样,才可能配对,就从前往后扫一遍,利用map存元素是否存在,然后记录一下前缀包含i个不同元素的点的个数,并且记录每次增加的以前没有出现的元素,对后缀也相同处理,然后对相同的不同元素个数从小到大扫一遍,再用一个map记录一下,如果前缀和后缀的元素一样,答案就加上当前元素个数的前缀点个数*当前元素个数的后缀点个数。
感觉解释的不清楚,看一看代码吧…
时间复杂度nlogn

代码
#include<cstdio>
#include<vector>
#include<algorithm>
#include<map>
#define MAXN 50006
using namespace std;
map<int,bool>mf,mb,in;
long long ans;
int sumf[MAXN],sumb[MAXN],f[MAXN],b[MAXN],n,a[MAXN],cntf,cntb,cnt;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    {
        if(!mf[a[i]])
        {
            cntf++;
            f[cntf]=a[i];
            sumf[cntf]=1;
            mf[a[i]]=1;
        }
        else sumf[cntf]++;
    }
    for(int i=n;i>=1;i--)
    {
        if(!mb[a[i]])
        {
            cntb++;
            b[cntb]=a[i];
            sumb[cntb]=1;
            mb[a[i]]=1;
        }
        else sumb[cntb]++;
    }
    for(int i=1;i<=cntf;i++)
    {
        if(in[f[i]]) cnt++;
        else in[f[i]]=1;
        if(in[b[i]]) cnt++;
        else in[b[i]]=1;
        if(cnt==i) ans+=1ll*sumf[i]*sumb[i];
    }
    printf("%lld",ans);
}

D - 山峰和旗子 51Nod - 1281

题意

用一个长度为N的整数数组A,描述山峰和山谷的高度。山峰需要满足如下条件, 0 < P < N - 1 且 AP−1P−1 < APP > AP+1P+1。

现在要在山峰上插上K个旗子,并且每个旗子之间的距离 >= K,问最多能插上多少个旗子(即求K的最大值)。两个山峰之间的距离为|P - Q|。
以上图为例,高度为:1 5 3 4 3 4 1 2 3 4 6 2。其中可以作为山峰的点为:1 3 5 10。

放2面旗子, 可以放在1 和 5。
放3面旗子, 可以放在1 5 和 10。
放4面旗子, 可以放在1 5 和 10,之后就放不下了。
所以最多可以放3面旗子。
Input
第1行:一个数N,表示数组的长度(1 <= N <= 50000)。
第2 - N + 1行:每行1个数Ai(1 <= Ai <= 10^9)。
Output
输出最多能插上多少面旗子(即求K的最大值)。
Sample Input
12
1
5
3
4
3
4
1
2
3
4
6
2
Sample Output
3

分析

预处理一下山峰的个数和位置,然后二分k即可。

代码
#include<cstdio>
#include<algorithm>
#define MAXN 50006
using namespace std;
int n,a[MAXN],p[MAXN],cnt;
bool check(int x)
{
    int last=-MAXN;
    int sum=0;
    for(int i=1;i<=cnt;i++)
    {
        if(p[i]-last>=x)
        {
            sum++;
            last=p[i];
        }
    }
    return sum>=x;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=2;i<n;i++)
        if(a[i]>a[i-1]&&a[i]>a[i+1]) p[++cnt]=i;
    int l=0,r=cnt;
    while(l<r)
    {
        int mid=(l+r+1)/2;
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    printf("%d",l);
}

E - 最长等差数列 51Nod - 1055

题意

N个不同的正整数,找出由这些数组成的最长的等差数列。

例如:1 3 5 6 8 9 10 12 13 14
等差子数列包括(仅包括两项的不列举)
1 3 5
1 5 9 13
3 6 9 12
3 8 13
5 9 13
6 8 10 12 14

其中6 8 10 12 14最长,长度为5。
Input
第1行:N,N为正整数的数量(3 <= N <= 10000)。
第2 - N+1行:N个正整数。(2<= Aii <= 10^9)
Output
最长等差数列的长度。
Sample Input
10
1
3
5
6
8
9
10
12
13
14
Sample Output
5

分析

考试时看见数据范围,没敢写n^2……以为光内存就要炸,然后发现要用short int(手动再见)
先排序,然后DP,d[i][j]表示a[i]为第倒数第二个数,a[j]为最后一个数形成的等差数列长值,枚举j,然后向两边枚举,前面的是l,后面的是r,如果a[j]-a[l]==a[r]-a[j],那么d[j][r]就和d[l][j]+1取max,不等,就往两边滑。
记得如果d数组一开始为0,答案就要+2,你也可以把d数组初始化为2,但是没有必要。

代码
#include<cstdio>
#include<algorithm>
#define MAXN 10006
using namespace std;
int n,a[MAXN];
short int ans,d[MAXN][MAXN];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++)
    {
        int l=i-1;
        int r=i+1;
        while(l>=1&&r<=n)
        {
            if(a[i]-a[l]==a[r]-a[i]) 
            {
                if(d[l][i]+1>d[i][r]) 
                {
                    d[i][r]=d[l][i]+1;
                    ans=max(ans,d[i][r]);
                }
            }
            if(a[i]-a[l]>a[r]-a[i]) r++;
            else l--;
        }
    }
    printf("%d",ans+2);
}

F - 零树 51Nod - 1424

题意

有一棵以1为根的树,他有n个结点,用1到n编号。第i号点有一个值vi。

现在可以对树进行如下操作:

步骤1:在树中选一个连通块,这个连通块必须包含1这个结点。

步骤2:然后对这个连通块中所有结点的值加1或者减1。

问最少要经过几次操作才能把树中所有结点都变成0。

注意:步骤1与步骤2合在一起为一次操作。

Input
单组测试数据。
第一行有一个整数n(1 ≤ n ≤ 10^5)
接下来n-1行,每行给出 ai 和 bi (1 ≤ ai, bi ≤ n; ai ≠ bi),表示ai和bi之间有一条边,输入保证是一棵树。
最后一行有n个以空格分开的整数,表示n个结点的值v1, v2, …, vn (|vi| ≤ 10^9)。
Output
输出一个整数表示最少的操作步数。.
Sample Input
3
1 2
1 3
1 -1 1
Sample Output
3

分析

树DP,记录每个点i要把它及其它子树变成0减一的次数最小值d[i][0]和加一的次数最小值d[i][1],然后对于每一个点,枚举每一棵子树j,先处理子树,然后d[i][0]=max(d[i][0],d[j][0]);d[i][1]=max(d[i][1],d[j][1]),因为所有子树可以一起处理,然后处理当前点,设一开始权值为c[i],c[i]+=d[i][1]-d[i][0],即将子树的加减加入这个根上,然后如果c[i]>0,就说明还要继续减,d[i][0]+=c[i],否则要加,d[i][1]+=-c[i],输出d[1][0]+d[1][1]即可

代码
#include<cstdio>
#include<vector>
#include<algorithm>
#define MAXN 100006
using namespace std;
vector<int> road[MAXN];
long long d[MAXN][2],c[MAXN];
int n,a,b;
void DP(int x,int fa)
{
    for(int i=0;i<(int)road[x].size();i++)
    {
        int next=road[x][i];
        if(next==fa) continue;
        DP(next,x);
        d[x][0]=max(d[x][0],d[next][0]);
        d[x][1]=max(d[x][1],d[next][1]);
    }
    c[x]+=d[x][1]-d[x][0];
    if(c[x]>0) d[x][0]+=c[x];
    else d[x][1]+=-c[x];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        road[a].push_back(b);
        road[b].push_back(a);
    }
    for(int i=1;i<=n;i++)
        scanf("%lld",&c[i]);
    DP(1,0);
    printf("%lld",d[1][0]+d[1][1]);
}

G - Jabby’s segment tree 51Nod - 1792

题意

线段树是一种经典的数据结构,一颗1,n1,n的线段树他的根是1,n1,n,当一个线段树的结点是l,rl,r时,设mid=(l+r)>>1,则这个结点的左儿子右儿子分别是l,midl,mid,mid+1,rmid+1,r
当我们在线段树上跑x,yx,y询问时,一般是从根节点开始计算的,设现在所在结点是l,rl,r,有以下几种分支:
1.若x,yx,y包含l,rl,r,计算结束
2.否则,若左儿子和x,yx,y有交,计算左儿子,若右儿子和x,yx,y有交,计算右儿子
定义询问x,yx,y的费用是询问时计算了几个结点
给定Q次询问,每次给定l,r,求满足l<=x<=y<=r的(x,y)的费用之和
你需要将答案对1000000007取模
Input
第一行两个正整数n,Q(1<=n,Q<=100000)
接下来Q行每行两个正整数l,r,保证l<=r
Output
输出Q行,每行一个非负整数表示答案
Sample Input
2 1
1 2
Sample Output
5

分析

然而现在还是不懂,待补。
大佬文:http://blog.youkuaiyun.com/alan_cty/article/details/78335664

代码

蛤蛤蛤…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值