T1:数列查询
题目
已知数列的通项公式为:
f(n) = f(n-1)*11/10,f[1]=10.
通项从左向右计算,*和/分别表示整数乘法和除法。 现在,要多次查询数列项的值。
INPUT
第1行,1个整数q,表示查询的次数, 1≤q≤10000. 第2至q+1行,每行1个整数i,表示要查询f(i)的值。
OUTPUT
q行,每行1个整数,表示f(i)的值。查询的值都在32位整数范围内。
Sample
INPUT
3
1
2
3
OUTPUT
10
11
12
Time limit
10ms
Memory limit
1MB
Data range
1≤q≤10000
Solution
读完题先看数据范围,1≤q≤10000,但并没有给数i的范围,但在输出格式内给出了查询的值都在32位整数范围内这一界定范围,我们可以通过一个简单的打表来找到i的最大值。
for(int i=2;i<=100000;i++){
f[i] = f[i - 1] * 11 / 10;
if(f[i] < 0)break;
printf("%d %d\n",i,f[i]);
}
输出为
177 119388179
178 131326996
179 144459695
180 158905664
181 174796230
182 192275853
183 211503438
得出i的数据范围是1-183左右
在得出i的范围后,就可以考虑解法了。
首先第一能想到的就是直接1-i暴力解出f(i),时间复杂度最坏为O(1830000),正常来说很稳
然而。。
TIME LIMIT: 10 ms
时间限制十分严格,任何常数大一点的操作都有可能T掉,更不要说暴力了
考虑用一个数组来存下f(1~183)
空间复杂度为183个int,不会超过空间限制
这样预处理复杂度只有183,询问复杂度只有O(1),在10ms的时间限制下是可以通过的
赛时AC代码
#include <bits/stdc++.h>
using namespace std;
int tot;
int f[100005];
int main(){
int q;
scanf("%d",&q);
f[1] = 10;
for(int i=2;i<=100000;i++)
f[i] = f[i - 1] * 11 / 10;
while(q--){
int x;scanf("%d",&x);
printf("%d\n",f[x]);
}
return 0;
}
但即使是算法正确,仍然有人不幸T掉这题,这是为什么呢?
这里就要引入常数的概念了
程序的任何一条语句,都要消耗一定的时间,将这一时间称之为常数。
有些算法虽然时间复杂度相同,但不同代码常数却不尽相同
例如如下两段代码
for(int i=1;i<=n;i++){
x += 2;
}
for(int i=1;i<=n;i++){
x += 1;
x += 1;
}
虽然效果一摸一样,时间复杂度也是O(n),但第二个程序相当于执行了两次加法,第一个只执行了一次。
在数据规模小的时候,是看不出来的
但在数据规模极大的时候,两个程序的耗时就会出现一定的差距
这也正是有时正解也不过的原因,代码常数过大
下面给出一些常数比对
printf/scanf 比 cin/cout 快
位运算 比 加减乘除 快
等等
感兴趣的可以自行搜索
T2:稀疏矩阵之和
题目
矩阵A和B都是稀疏矩阵。请计算矩阵的和A+B.如果A、B不能做和,输出“Illegal!”
INPUT
矩阵的输入采用三元组表示,先A后B。对每个矩阵:
第1行,3个整数N、M、t,用空格分隔,分别表示矩阵的行数、列数和非0数据项数。
第2至t+1行,每行3个整数r、c、v,用空格分隔,表示矩阵r行c列的位置是非0数据项v, v在32位有符号整型范围内。三元组默认按行列排序。
OUTPUT
矩阵A+B,采用三元组表示,默认按行列排序,非零项也在32位有符号整型范围内。
Sample
INPUT
10 10 3
2 2 2
5 5 5
10 10 20
10 10 2
2 2 1
6 6 6
OUTPUT
10 10 4
2 2 3
5 5 5
6 6 6
10 10 20
Time limit
100ms
Memory limit
10MB
Data range
10≤N、M≤50000,t≤min(N,M)
Solution
第一眼看的时候,我以为是教科书上的十字链表法存矩阵,但写了一半发现完全没有必要,用类似存图的手法来存该矩阵就可以,但需要注意的是如果加后矩阵该位置变成0了,需要从链表中删除。更需要注意的是,题目中“illegal!”中叹号是中文叹号,而数据中是英文叹号,这东西防不胜防,不用复制粘贴大法也还有“YE5”“N0”等着你,只能说多多注意
赛时AC代码
#include <bits/stdc++.h>
using namespace std;
struct Node{
int row,col;
long long val;
Node* rig;
};
Node* rhead[100005];
int n1,m1,t1;
int n2,m2,t2;
int sum;
int main(){
scanf("%d%d%d",&n1,&m1,&t1);
for(int i=1;i<=100000;i++){
rhead[i] = (Node*)malloc(sizeof(Node));
rhead[i] -> row = i;
rhead[i] -> col = -1;
rhead[i] -> val = 0;
rhead[i] -> rig = NULL;
}
for(int i=1;i<=t1;i++){
int r,c,val;
scanf("%d%d%d",&r,&c,&val);
Node* it = (Node*)malloc(sizeof(Node));
it -> row = r;
it -> col = c;
it -> val = val;
Node *now = rhead[r];
Node *las = NULL;
if(now -> rig == NULL)now -> rig = it,it -> rig = now,sum++;
else {
las = now;
now = now -> rig;
while(now -> col < c && now -> rig != rhead[r])las = now,now = now -> rig;
if(now -> col == c)now -> val += val;
else if(now -> rig == rhead[r] && now -> col < c)now -> rig = it,it -> rig = rhead[r],sum++;
else las -> rig = it,it -> rig = now,sum++;
}
}
scanf("%d%d%d",&n2,&m2,&t2);
if(n1 != n2 || m1 != m2)return printf("Illegal!"),0;
for(int i=1;i<=t2;i++){
int r,c,val;
scanf("%d%d%d",&r,&c,&val);
Node* it = (Node*)malloc(sizeof(Node));
it -> row = r;
it -> col = c;
it -> val = val;
Node *now = rhead[r];
Node *las = NULL;
if(now -> rig == NULL)now -> rig = it,it -> rig = now,sum++;
else {
las = now;
now = now -> rig;
while(now -> col < c && now -> rig != rhead[r])las = now,now = now -> rig;
if(now -> col == c){
now -> val += val;
if(now -> val == 0){
sum--;
las -> rig = now -> rig;
}
}
else if(now -> rig == rhead[r] && now -> col < c)now -> rig = it,it -> rig = rhead[r],sum++;
else las -> rig = it,it -> rig = now,sum++;
}
}
printf("%d %d %d\n",n1,m1,sum);
for(int i=1;i<=n1;i++){
if(rhead[i] -> rig == NULL)continue;
for(Node* j=rhead[i]->rig;j != rhead[i];j=j->rig){
if(j -> val == 0)continue;
printf("%d %d %lld\n",j -> row,j -> col,j -> val);
}
}
return 0;
}
T3:文字编辑
题目
一篇文章由n个汉字构成,汉字从前到后依次编号为1,2,……,n。 有四种操作:
A i j表示把编号为i的汉字移动编号为j的汉字之前;
B i j表示把编号为i的汉字移动编号为j的汉字之后;
Q 0 i为询问编号为i的汉字之前的汉字的编号;
Q 1 i为询问编号为i的汉字之后的汉字的编号。
规定:1号汉字之前是n号汉字,n号汉字之后是1号汉字。
INPUT
第1行,1个整数T,表示有T组测试数据
随后的每一组测试数据中,第1行两个整数n和m,用空格分隔,分别代表汉字数和操作数,第2至m+1行,每行包含3个常量s、i和j,用空格分隔,s代表操作的类型,若s为A或B,则i和j表示汉字的编号,若s为Q,i代表0或1,j代表汉字的编号。
OUTPUT
若干行,每行1个整数,对应每个询问的结果汉字编号。
Sample
INPUT
1
9999 4
B 1 2
A 3 9999
Q 1 1
Q 0 3
OUTPUT
4
9998
Time limit
1000ms
Memory limit
2MB
Data range
1≤T≤9999,2≤n≤9999,1≤m≤9999
Solution
观察发现所有操作只改变前后关系,并不改变其数值,双向链表模拟操作即可
赛时AC代码
#include <bits/stdc++.h>
using namespace std;
int bef[100005];
int aft[100005];
int main(){
int T;scanf("%d",&T);
while(T--){
int n,m;scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
bef[i] = i - 1,aft[i] = i + 1;
bef[1] = n;
aft[n] = 1;
while(m--){
char ch[10];
scanf("%s",ch);
if(ch[0] == 'A'){
int x,y;scanf("%d%d",&x,&y);
aft[bef[x]] = aft[x];
bef[aft[x]] = bef[x];
bef[x] = bef[y];
aft[bef[y]] = x;
aft[x] = y;
bef[y] = x;
}
else if(ch[0] == 'B'){
int x,y;scanf("%d%d",&x,&y);
aft[bef[x]] = aft[x];
bef[aft[x]] = bef[x];//a y x b
aft[x] = aft[y];
bef[aft[y]] = x;
aft[y] = x;
bef[x] = y;
}
else {
int opt;scanf("%d",&opt);
int x;scanf("%d",&x);
if(opt == 1)printf("%d\n",aft[x]);
else printf("%d\n",bef[x]);
}
}
}
return 0;
}
T4:幸福指数
题目
人生中哪段时间最幸福?幸福指数可能会帮你发现。幸福指数要求:对自己每天的生活赋予一个幸福值,幸福值越大表示越幸福。一段时间的幸福指数就是:这段时间的幸福值的和乘以这段时间的幸福值的最小值。幸福指数最大的那段时间,可能就是人生中最幸福的时光。
INPUT
第1行,1个整数n,表示要考察的天数。
第2行,n个整数Hi,用空格分隔,Hi表示第i天的幸福值。
OUTPUT
第1行,1个整数,表示最大幸福指数。
第2行,2个整数l和r,用空格分隔,表示最大幸福指数对应的区间[l,r]。如果有多个这样的区间,输出最长最左区间。
Sample
INPUT
7
6 4 5 1 4 5 6
OUTPUT
60
1 3
Time limit
100ms
Memory limit
64MB
Data range
1≤n≤100000 0≤Hi≤1000000
Solution
本题与 POJ-2559 Largest Rectangle in a Histogram除了数值算法不同以外几乎相同,然而我没看出来,赛时瞎写了暴力,赛后立即顿悟。
本题的解法是单调栈
先来复习一下单调栈
定义:栈内元素单调递增或者单调递减的栈
性质:
1.单调栈里的元素具有单调性
2.元素加入栈前,会将栈顶所有破坏单调性的元素都删除
3.利用单调栈可以找到元素向左遍历第一个比他小/大的元素
4.时间复杂度O(n)
再来看本题,幸福指数的计算需要幸福值的和和幸福值的最小值,于是我们可以做一个结构体,里面存区间和与区间最小值,当当前要插入的数比栈顶区间最小值小时,将这些弹出来的结构体捏成一个新的结构体再放回去,期间不断计算当前的幸福指数并更新ans即可得出答案。
赛后AC代码
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define int long long
int read(){
int x = 0,y = 1;
char ch = getchar();
while(ch > '9' || ch < '0')
if(ch == '-')y = -1,ch = getchar();
else ch = getchar();
while(ch >= '0' && ch <= '9')x = x * 10 + ch - '0',ch = getchar();
return x * y;
}
struct Node{
int high,wide;
int l,r;
}stac[100005];
Node NewNode(int x,int y,int ll,int rr){
Node v;
v.high = x,v.wide = y;
v.l = ll,v.r = rr;
return v;
}
int n,T;
int a[100005];
int top;
int ans,mxl,mxr;
signed main(){
scanf("%d",&n);
for(int i=1;i<=n+1;i++){
if(i != n + 1)a[i] = read();
else a[i] = 0;
if(a[i] >= stac[top].high)stac[++top] = NewNode(a[i],a[i],i,i);
else {
int wid = 0,l,r = stac[top].r;
while(a[i] < stac[top].high && top){
wid += stac[top].wide;
l = stac[top].l;
if(ans < stac[top].high * wid){
ans = stac[top].high * wid;
mxl = l,mxr = r;
} else if(ans == stac[top].high * wid){
if(mxr - mxl < r - l)mxl = l,mxr = r;
else if(mxr - mxl == r - l && l < mxl)mxl = l,mxr = r;
}
top--;
}
Node t = NewNode(a[i],wid + a[i],l,i);
if(ans < t.wide * t.high){
ans = t.wide * t.high;
mxl = l,mxr = i;
} else if(ans == t.wide * t.high){
if(mxr - mxl < i - l)mxl = l,mxr = i;
else if(mxr - mxl == i - l && l < mxl)mxl = l,mxr = i;
}
stac[++top] = t;
}
}
if(!mxl && !mxr){printf("0\n1 %d",n);}
else printf("%lld\n%lld %lld",ans,mxl,mxr);
return 0;
}