T1
题目:
高精度数是指大大超出了标准数据类型能表示的范围的数,例如10000位整数。很多计算问题的结果都很大,因此,高精度数极其重要。
一般使用一个数组来存储高精度数的所有数位,数组中的每个元素存储该高精度数的1位数字或多位数字。 请尝试计算:N个高精度数的加和。这个任务对于在学习数据结构的你来说应该是小菜一碟。 。
INPUT:
第1行,1个整数N,表示高精度整数的个数。
第2至N+1行,每行1个高精度整数x。
OUTPUT:
1行,1个高精度整数,表示输入的N个高精度数的加和。
Sample
INPUT
3
12345678910
12345678910
12345678910
OUTPUT
37037036730
Time Limit
100ms
Memory Limit
1MB
Data range
1≤N≤10000 x最多100位
Solution:
数组模拟竖式计算即可
赛时AC代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
struct Add{
int a[N];
Add(){memset(a,0,sizeof a);}
void read(){
memset(a,0,sizeof a);
char s[N];
scanf("%s",s+1);
a[0] = strlen(s+1);
for(int i=a[0];i>=1;i--)
a[a[0]-i+1] = s[i] - '0';
}
void print(){
for(int i=a[0];i>=1;i--)
printf("%d",a[i]);
}
Add operator +(const Add &b){
Add res;res.a[0] = max(a[0],b.a[0]);
for(int i=1;i<=res.a[0];i++)
res.a[i] += a[i] + b.a[i],res.a[i+1] += res.a[i]/10,res.a[i] %= 10;
if(res.a[res.a[0]+1])res.a[0] ++;
return res;
}
};
int main(){
int n;
scanf("%d",&n);
Add ans,x;
ans.read();
for(int i=2;i<=n;i++){
x.read();
ans = ans + x;
}
ans.print();
return 0;
}
T2
题目:
二叉树结点间的一种加权距离定义为:上行方向的变数×3 +下行方向的边数×2 。上行方向是指由结点向根的方向,下行方向是指与由根向叶结点方向。 给定一棵二叉树T及两个结点u和v,试求u到v的加权距离。
INPUT:
第1行,1个整数N,表示二叉树的结点数。
随后若干行,每行两个整数a和b,用空格分隔,表示结点a到结点b有一条边,a、b是结点的编号,1≤a、b≤N;根结点编号为1,边从根向叶结点方向。
最后1行,两个整数u和v,用空格分隔,表示所查询的两个结点的编号。
OUTPUT:
1行,1个整数,表示查询的加权距离。
Sample
INPUT
5
1 2
2 3
1 4
4 5
3 4
OUTPUT
8
Time Limit
100ms
Memory Limit
10MB
Data range
1≤N≤100000 1≤u、v≤N
Solution:
LCA,分三种情况,一是u是v祖先,一是v是u祖先,一是u和v的祖先是另一个点
赛时AC代码:
#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int f[N][18],d[N],dis[N];
int ver[N],Next[N],head[N];
int s,n,m,tot,t;
queue<int> q;
int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x;
}
void add(int u,int v){
ver[++tot]=v;Next[tot]=head[u];head[u]=tot;
}
void bfs(int s){
q.push(s);d[s]=1;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=Next[i]){
int y=ver[i];
if(d[y])continue;
d[y]=d[x]+1;
f[y][0]=x;
for(int j=1;j<=t;j++)f[y][j]=f[f[y][j-1]][j-1];
q.push(y);
}
}
}
int lca(int x,int y){
if(d[x]>d[y])swap(x,y);
for(int i=t;i>=0;i--)
if(d[f[y][i]]>=d[x])y=f[y][i];
if(x==y)return x;
for(int i=t;i>=0;i--)
if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
return f[x][0];
}
int main(){
n=read();
s = 1;
t=(int)(log(n)/log(2))+1;//t=logn
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y);
}
bfs(s);
int u = read(),v = read();
int w = lca(u,v);
if(w == u)printf("%d",2 * (d[v] - d[u]));
else if(w == v)printf("%d",3 * (d[u] - d[v]));
else printf("%d",3 * (d[u] - d[w]) + 2 * (d[v] - d[w]));
return 0;
}
T3
题目:
长春市有n个交通枢纽,计划在1号枢纽到n号枢纽之间修建一条轻轨。轻轨由多段隧道组成,候选隧道有m段。每段候选隧道只能由一个公司施工,施工天数对各家公司一致。有n家施工公司,每家公司同时最多只能修建一条候选隧道。所有公司可以同时开始施工。请评估:修建这条轻轨最少要多少天。。
INPUT:
第1行,两个整数n和m,用空格分隔,分别表示交通枢纽的数量和候选隧道的数量。
第2行到第m+1行,每行三个整数a、b、c,用空格分隔,表示枢纽a和枢纽b之间可以修建一条双向隧道,施工时间为c天
OUTPUT:
输出一行,包含一个整数,表示最少施工天数。
Sample
INPUT
6 6
1 2 4
2 3 4
3 6 7
1 4 2
4 5 5
5 6 6
OUTPUT
6
Time Limit
500ms
Memory Limit
10MB
Data range
1 ≤ n ≤ 100000,1 ≤ m ≤ 200000 1 ≤ a, b ≤ n,1 ≤ c ≤ 1000000
Solution:
容易证明,在一个无负权的图中,任意最短路最多经过n-1条边,因为一条最短路不可能经过一个点两次,所以问题转化为了,寻找在1到n的所有最短路中经过的最大边最小的那条最短路。可以二分,也可以利用最小生成树,这里我用的是最小生成树
赛时AC代码:
#include <bits/stdc++.h>
using namespace std;
int n,m;
bool flag;
int vis[100003],fa[100005];
int to[200005],nex[200005],head[100005],val[200005],ce;
int stac[100005],top;
queue<int> q;
struct Node{
int x,y,w;
}Ed[200005];
int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x;
}
void add(int u,int v,int w){
to[++ce] = v,nex[ce] = head[u],head[u] = ce,val[ce] = w;
}
void load(){
for(int i=1;i<=n;i++)fa[i]=i;
}
int find(int x){
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y){
fa[find(x)] = find(y);
}
bool cmp(Node a,Node b){
return a.w < b.w;
}
void kruskal(){
for(int i=1;i<=m;i++)
if(find(Ed[i].y)!=find(Ed[i].x)){
merge(Ed[i].x,Ed[i].y);
add(Ed[i].x,Ed[i].y,Ed[i].w);
add(Ed[i].y,Ed[i].x,Ed[i].w);
if(find(1) == find(n))return;
}
}
void dfs(int x){
if(x == n){
int ans = 0;
for(int i=1;i<=top;i++)
ans = max(ans,val[stac[i]]);
printf("%d",ans);
exit(0);
}
for(int i=head[x];i;i=nex[i]){
int y = to[i];
if(vis[y])continue;
stac[++top] = i;
vis[y] = 1;
dfs(y);
top--;
}
}
int main(){
n = read(),m = read();
load();
for(int i=1;i<=m;i++)
Ed[i].x = read(),Ed[i].y = read(),Ed[i].w = read();
sort(Ed+1,Ed+1+m,cmp);
kruskal();
vis[1] = 1;
dfs(1);
return 0;
}
T4
题目:
小唐正在学习数据结构。他尝试应用数据结构理论处理数据。最近,他接到一个任务,要求维护一个动态数据表,并支持如下操作:
-
插入操作(I):从表的一端插入一个整数。
-
删除操作(D):从表的另一端删除一个整数。
-
取反操作(R):把当前表中的所有整数都变成相反数。
-
取最大值操作(M):取当前表中的最大值。
如何高效实现这个动态数据结构呢?
INPUT:
第1行,包含1个整数M,代表操作的个数。
第2到M+1行,每行包含1个操作。每个操作以一个字符开头,可以是I、D、R、M。如果是I操作,格式如下:I x, x代表插入的整数。 。
OUTPUT:
若干行,每行1个整数,对应M操作的返回值。如果M和D操作时队列为空,忽略对应操作。
Sample
INPUT
6
I 6
R
I 2
M
D
M
OUTPUT
2
2
Time Limit
500ms
Memory Limit
50MB
Data range
2≤M≤1000000 -10000000≤x≤10000000
Solution:
这里我的解法是线段树,每次插入删除后变化区间,在询问时将当前区间存下来,把询问离线化,之后对整个数列建树。
赛时AC代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
int read(){
int x=0,y=1;char ch=getchar();
while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
if(ch=='-')y=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*y;
}
int a[2000005];
int m,now,noww;
struct Req{
int id,x,y;
}ask[2000005];
struct Tree{
int mx,mn;
bool lazy;
}v[10000005];
void pushup(int rt){
v[rt].mx = max(v[rt<<1].mx,v[rt<<1|1].mx);
v[rt].mn = min(v[rt<<1].mn,v[rt<<1|1].mn);
}
void pushdown(int rt){
if(v[rt].lazy){
v[rt<<1].lazy ^= 1;
v[rt<<1|1].lazy ^= 1;
v[rt<<1].mx = -v[rt<<1].mx;
v[rt<<1].mn = -v[rt<<1].mn;
if(v[rt<<1].mx < v[rt<<1].mn)swap(v[rt<<1].mx,v[rt<<1].mn);
v[rt<<1|1].mx = -v[rt<<1|1].mx;
v[rt<<1|1].mn = -v[rt<<1|1].mn;
if(v[rt<<1|1].mx < v[rt<<1|1].mn)swap(v[rt<<1|1].mx,v[rt<<1|1].mn);
}
v[rt].lazy = 0;
}
void plant(int l,int r,int rt){
if(l == r){
v[rt].mx = v[rt].mn = a[l];
return;
}
int mid = (l + r) / 2;
plant(l,mid,rt<<1);
plant(mid+1,r,rt<<1|1);
pushup(rt);
}
void rotate(int L,int R,int l,int r,int rt){
if(L <= l && r <= R){
if(v[rt].lazy)v[rt].lazy = 0;
else v[rt].lazy = 1;
v[rt].mx = -v[rt].mx;
v[rt].mn = -v[rt].mn;
if(v[rt].mx < v[rt].mn)swap(v[rt].mx,v[rt].mn);
return;
}
pushdown(rt);
int mid = (l + r) / 2;
if(L <= mid)rotate(L,R,l,mid,rt<<1);
if(R > mid)rotate(L,R,mid+1,r,rt<<1|1);
pushup(rt);
}
int MAX(int L,int R,int l,int r,int rt){
if(L <= l && r <= R){
return v[rt].mx;
}
pushdown(rt);
int ans = -1e9,mid = (l + r) / 2;
if(L <= mid)ans = max(ans,MAX(L,R,l,mid,rt<<1));
if(R > mid)ans = max(ans,MAX(L,R,mid+1,r,rt<<1|1));
pushup(rt);
return ans;
}
int main(){
m = read();
int nowl = 1;
for(int i=1;i<=m;i++){
char s[10];
scanf("%s",s);
if(s[0] == 'I')a[++now] = read();
else if(s[0] == 'D'){if(nowl > now)continue;nowl++;}
else if(s[0] == 'R'){
if(nowl > now)continue;
ask[++noww].id = 1;
ask[noww].x = nowl;
ask[noww].y = now;
}
else if(s[0] == 'M'){
if(nowl > now)continue;
ask[++noww].id = 2;
ask[noww].x = nowl;
ask[noww].y = now;
}
}
if(now)plant(1,now,1);
for(int i=1;i<=noww;i++){
int L = ask[i].x,R = ask[i].y;
if(ask[i].id == 1){
rotate(L,R,1,now,1);
} else {
printf("%d\n",MAX(L,R,1,now,1));
}
}
return 0;
}