P1993 小 K 的农场
提交答案加入题单复制题目
题目描述
小 K 在 MC 里面建立很多很多的农场,总共 n 个,以至于他自己都忘记了每个农场中种植作物的具体数量了,他只记得一些含糊的信息(共 m 个),以下列三种形式描述:
- 农场 a 比农场 b 至少多种植了 c 个单位的作物;
- 农场 a 比农场 b 至多多种植了 c 个单位的作物;
- 农场 a 与农场 b 种植的作物数一样多。
但是,由于小 K 的记忆有些偏差,所以他想要知道存不存在一种情况,使得农场的种植作物数量与他记忆中的所有信息吻合。
输入格式
第一行包括两个整数 n 和 m,分别表示农场数目和小 K 记忆中的信息数目。
接下来 m 行:
- 如果每行的第一个数是 1,接下来有三个整数 a,b,c,表示农场 a 比农场 b 至少多种植了 c 个单位的作物;
- 如果每行的第一个数是 2,接下来有三个整数 a,b,c,表示农场 a 比农场 b 至多多种植了 c 个单位的作物;
- 如果每行的第一个数是 3,接下来有两个整数 a,b,表示农场 a 种植的的数量和 b 一样多。
输出格式
如果存在某种情况与小 K 的记忆吻合,输出 Yes,否则输出 No。
输入输出样例
输入 #1复制
3 3
3 1 2
1 1 3 1
2 2 3 2
输出 #1复制
Yes
说明/提示
对于 100% 的数据,保证 1≤n,m,a,b,c≤5×103。
实现代码:
cpp
#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
int n, m, cnt, elast[5005], dis[5005], num[5005];
bool vis[5005];
struct edge {
int to, len, next;
} e[15005];
queue<int> q;
void add (int u, int v, int w) {
e[++cnt].to = v;
e[cnt].len = w;
e[cnt].next = elast[u];
elast[u] = cnt;
}
bool spfa (int x) {
dis[x] = 0;
q.push(x);
vis[x] = true;
num[x]++;
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for (int i = elast[u]; i != 0; i = e[i].next)
if (dis[e[i].to] > dis[u] + e[i].len) {
dis[e[i].to] = dis[u] + e[i].len;
if (!vis[e[i].to]) {
q.push(e[i].to);
vis[e[i].to] = true;
num[e[i].to]++;
if (num[e[i].to] == n + 1)
return false;
}
}
}
return true;
}
int main () {
scanf("%d %d", &n, &m);
memset(dis, 0x3f3f3f3f, sizeof(dis));
for (int i = 1; i <= m; i++) {
int opt;
scanf("%d", &opt);
switch (opt) {
case 1: {
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
add(a, b, -c);
break;
}
case 2: {
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
add(b, a, c);
break;
}
case 3: {
int a, b;
scanf("%d %d", &a, &b);
add(a, b, 0);
add(b, a, 0);
break;
}
}
}
for (int i = 1; i <= n; i++)
add(n + 1, i, 0);
bool flag = spfa(n + 1);
if (!flag) {
printf("No");
return 0;
}
printf("Yes");
return 0;
}
P3275 [SCOI2011] 糖果
题目描述
幼儿园里有 N 个小朋友,lxhgww 老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww 需要满足小朋友们的 K 个要求。幼儿园的糖果总是有限的,lxhgww 想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
输入格式
输入的第一行是两个整数 N,K。接下来 K 行,每行 3 个数字 X,A,B,表示小朋友们的要求。
- 如果 X=1, 表示第 A 个小朋友分到的糖果必须和第 B 个小朋友分到的糖果一样多;
- 如果 X=2, 表示第 A 个小朋友分到的糖果必须少于第 B 个小朋友分到的糖果;
- 如果 X=3, 表示第 A 个小朋友分到的糖果必须不少于第 B 个小朋友分到的糖果;
- 如果 X=4, 表示第 A 个小朋友分到的糖果必须多于第 B 个小朋友分到的糖果;
- 如果 X=5, 表示第 A 个小朋友分到的糖果必须不多于第 B 个小朋友分到的糖果;
输出格式
输出一行一个整数,表示 lxhgww 老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,则输出 −1。
输入输出样例
输入 #1复制
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
输出 #1复制
11
说明/提示
对于 30% 的数据,N≤100;
对于 100% 的数据,1≤N,K≤105,1≤X≤5,1≤A,B≤N。
upd 2022.7.6:新添加 21 组 Hack 数据。
实现代码:
cpp
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=100010;
struct relationship{
int x,a,b;
}r[maxn];
struct edge{
int to,next;
bool same;
}e[maxn<<1],e2[maxn];
int n,m,num,num2,cnt,head[maxn],head2[maxn],ltk[maxn],dfn[maxn],low[maxn];
int sta[maxn],top,size[maxn],que[maxn],du[maxn],h,tail,candy[maxn];
bool vis[maxn];
long long ans=0;
void add(int u,int v,bool k){
e[++num].to=v;e[num].same=k;e[num].next=head[u];head[u]=num;
}
void add2(int u,int v,bool k){
du[v]++;
e2[++num2].to=v;e2[num2].same=k;e2[num2].next=head2[u];head2[u]=num2;
}
void dfs(int x){
dfn[x]=low[x]=++cnt;sta[++top]=x;
for(int i=head[x];i;i=e[i].next){
if(!dfn[e[i].to]){
dfs(e[i].to);
low[x]=min(low[x],low[e[i].to]);
}
else if(!ltk[e[i].to]){
low[x]=min(low[x],dfn[e[i].to]);
}
}
if(dfn[x]==low[x]){
ltk[0]++;
while(top){
ltk[sta[top]]=ltk[0];
size[ltk[0]]++;
if(sta[top--]==x)break;
}
}
}
void Rebuild(){
for(int i=1;i<=n;i++){
for(int j=head[i];j;j=e[j].next){
if(ltk[i]!=ltk[e[j].to]){
add2(ltk[i],ltk[e[j].to],true);
}
}
}
for(int i=1;i<=m;i++){
if(r[i].x==2){
if(ltk[r[i].a]==ltk[r[i].b]){
printf("-1\n");
exit(0);
}
else{
add2(ltk[r[i].a],ltk[r[i].b],false);
}
}
else if(r[i].x==4){
if(ltk[r[i].a]==ltk[r[i].b]){
printf("-1\n");
exit(0);
}
else{
add2(ltk[r[i].b],ltk[r[i].a],false);
}
}
}
}
void Topsort(){
for(int i=1;i<=ltk[0];i++){
if(!du[i]){
que[++tail]=i;
vis[i]=true;
candy[i]=1;
}
}
while(h<tail){
int x=que[++h];
for(int i=head2[x];i;i=e2[i].next){
if(!--du[e2[i].to]){
que[++tail]=e2[i].to;
vis[e2[i].to]=true;
}
if(!e2[i].same){
candy[e2[i].to]=max(candy[e2[i].to],candy[x]+1);
}
else{
candy[e2[i].to]=max(candy[e2[i].to],candy[x]);
}
}
}
for(int i=1;i<=ltk[0];i++){
if(!vis[i]){
printf("-1\n");
exit(0);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&r[i].x,&r[i].a,&r[i].b);
if(r[i].x==1){
add(r[i].a,r[i].b,true);
add(r[i].b,r[i].a,true);
}
else if(r[i].x==3){
add(r[i].b,r[i].a,true);
}
else if(r[i].x==5){
add(r[i].a,r[i].b,true);
}
}
for(int i=1;i<=n;i++){
if(!dfn[i]){
dfs(i);
}
}
Rebuild();Topsort();
for(int i=1;i<=ltk[0];i++){
ans+=(candy[i]*size[i]);
}
printf("%lld\n",ans);
return 0;
}
P6464 [传智杯 #2 决赛] 传送门
题目描述
传智专修学院里有 n 栋教学楼,有 m 条双向通行道路连接这些教学楼,不存在重边和自环。每条道路都有一定的长度,而且所有教学楼之间都可以直接或者间接的通过道路到达。我们可以很容易的求出这些教学楼之间的最短路。
为了使交通更为顺畅,校方决定在两个教学楼里增设一对传送门。传送门可以将这对教学楼的距离直接缩短为 0。利用传送门,某些教学楼之间的最短路的距离就变短了。
由于预算有限,学校里只能安装一对传送门。但是校长希望尽可能方便学生,使任意两点之间的最短路长度的总和最小。当然啦,从 x 教学楼到 y 教学楼的长度和从 y 教学楼到 x 教学楼的长度只需要统计一次就可以了。
输入格式
输入第 1 行两个正整数 n,m(n≤100,m≤21n(n−1)),代表教学楼和道路数量。
接下来 m 行,每行三个正整数 xi,yi,wi(0<wi≤104),表示在教学楼 xi 和 yi 之间,有一条长度为 wi 的道路。
输出格式
输出一行,在最优方案下的任意点对的最短道路之和。
输入输出样例
输入 #1复制
4 5
1 2 3
1 3 6
2 3 4
2 4 7
3 4 2
输出 #1复制
14
说明/提示

样例如图。当在 1 和 4 号教学楼架设一对传送门时,1 → 2 的最短路是 3,1 → 3 的最短路是 0+2,1 → 4 的最短路是 0,2 → 3 的最短路是 4,2 → 4 的最短路是 3+0,3 → 4 的最短路是 2,最短路之和是 14,是最佳方案。
实现代码:
cpp
#include<bits/stdc++.h>
#define FOR(i,j,k) for(int i=(j);i<=(k);i++)
using namespace std;
int n,m;
int f[101][101];
int F[101][101];
inline void back()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
F[i][j]=f[i][j];
}
int main()
{
scanf("%d%d",&n,&m);
memset(f,-1,sizeof(f));
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
if(f[u][v]==-1||f[u][v]>w) f[u][v]=f[v][u]=w;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(f[i][k]!=-1&&f[k][j]!=-1)
if(f[i][j]==-1||f[i][j]>f[k][j]+f[i][k])
f[i][j]=f[i][k]+f[k][j];
int ans=2e9;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
back();
F[i][j]=F[j][i]=0;
for(int x=1;x<=n;x++)
for(int y=1;y<=n;y++)
if(F[x][y]==-1||F[x][y]>F[x][i]+F[i][y])
F[x][y]=F[x][i]+F[i][y];
for(int x=1;x<=n;x++)
for(int y=1;y<=n;y++)
if(F[x][y]==-1||F[x][y]>F[x][j]+F[j][y])
F[x][y]=F[x][j]+F[j][y];
int res=0;
for(int x=1;x<=n;x++)
for(int y=1;y<x;y++)
res+=F[x][y];
ans=min(res,ans);
}
printf("%d\n",ans);
return 0;
}
P3403 跳楼机
题目背景
DJL 为了避免成为一只咸鱼,来找 srwudi 学习压代码的技巧。
题目描述
Srwudi 的家是一幢 h 层的摩天大楼。由于前来学习的蒟蒻越来越多,srwudi 改造了一个跳楼机,使得访客可以更方便的上楼。
经过改造,srwudi 的跳楼机可以采用以下四种方式移动:
- 向上移动 x 层;
- 向上移动 y 层;
- 向上移动 z 层;
- 回到第一层。
一个月黑风高的大中午,DJL 来到了 srwudi 的家,现在他在 srwudi 家的第一层,碰巧跳楼机也在第一层。DJL 想知道,他可以乘坐跳楼机前往的楼层数。
输入格式
第一行一个整数 h,表示摩天大楼的层数。
第二行三个正整数,分别表示题目中的 x,y,z。
输出格式
一行一个整数,表示 DJL 可以到达的楼层数。
输入输出样例
输入 #1复制
15
4 7 9
输出 #1复制
9
输入 #2复制
33333333333
99005 99002 100000
输出 #2复制
33302114671
说明/提示
【样例 1 解释】
可以到达的楼层有:1,5,8,9,10,12,13,14,15。
【数据规模与约定】
对于 100% 的数据,1≤h≤263−1,1≤x,y,z≤105。
实现代码:
cpp
#include <iostream>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <climits>
#include <algorithm>
#include <vector>
typedef long long ll;
ll fr() {
ll x = 0, f = 1;
char c = getchar();
while (!isdigit(c)) {
if (c == '-') f = -1;
c = getchar();
}
while (isdigit(c)) {
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
return x * f;
}
const int maxn = 1e5+100;
ll d[maxn];
bool vis[maxn];
ll ans;
int gcd(int a,int b) {
return b?gcd(b,a%b):a;
}
void upd(int step,int M) {
int D=gcd(step,M);
int len=M/D;
for (int st = 0; st < D; st++) {
if (vis[st]) continue;
std::vector<int> v;
int u=st;
for (int i = 0; i < len; i++) {
v.push_back(u);
vis[u]=true;
u=(u+step)%M;
}
for (int r = 0; r < 2; r++) {
for (int i = 0; i < len; i++) {
int las=v[i];
int now=v[(i+1)%len];
if (d[las]!=LLONG_MAX) d[now]=std::min(d[now],d[las]+step);
}
}
}
}
int main() {
ll h = fr();
int x[3];
for (int i = 0; i < 3; i++) x[i]=fr();
std::sort(x,x+3);
int M=x[0];
for (int i = 0; i < M; i++) d[i]=LLONG_MAX;
d[0]=0;
upd(x[1],M);
memset(vis,0,sizeof(vis));
upd(x[2],M);
ll H=h-1;
ans=0;
for (int i = 0; i < M; i++) {
if (d[i]<=H&&d[i]!=LLONG_MAX) {
ans+=(H-d[i])/M+1;
}
}
printf("%lld\n",ans);
return 0;
}