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
代码
蛤蛤蛤…