T1 拉力赛(lali)
1000ms 65536KB
【description】
车展结束后,游乐园决定举办一次盛大的山道拉力赛,平平和韵韵自然也要来参加大赛。
赛场上共有n个连通的计时点,n-1条赛道(构成了一棵树)。每个计时点的高度都不相同(父结点的高度必然大于子结点),相邻计时点间由赛道相连。由于马力不够,所以韵韵的遥控车只能从高处驶向低处。而且韵韵的车跑完每条赛道都需花费一定的时间。
举办方共拟举办m个赛段的比赛,每次从第u个计时点到第v个计时点,当然其中有不少比赛韵韵的遥控车是不能参加的(因为要上坡)。平平想知道他能参加多少个赛段的比赛,并且想知道他完成这些赛段的总用时。
【input】
第一行两个整数n,m。
接下来n-1行每行3个整数a、b、t。
表示韵韵的遥控车可以花t秒从第a个计时点到第b个计时点。
接下来m行每行2个整数u、v,意义如描述所示。
【output】
第一行输出一个正整数,表示能参加的赛段数。
第二行输出一个正整数,表示总用时。
【sample input】
input1:
6 2
1 2 1
2 4 1
2 5 1
5 6 1
1 3 1
2 6
4 5
【sample output】
output1:
1
2
【Data Constraint】
第一个计时点的高度是最高的;
u≠v;
对于50%的数据 n≤1000 m≤1000;
对于100%的数据 n≤10000 m≤100000;
答案小于2^64。
所以题目数据居然还是有U == V???
介绍一下此题的两种写法,一种即是最朴素的LCA,我们判断一下U是不是U 和 V的LCA即可
老师提供了另一种更快的方法,我们直接判断V是不是在U的DFS序的区间内即可(真好的方法)
所以本题是不是应该卡一卡
AC Code:
#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 200005
#define ll long long
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch =='-') w = -1 ; ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}return x * w;}
struct edge{
int to , next , v;
}e[maxn << 1];
int head[maxn] , cnt , fa[maxn] , dep[maxn] , f[maxn][22];
ll val[maxn];
void add(int u , int v , int w){
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
e[cnt].v = w;
}
void dfs(int u , int faz , int depth){
dep[u] = depth;
f[u][0] = faz;
for (rg int i = 1 ; i <= 20 ; ++i) f[u][i] = f[f[u][i - 1]][i - 1];
for (rg int i = head[u] ; i ; i = e[i].next){
rg int to = e[i].to;
if (to != faz){
val[to] = val[u] + e[i].v;
dfs(to , u , depth + 1);
}
}
}
int lca(int a , int b){
if (dep[a] > dep[b]) swap(a , b);
for (rg int i = 20 ; i >= 0 ; --i)
if (dep[b] - dep[a] >= (1 << i)) b = f[b][i];
if (a == b) return a;
for (rg int i = 20 ; i >= 0 ; --i){
if (f[a][i] != f[b][i])
a = f[a][i] , b = f[b][i];
}
return f[a][0];
}
int main(){
rg int n = read() , m = read() , u , v , w;
for (rg int i = 1 ; i < n ; ++i){
u = read() , v = read() , w = read();
add(u , v , w);
add(v , u , w);
}
dfs(1 , 0 , 1);
rg int cnt = 0;
ll ans = 0;
for (rg int i = 1 ; i <= m ; ++i){
u = read() , v = read();
rg int lcax = lca(u , v);
//if (dep[u] > dep[v]) continue;
if (lcax == u){
++cnt;
ans += val[u] + val[v] - (val[lcax] << 1);
}
}
printf("%d\n%lld\n" , cnt , ans);
return 0;
}
T2 [HNOI2009]最小圈
https://www.luogu.org/problemnew/show/P3199
一道省选题,当然题解中的什么定理我真不值得,但是我会分数规划啊(然而考试中没想出来)
分数规划(这里是01分数规划),能解决 形如
的问题,那么这里的ai就是边权,bi很简单,是点的个数,由题意得
(mid为二分的答案)
因此化简得
,因此按照分数规划的思路,我们就将边权设为
,然后用SPFA判断有无负环。有负环,说明答案还有较小的值未取到,我们就进一步二分
这道题BFS版找负环会挂掉,我们得用DFS版
AC Code:`
#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 500005
#define eps 1e-10
#define ll long long
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch =='-') w = -1 ; ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}return x * w;}
struct edge{
int to , next;
double v;
}e[maxn << 1];
int head[maxn] , cnt , p , flag , tot[3005] , n , m , q[maxn] , vis0;
double save[maxn] , dis[3005];
int vis[maxn];
void add(int u , int v , double w){
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
e[cnt].v = w;
save[cnt] = w;
}
bool spfa(int u){
vis[u] = vis0;
for (rg int i = head[u] ; i ; i = e[i].next){
rg int to = e[i].to;
if (dis[to] > dis[u] + e[i].v){
dis[to] = dis[u] + e[i].v;
if (vis[to] == vis0 || spfa(to)){
return 1;
}
}
}
vis[u]--;
return 0;
}
bool check(double mid){
memset(dis , 0 , sizeof(dis));
++vis0;
for (rg int i = 1 ; i <= cnt ; ++i) e[i].v = save[i] - mid;
for (rg int i = 1 ; i <= n ; ++i)
if (spfa(i)) return 1;
return 0;
}
int main(){
n = read() , m = read();
rg int u , v;
double w;
for (rg int i = 1 ; i <= m ; ++i){
u = read() , v = read();
scanf("%lf" , &w);
add(u , v , w);
}
double l = -1000000001.0 , r = 1000000001.0 , ans = 0;
while (r - l >= eps){
double mid = (l + r) / 2;
if (check(mid)) ans = mid , r = mid;
else l = mid;
}
printf("%.8lf" , ans);
return 0;
}
T3 可见点数(see)
题目描述
ZPS 经过长期的努力争取,终于成为了0901班的领操员,他要带领0901班参加广播操比赛。 现在0901班的队伍可以看作是一个 n*n 的点阵,每个人都站在格点上。现在作为领操员的 ZPS 站(0,0)点,他想知道如果0901班的队伍站齐了,他能看到多少个人的脸(假设每个人的 身高相同,体积相同)。
输入输出格式
输入格式:
一个正整数 n。
输出格式:
ZPS 能看到多少个人的脸(当然他是看不到自己的脸的)。
输入输出样例
暂无测试点
说明
100%的数据,n<=100000。
所以这是考场上唯一A的题?晕
其实这道题用埃氏筛(也就是不带优化的线性筛)即可,但我用了容斥(因为最近做容斥的题很多。。)
AC Code:
#include<bits/stdc++.h>
#define rg register
#define il inline
#define maxn 500005
#define ll long long
using namespace std;
il int read(){rg int x = 0 , w = 1;rg char ch = getchar();while (ch < '0' || ch > '9'){if (ch =='-') w = -1 ; ch = getchar();}while (ch >= '0' && ch <= '9'){x = (x << 3) + (x << 1) + ch - '0';ch = getchar();}return x * w;}
ll f[maxn];
int primes[maxn] , cnt , res , a[maxn] , q[maxn];
bool vis[maxn];
void prime(int n){
vis[1] = 1;
for (rg int i = 2 ; i <= n ; ++i){
if (!vis[i]) primes[++cnt] = i;
for (rg int j = 1 ; j <= cnt && i * primes[j] <= n; ++j){
vis[i * primes[j]] = 1;
if (!(i % primes[j])) break;
}
}
}
void fenjie(int n){
res = 0;
rg int x = n , totn = 0;
while(vis[x] && x != 1){
if (!(x % primes[++totn])){
a[++res] = primes[totn];
x /= primes[totn];
}
while(!(x % primes[totn]))
x /= primes[totn];
}
if (x != 1 && !vis[x]) a[++res] = x;
}
ll shai(int n){
rg int k = 0 , len = 0;
q[0] = -1;
//cout << res << endl;
for (rg int i = 1 ; i <= res ; ++i){
k = len;
for (rg int j = 0 ; j <= k ; ++j){
q[++len] = q[j] * a[i] * (-1);
}
}
rg ll ans2 = 0;
for (rg int i = 1 ; i <= len ; ++i){
ans2 += n / q[i];
}
return (n - ans2) << 1;
}
int main(){
int n = read();
if (n == 1){
cout << '0';
return 0;
}
prime(n);
f[0] = f[1] = 0 , f[2] = 3 , f[3] = 5;
if (f[n]){
printf("%lld\n" , f[n]);
return 0;
}
rg ll ans = 5;
for (rg int i = 4 ; i <= n ; ++i){
rg int x = i - 1;
if (!vis[x]){
ans += (x - 1) << 1;
continue;
}else{
fenjie(x);
ans += shai(x);
}
}
cout << ans;
return 0;
}