A - As Fast As Possible
首先,明确一点,大巴车是可以回去接人的。
在这里,我们不难发现,为了达到最优解,那些还未坐车的“大部队”和坐了车的“先行部队”的距离S是固定的。
画出对应的t-x图:
我们可以列出一个方程组:
指的是总花费时间。
我们假设每个bus向前的花费时间是x,返回接学生花费时间是y,那么,最优解的情况是每一位学生都能上车一次,因为车的载客量是K,所以我们的tim是N除以K向上取整。
然后连立这两个方程就可以通过x、y之间的固定关系求解了。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define eps 1e-9
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
double L, v1, v2;
int N, K, tim;
int main()
{
scanf("%d%lf%lf%lf%d", &N, &L, &v1, &v2, &K);
tim = N / K + (bool)(N % K);
double ans;
double A = 1. * tim + 1. * (tim - 1.) * (v2 - v1) / (v2 + v1);
double B = (v2 - v1) / A;
ans = L / (B + v1);
printf("%lf\n", ans);
return 0;
}
B - Connecting Universities
一个树上的思维操作,跟结点的size有关哦(非一般size,是有效size)
就是给出2*K个点(一开始我竟然读成了两千个点),然后我们可以画图来看,能发现其中的某些规律。
假设如图,红色的点是题目中说到 的2*K个点。
那么如果要去找最优解,也就是应该这样配对{5, 6}、{1, 4}也可以是{1, 6}、{5, 4}。那么,我们可以从每条边产生的贡献角度来考虑这个问题,1-2这条边要贡献一次,3-5、2-3的边也是会贡献一次,然后为了达成最优解,2-4这条边必须贡献两次,4-6也是必须贡献一次才可以。所以我们不如去看如何处理每条边的贡献。
那么问题似乎被简化了,我们把每个结点的入度的边等效给结点上面去,(代表了3这号点的代表边是2-3),3的子树当中有一个标记点;2的子树中有3个,但是呢一共就只有4个点,所以其实1-2的边只用了4-3=1次。我们要考虑目前点的size和2*K-size的关系。
所以,这道题简化一下,就是求树的子结点(包括本身)的有效结点个数,dfs的O(N)搜索即可。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define eps 1e-9
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 2e5 + 7;
int N, K, head[maxN], cnt, siz[maxN] = {0};
bool op[maxN] = {false};
struct Eddge
{
int nex, to;
Eddge(int a=-1, int b=0):nex(a), to(b) {}
}edge[maxN<<1];
inline void addEddge(int u, int v)
{
edge[cnt] = Eddge(head[u], v);
head[u] = cnt++;
}
inline void _add(int u, int v) { addEddge(u, v); addEddge(v, u); }
ll ans = 0;
void dfs(int u, int fa)
{
siz[u] += op[u];
for(int i=head[u], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
if(v == fa) continue;
dfs(v, u);
siz[u] += siz[v];
}
ans += min(siz[u], 2 * K - siz[u]);
}
inline void init()
{
cnt = 0;
for(int i=1; i<=N; i++) head[i] = -1;
}
int main()
{
scanf("%d%d", &N, &K);
init();
for(int i=(K<<1), x; i>=1; i--)
{
scanf("%d", &x); op[x] = true;
}
for(int i=1, u, v; i<N; i++)
{
scanf("%d%d", &u, &v);
_add(u, v);
}
dfs(1, 0);
printf("%lld\n", ans);
return 0;
}
C - Little Boxes
特地卡了
long long
这两个定义方式,看很多人写的是大数,但是,实际上使用
__int128
就可以解决这个问题了。(很多编译器不支持__int128)但是测评机支持。
__int128的读入,需要使用自定义读入。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define eps 1e-9
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
__int128 A, B, C, D, sum;
inline __int128 read()
{
__int128 x = 0;
char ch = getchar();
while(ch >= '0' && ch <= '9')
{
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
inline void out(__int128 x)
{
if(x > 9) out(x / 10);
putchar(x % 10 + '0');
}
int main()
{
int T; scanf("%d", &T);
getchar();
while(T--)
{
A = read(); B = read(); C = read(); D = read();
sum = A + B + C + D;
out(sum); puts("");
}
return 0;
}
D - Cube Stacking
【种类并查集基础】
首先,我们来推一下样例,样例就是把1号结点放在6上面,把2号结点放在4上面,然后把4号结点放在6这个树上面,我们来看一下:
我们建立反向树,其实就是一个种类并查集的过程了。
我们一开始的时候,看向只有2、4结点的时候,是4结点权值为0,2结点权值为1(因为它下面的子树有一个结点),然后再接着合并的时候,4号结点得到了6号结点的size=2,然后2号结点被跟着更新。
(POJ对头文件的限制,会让一部分头文件变成CE)。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define eps 1e-9
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 3e4 + 5;
int Q, root[maxN], siz[maxN] = {0}, sum[maxN];
int fid(int x)
{
if(x == root[x]) return x;
int fa = root[x];
root[x] = fid(root[x]);
siz[x] = siz[x] + siz[fa];
return x = root[x];
}
void Mix(int x, int y) //把x这一列放到y这一列的上面去
{
int u = fid(x), v = fid(y);
if(u == v) return;
siz[u] = sum[v];
root[u] = v;
sum[v] += sum[u];
}
inline void init()
{
for(int i=1; i<maxN; i++)
{
root[i] = i;
sum[i] = 1;
}
}
int main()
{
scanf("%d", &Q);
init();
char op[3]; int x, y, fa;
while(Q--)
{
scanf("%s", op);
if(op[0] == 'M')
{
scanf("%d%d", &x, &y);
Mix(x, y);
}
else
{
scanf("%d", &x);
fa = fid(x);
printf("%d\n", siz[x]);
}
}
return 0;
}
E - Rank of Tetris
我们可以首先看到题,可以先去处理相等的,也就是“=”号结点,不难发现,他们应当缩成一个点,然后我们再去建边,利用拓扑排序的性质(或者有的同学自学了Tarjan)来判断有没有环,如果有环了,说明构成了冲突,如果从任意一个0度的点出发,且仅从一个点出发,跑一次,如果所有点跑完了,就是一个确定的,如果跑出来环了,跑不下去了,那么就是说明了构成了环了,产生冲突。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxN=10005;
int N, M, root[maxN], head[maxN<<2], cnt, point, degree[maxN];
bool flag;
map<pair<int, int>, bool> mp;
struct eddge
{
int no, to;
char state;
eddge(int a=0, int b=0, char c=NULL):no(a), to(b), state(c) {}
}ship[maxN<<1];
struct node
{
int nex, to, val;
node(int a=-1, int b=0, int c=0):nex(a), to(b), val(c) {}
}edge[maxN<<1];
void addEddge(int u, int v, int val)
{
edge[cnt]=node(head[u], v, val);
head[u]=cnt++;
}
queue<int> Q;
void init()
{
flag=false;
mp.clear();
while(!Q.empty()) Q.pop();
cnt=0; point=N;
memset(head, -1, sizeof(head));
memset(degree, 0, sizeof(degree));
for(int i=0; i<N; i++) { root[i]=i; }
}
int fid(int x) { return x==root[x]?x:(root[x]=fid(root[x])); }
void mix(int x, int y)
{
int u=fid(x), v=fid(y);
if(u!=v)
{
root[u] = v;
point--;
}
}
void bfs()
{
while(!Q.empty())
{
if(Q.size()>1) flag=true;
int now=Q.front(); Q.pop();
for(int u=head[now]; u!=-1; u=edge[u].nex)
{
int v=edge[u].to;
degree[v]--;
if(degree[v]==0)
{
Q.push(v);
point--;
}
}
}
}
int main()
{
while(scanf("%d%d", &N, &M)!=EOF)
{
init();
for(int i=1; i<=M; i++)
{
scanf("%d %c %d", &ship[i].no, &ship[i].state, &ship[i].to);
if(ship[i].state == '=')
{
mix(ship[i].no, ship[i].to);
}
}
for(int i=1; i<=M; i++)
{
if(ship[i].state == '=') continue;
if(ship[i].state == '<')
{
if(mp[make_pair(fid(ship[i].to), fid(ship[i].no))]) continue;
mp[make_pair(fid(ship[i].to), fid(ship[i].no))]=true;
addEddge(fid(ship[i].to), fid(ship[i].no), 1);
degree[fid(ship[i].no)]++;
}
else
{
if(mp[make_pair(fid(ship[i].no), fid(ship[i].to))]) continue;
mp[make_pair(fid(ship[i].no), fid(ship[i].to))]=true;
addEddge(fid(ship[i].no), fid(ship[i].to), 1);
degree[fid(ship[i].to)]++;
}
}
for(int i=0; i<N; i++)
{
if(degree[fid(i)]==0 && root[i]==i)
{
Q.push(fid(i));
point--;
}
}
bfs();
if(point>0) printf("CONFLICT\n");
else if(flag) printf("UNCERTAIN\n");
else printf("OK\n");
}
return 0;
}
F - Mike and Shortcuts
题意:有N个十字路口,我们是从十字路口1号出发,问到1~N这几号十字路口的最短举例,然后,对于每个十字路口,都有一个近路可以走,路径长度都是1,并且是单向边。最后输出从1出发到1~N这些点的最短举例。
我们可以直接建边,但是别忘了考虑,我们可以从i点回到i-1这个点的可能性,因为有可能回溯回来再走是更优的解。
所以,我们的做法就是去跑一个线形dp最短路即可。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <unordered_map>
#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 2e5 + 7;
int N, a[maxN], dp[maxN], head[maxN], cnt;
struct Eddge
{
int nex, to;
Eddge(int a=-1, int b=0):nex(a), to(b) {}
}edge[maxN<<2];
inline void addEddge(int u, int v)
{
edge[cnt] = Eddge(head[u], v);
head[u] = cnt++;
}
void tp()
{
queue<int> Q;
Q.push(1); dp[1] = 0;
while(!Q.empty())
{
int u = Q.front(); Q.pop();
for(int i=head[u], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
if(dp[v] > dp[u] + 1)
{
dp[v] = dp[u] + 1;
Q.push(v);
}
}
}
}
int main()
{
scanf("%d", &N);
for(int i=1; i<=N; i++) head[i] = -1;
for(int i=1; i<=N; i++)
{
scanf("%d", &a[i]);
addEddge(i, a[i]);
if(i < N) addEddge(i, i + 1);
if(i > 1) addEddge(i, i-1);
}
memset(dp, INF, sizeof(dp));
tp();
for(int i=1; i<=N; i++) printf("%d ", dp[i]);
puts("");
return 0;
}