SDSC2018 Day1
$ \Rightarrow $ 戳我下载文件 密码:h9ej
Problem A. 线段树 (segmenttree.c/cpp/pas)
Input file: segmenttree.in
Output file: segmenttree.out
Time Limit: 3 seconds
Memory Limit: 512 megabytes
线段树(Segment Tree)是一种二叉树,可视为树状数组的变种,最早出现在 2001 年,由程式竞赛选手发明。
此数据结构实际应用用途不大,但由于其程式易于实作而被广泛应用于程式竞赛当中。
其用途是在 $ O(log_N) $ 查询一个指定区间内的信息,并可在同样的时间复杂度支持更新等操作。
为了学习线段树,你得到了一个长度为 $ n $ 的数组 $ a $(下标从 $ 1 $ 开始),所有元素初始值均为 $ 0 $ 。
你需要进行 q 次操作,操作有三种:
$ 1 \quad x \quad y $ 表示:把 $ a[x] $ 的值修改为值修改为 $ y $ 。
$ 2 \quad x \quad y $ 表示:把 $ a[x] $ 的值修改为值修改为 $ a[x] + y $ 。
$ 3 \quad y $ 表示:把数组中全部的值修改为 $ y $ 。
进行每次操作之后,你都要查询数组中所有元素的和。
本来这道题是在仙人掌上对一条路径操作并支持历史版本查询,
鉴于这只是一套 NOIP 模拟题的签到题,你只需要解决这个弱化版的问题。
Input
第 $ 1 $ 行为两个整数 $ n,q $ ,以空格隔开。
接下来 $ q $ 行,每行格式为 $ 1 \quad x \quad y $ 或 $ 2 \quad x \quad y $ 或 $ 3 \quad y $ 。
Output
输出 $ q $ 行,每行表示进完当前操作后数组中所有元素的和。
Examples
input
5 3
1 3 3
3 2
2 3 3
output
3
10
13
大样例见下发文件。
Notes
对于 $ 10 $ % 的数据,$ n = 1,q ≤ 10 $ 。
对于 $ 40 $ % 的数据,$ n ≤ 10^4,q ≤ 10^3 $ 。
对于 $ 70 $ % 的数据,$ n ≤ 10^ 6,q ≤ 10^5 $ 。
对于 $ 100 $ % 的数据,$ 1 ≤ n ≤ 10^7,1 ≤ q ≤ 10^6,1 ≤ x ≤ n,0 ≤ y ≤ 100 $ 。
本题输入规模较大,为减小输入数据需要的时间对你的程序效率造成的影响,在下发文件里提供 了一个基于 fread 的输入输出模板。
如果你要使用该模板,请在文件输入输出而不是标准输入输出 下测试你的程序。
题解
-
裸线段树,其实用常数好的线段树可以直接水过,只能说明数据卡得不够好,但是正解一定不是线段树。 -
我们发现这道题只涉及到了单点和全局的操作所以:
对于操作3,我们记录一个当前3操作的版本号和值,然后直接更新sum值
对于操作1、2,我们首先比较它们的版本号是否与最近一次3操作的版本号相等,如果不相等先强制更新,然后单点修改
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXSIZE=50000020; //读入缓存大小,不要改动
int bufpos;
char buf[MAXSIZE];
int re(){ //读入一个int类型的整数
int val = 0;
for(; buf[bufpos] < '0' || buf[bufpos] > '9'; bufpos ++);
for(; buf[bufpos] >= '0' && buf[bufpos] <= '9'; bufpos ++)
val = val * 10 + buf[bufpos] - '0';
return val;
}
int n,q,a[10000010],sum,flag,c[10000010],cnt;
int main(){
freopen("segmenttree.in","r", stdin);
freopen("segmenttree.out","w", stdout);
buf[fread(buf, 1, MAXSIZE, stdin)] = '\0';
bufpos = 0;
n = re(); q = re();
for(int i = 1; i <= q; i ++){
int opt = re();
if(opt == 1){
int x = re(), y = re();
if(c[x]<cnt)a[x]=flag,c[x]=cnt;
sum+=y-a[x];
a[x]=y;
printf("%d\n",sum);
}
if(opt == 2){
int x = re(), y = re();
if(c[x]<cnt)a[x]=flag,c[x]=cnt;
sum+=y;
a[x]+=y;
printf("%d\n",sum);
}
if(opt == 3){
int y = re();
flag=y;
cnt++;
sum=y*n;
printf("%d\n",sum);
}
// addedge(u, v, w);
}
return 0;
}
Problem B. 最长路 (lpsa.c/cpp/pas)
Input file: lpsa.in
Output file: lpsa.out
Time Limit : 1 second
Memory Limit: 512 megabytes
LPSA 算法(Longest Path Slower Algorithm)算法是于 1926 年发表的求多源最长路径的一种算法,
从名字我们就可以看出,这种算法在效率上一定有过人之处。
为了学习 LPSA 算法,你得到了张 $ n $ 个点,$ m $ 条边的有向图,要求求出整张图所有的路径中最 长路的长度,保证最长路一定存在。
本来这道题是让你支持强制在线 Link-Cut 操作与可持久化的,
鉴于这只是一套 NOIP 模拟题的 签到题,你只需要解决这个弱化版的问题。
Input
第 $ 1 $ 行两个整数 $ n,m $ 。
第 $ 2 $ 行到第 $ m + 1 $ 行,每行三个整数 $ u,v,w $ ,表示一条 $ u $ 到 $ v $ ,长为 $ w $ 的边。
Output
一行一个数,表示整张图所有的路径中最长路的长度。
Examples
input
3 3
1 2 1
2 3 1
1 3 3
output
3
大样例见下发文件。
Note
子任务 1 (20 分):满足 $ 1 ≤ n ≤ 100,1 ≤ m ≤ 1000 $ 。
子任务 2 (20 分):满足 $ 1 ≤ n ≤ 1000,1 ≤ m ≤ 10000 $ 。
子任务 3 (60 分):满足 $ 1 ≤ n ≤ 10^5,1 ≤ m ≤ 10^6 $ 。
对于 100% 的数据,满足 $ 1 ≤ u,v ≤ n,1 ≤ w ≤ 10^9 $ 。
本题输入规模较大,为减小输入数据需要的时间对你的程序效率造成的影响,
在下发文件里提供 了一个基于 fread 的输入输出模板。如果你要使用该模板,请在文件输入输出而不是标准输入输出 下测试你的程序。
题解
-
首先,这个LPSA好像是1984年发表的单源最长路来着。。。。。。
-
40分:用裸的LPSA直接求解,时间复杂度 $ O(n^2 \times log_n ) $
-
100分:直接进行拓扑排序,边排序边累计答案,时间复杂度 $ O(n+m) $
代码
40分LPSA
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXSIZE=50000020;
int bufpos;
char buf[MAXSIZE];
int re(){
int val = 0;
for(; buf[bufpos] < '0' || buf[bufpos] > '9'; bufpos ++);
for(; buf[bufpos] >= '0' && buf[bufpos] <= '9'; bufpos ++)
val = val * 10 + buf[bufpos] - '0';
return val;
}
struct edge{
int to, nxt; ll w;
}e[10005];
int h[1005], cnt;
void addedge(int x, int y, ll w){
cnt++; e[cnt].to = y; e[cnt].w = w; e[cnt].nxt = h[x]; h[x] = cnt;
}
ll dis[1005], vis[1005];
int n, m;
ll spfa(int s){
memset(dis, 0, sizeof(dis));
memset(vis, 0, sizeof(vis));
vis[s] = 1; queue<int> q; q.push(s);
while(!q.empty()){
int x = q.front(); q.pop();
for(int i = h[x]; i; i = e[i].nxt){
if(dis[e[i].to] < dis[x] + e[i].w){
dis[e[i].to] = dis[x] + e[i].w;
if(!vis[e[i].to]){
vis[e[i].to] = 1; q.push(e[i].to);
}
}
}
vis[x] = 0;
}
ll mx = 0;
for(int i = 1; i <= n; i ++){
mx = max(mx, dis[i]);
}
return mx;
}
int main(){
buf[fread(buf, 1, MAXSIZE, stdin)] = '\0';
bufpos = 0;
n = re(); m = re();
for(int i = 1; i <= m; i ++){
int u = re(), v = re(), w = re();
addedge(u, v, w);
}
ll ans = 0;
for(int i = 1; i <= n; i ++) ans = max(ans, spfa(i));
printf("%lld\n", ans);
return 0;
}
100分拓扑排序
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXSIZE=50000020; //读入缓存大小,不要改动
int bufpos;
char buf[MAXSIZE];
int re(){ //读入一个int类型的整数
int val = 0;
for(; buf[bufpos] < '0' || buf[bufpos] > '9'; bufpos ++);
for(; buf[bufpos] >= '0' && buf[bufpos] <= '9'; bufpos ++)
val = val * 10 + buf[bufpos] - '0';
return val;
}
int n, m;
struct edge{ int v,w,nxt; }e[1000005];
int head[100005],tot,in[100005];
void add(int u,int v,int w){ e[++tot].v=v; e[tot].w=w; e[tot].nxt=head[u]; head[u]=tot; }
queue<int>q;
ll dis[100005];
int main(){
freopen("lpsa.in", "r", stdin);
freopen("lpsa.out", "w", stdout);
buf[fread(buf, 1, MAXSIZE, stdin)] = '\0';
bufpos = 0;
n = re(); m = re();
for(int i = 1; i <= m; i ++){
int u = re(), v = re(), w = re();
add(u,v,w);
++in[v];
// addedge(u, v, w);
}
ll ans=-1;
for(int i=1;i<=n;++i) if(in[i]==0) q.push(i);
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i;i=e[i].nxt){
dis[e[i].v]=max(dis[e[i].v],dis[u]+e[i].w);
ans=max(ans,dis[e[i].v]);
--in[e[i].v];
if(in[e[i].v]==0) q.push(e[i].v);
}
}
printf("%lld\n", ans);
return 0;
}
Problem C. 快餐店 (foodshop.c/cpp/pas)
Input file: foodshop.in
Output file: foodshop.out
Time Limit : 2 seconds
Memory Limit: 512 megabytes
杰哥和 HLY 打算在城市 C 开设两家外送快餐店。
送餐到某一个地点的时间与外卖店到该地点之 间最短路径长度是成正比的,
台湾最速传说杰哥希望快餐店的地址选在离每个顾客的最小距离之和最小的地方,
王道征途 HLY 希望快餐店的地址选在离每个顾客的最小距离之和最大的地方。
快餐店的顾客分布在城市 C 的 $ n $ 个建筑中,这 $ n $ 个建筑通过恰好 $ m $ 条双向道路连接起来,可能存在两条道路连接了相同的两个建筑。
任意两个建筑之间至少存在一条由双向道路连接而成的路径。
快餐店可以开设在任一建筑中,也可以开设在任意一条道路的某个位置上(该位置与道路两端的建筑 的距离不一定是整数)。
现给定城市 C 的地图(道路分布及其长度),请帮杰哥和 HLY 找出最佳的快餐店选址,输出其 与最远的顾客之间的距离。
Input
第 $ 1 $ 行两个整数 $ n,m $ 。
第 $ 2 $ 行到第 $ m + 1 $ 行,每行三个整数 $ u,v,w $ ,表示一条连接了 $ u $ 和 $ v $ ,长为 $ w $ 的边。
Output
一行两个实数,分别保留一位小数。
分别表示杰哥和 HLY 的快餐店的地址选在离每个顾客的最小距离之和。注意,小数位数不正确不能得分。
Examples
input
3 3
1 2 1
2 3 1
3 1 1
output
2.0 2.5
大样例见下发文件。
Notes
对于 100% 的数据,$ 1 ≤ n ≤ 1000,1 ≤ m ≤ 5000,0 ≤ w ≤ 10^6 $ 。
子任务 1 (10 分):满足 $ m = n−1 $ 且第 $ i $ 条边连接 $ i $ 号建筑与 $ i + 1 $ 号建筑。
子任务 2 (15 分):满足 $ m = n−1 $ 。
子任务 3 (20 分):满足 $ w = 1 $ 。
子任务 4 (25 分):满足 $ 1 ≤ n ≤ 100 $ 。
子任务 5 (30 分):没有特殊限制。