目录
1 904 虫洞
农夫约翰在巡视他的众多农场时,发现了很多令人惊叹的虫洞。
虫洞非常奇特,它可以看作是一条 单向 路径,通过它可以使你回到过去的某个时刻(相对于你进入虫洞之前)。
农夫约翰的每个农场中包含 N 片田地,M 条路径(双向)以及 W 个虫洞。
现在农夫约翰希望能够从农场中的某片田地出发,经过一些路径和虫洞回到过去,并在他的出发时刻之前赶到他的出发地。
他希望能够看到出发之前的自己。
请你判断一下约翰能否做到这一点。
下面我们将给你提供约翰拥有的农场数量 F,以及每个农场的完整信息。
已知走过任何一条路径所花费的时间都不超过 10000 秒,任何虫洞将他带回的时间都不会超过 10000秒。
输入格式
第一行包含整数 F,表示约翰共有 F 个农场。
对于每个农场,第一行包含三个整数 N,M,W
接下来 M 行,每行包含三个整数 S,E,T,表示田地 S 和 E 之间存在一条路径,经过这条路径所花的时间为 T。
再接下来 W 行,每行包含三个整数 S,E,T,表示存在一条从田地 S 走到田地 E 的虫洞,走过这条虫洞,可以回到 T 秒之前。
输出格式
输出共 F 行,每行输出一个结果。
如果约翰能够在出发时刻之前回到出发地,则输出 YES
,否则输出 NO
。
数据范围
1≤F≤5
1≤N≤500
1≤M≤2500
1≤W≤200
1≤T≤10000
1≤S,E≤N
输入样例:
2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8
输出样例:
NO
YES
#include<bits/stdc++.h>
using namespace std;
const int N=5210;
int e[N],h[N],ne[N],w[N],idx;
int d[N],cnt[N];
bool st[N];
int n,m,k;
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
bool spfa()
{
queue<int>q;
for(int i=1;i<=n;i++){
q.push(i);
st[i]=true;
}
memset(d,0,sizeof d);
memset(cnt,0,sizeof cnt);
while(q.size())
{
int t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(d[j]>d[t]+w[i])
{
d[j]=d[t]+w[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n)return true;
if(!st[j])
{
q.push(j);
st[j]=true;
}
}
}
}
return false;
}
int main()
{
int t;
cin>>t;
while(t--)
{
cin>>n>>m>>k;
memset(h,-1,sizeof h);
idx=0;
while(m--){
int a,b,c;
cin>>a>>b>>c;
add(a,b,c),add(b,a,c);
}
while(k--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,-c);
}
if(spfa())puts("YES");
else puts("NO");
}
return 0;
}
2 1165 单词环
我们有 n 个字符串,每个字符串都是由 a∼z 的小写英文字母组成的。
如果字符串 A 的结尾两个字符刚好与字符串 B 的开头两个字符相匹配,那么我们称 A 与 B 能够相连(注意:A 能与 B 相连不代表 B 能与 A 相连)。
我们希望从给定的字符串中找出一些,使得它们首尾相连形成一个环串(一个串首尾相连也算),我们想要使这个环串的平均长度最大。
如下例:
ababc
bckjaca
caahoynaab
第一个串能与第二个串相连,第二个串能与第三个串相连,第三个串能与第一个串相连,我们按照此顺序相连,便形成了一个环串,长度为 5+7+10=22(重复部分算两次),总共使用了 3 个串,所以平均长度是 22/3≈7.33
输入格式
本题有多组数据。
每组数据的第一行,一个整数 n,表示字符串数量;
接下来 n 行,每行一个长度小于等于 1000 的字符串。
读入以 n=0 结束。
输出格式
若不存在环串,输出”No solution”,否则输出最长的环串的平均长度。
只要答案与标准答案的差不超过 0.01,就视为答案正确。
数据范围
1≤n≤105
输入样例:
3
intercommunicational
alkylbenzenesulfonate
tetraiodophenolphthalein
0
输出样例:
21.66
#include<bits/stdc++.h>
using namespace std;
const int N=700,M=100010;
int e[M],ne[M],h[N],w[M];
int idx,cnt[N];
double d[N];
bool st[N];
int n;
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
bool check(double mid)
{
memset(st,0,sizeof st);
memset(cnt,0,sizeof cnt);
queue<int>q;
for(int i=0;i<676;i++){
q.push(i);
st[i]=true;
}
int count=0;
while(q.size())
{
int t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(d[j]<d[t]+w[i]-mid){
cnt[j]=cnt[t]+1;
d[j]=d[t]+w[i]-mid;
if(++count>10000)return true;
if(cnt[j]>=N)return true;
if(!st[j]){
q.push(j);
st[j]=true;
}
}
}
}
return false;
}
int main()
{
char s[1010];
while(cin>>n,n)
{
memset(h,-1,sizeof h);
idx=0;
for(int i=0;i<n;i++){
cin>>s;
int len=strlen(s);
if(len>=2)
{
int l=(s[0]-'a')*26+s[1]-'a';
int r=(s[len-2]-'a')*26+s[len-1]-'a';
add(l,r,len);
}
}
if(!check(0))puts("No solution");
else{
double l=0,r=1000;
while(r-l>1e-4){
double mid=(l+r)/2;
if(check(mid))l=mid;
else r=mid;
}
printf("%lf\n",r);
}
}
return 0;
}
3 1169 糖果
幼儿园里有 N 个小朋友,老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。
但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候, 老师需要满足小朋友们的 K 个要求。
幼儿园的糖果总是有限的,老师想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
输入格式
输入的第一行是两个整数 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 个小朋友分到的糖果。
小朋友编号从 1 到 N。
输出格式
输出一行,表示老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 −1。
数据范围
1≤N<105
1≤K≤105
1≤X≤5
1≤A,B≤N
输入数据完全随机。
输入样例:
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
输出样例:
11
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100010,M=300010;
int n,m;
int h[N],e[M],ne[M],w[M],idx;
LL d[N];
int cnt[N];
bool st[N];
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
bool spfa()
{
stack<int>q;
memset(d,-0x3f,sizeof d);
q.push(0);
d[0]=0;
st[0]=true;
while(q.size())
{
int t=q.top();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(d[j]<d[t]+w[i])
{
d[j]=d[t]+w[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n+1)return false;
if(!st[j])
{
q.push(j);
st[j]=true;
}
}
}
}
return true;
}
int main()
{
cin>>n>>m;
memset(h,-1,sizeof h);
while(m--)
{
int x,a,b;
cin>>x>>a>>b;
if(x==1)add(b,a,0),add(a,b,0);
else if(x==2)add(a,b,1);
else if(x==3)add(b,a,0);
else if(x==4)add(b,a,1);
else add(a,b,0);
}
for(int i=1;i<=n;i++)add(0,i,1);
if(!spfa())puts("-1");
else{
LL ans=0;
for(int i=1;i<=n;i++)ans+=d[i];
cout<<ans<<endl;
}
return 0;
}
4 1170 排队布局
当排队等候喂食时,奶牛喜欢和它们的朋友站得靠近些。
农夫约翰有 N 头奶牛,编号从 1 到 N,沿一条直线站着等候喂食。
奶牛排在队伍中的顺序和它们的编号是相同的。
因为奶牛相当苗条,所以可能有两头或者更多奶牛站在同一位置上。
如果我们想象奶牛是站在一条数轴上的话,允许有两头或更多奶牛拥有相同的横坐标。
一些奶牛相互间存有好感,它们希望两者之间的距离不超过一个给定的数 L。
另一方面,一些奶牛相互间非常反感,它们希望两者间的距离不小于一个给定的数 D。
给出 ML 条关于两头奶牛间有好感的描述,再给出 MD 条关于两头奶牛间存有反感的描述。
你的工作是:如果不存在满足要求的方案,输出-1;如果 1 号奶牛和 N 号奶牛间的距离可以任意大,输出-2;否则,计算出在满足所有要求的情况下,1 号奶牛和 N 号奶牛间可能的最大距离。
输入格式
第一行包含三个整数 N,ML,MD。
接下来 ML 行,每行包含三个正整数 A,B,L,表示奶牛 A 和奶牛 B 至多相隔 L 的距离。
再接下来 MD行,每行包含三个正整数 A,B,D,表示奶牛 A 和奶牛 B 至少相隔 D 的距离。
输出格式
输出一个整数,如果不存在满足要求的方案,输出-1;如果 1 号奶牛和 N 号奶牛间的距离可以任意大,输出-2;否则,输出在满足所有要求的情况下,1 号奶牛和 N 号奶牛间可能的最大距离。
数据范围
2≤N≤1000
1≤ML,MD≤104
1≤L,D≤106
输入样例:
4 2 1
1 3 10
2 4 20
2 3 3
输出样例:
27
#include<bits/stdc++.h>
using namespace std;
const int N=1010,M=21010,INF=0x3f3f3f3f;
int n,m1,m2;
int h[N],e[M],ne[M],w[M],idx;
int d[N],cnt[N];
bool st[N];
void add(int a,int b,int c)
{
e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;
}
bool spfa(int u)
{
queue<int>q;
memset(st,0,sizeof st);
memset(cnt,0,sizeof st);
memset(d,0x3f,sizeof d);
for(int i=1;i<=u;i++){
d[i]=0;
q.push(i);
st[i]=true;
}
while(q.size())
{
int t=q.front();
q.pop();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
if(d[j]>d[t]+w[i]){
d[j]=d[t]+w[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n)return false;
if(!st[j]){
q.push(j);
st[j]=true;
}
}
}
}
return true;
}
int main()
{
cin>>n>>m1>>m2;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++)add(i+1,i,0);
while(m1--)
{
int a,b,c;
cin>>a>>b>>c;
if(b<a)swap(b,a);
add(a,b,c);
}
while(m2--)
{
int a,b,c;
cin>>a>>b>>c;
if(b<a)swap(a,b);
add(b,a,-c);
}
if(!spfa(n))puts("-1");
else{
spfa(1);
if(d[n]==INF)puts("-2");
else cout<<d[n]<<endl;
}
return 0;
}
5 1250 格子游戏
Alice和Bob玩了一个古老的游戏:首先画一个 n×n的点阵(下图 n=3 )。
接着,他们两个轮流在相邻的点之间画上红边和蓝边:
直到围成一个封闭的圈(面积不必为 1)为止,“封圈”的那个人就是赢家。因为棋盘实在是太大了,他们的游戏实在是太长了!
他们甚至在游戏中都不知道谁赢得了游戏。
于是请你写一个程序,帮助他们计算他们是否结束了游戏?
输入格式
输入数据第一行为两个整数 n 和 m。n表示点阵的大小,m 表示一共画了 m 条线。
以后 m 行,每行首先有两个数字 (x,y),代表了画线的起点坐标,接着用空格隔开一个字符,假如字符是 D,则是向下连一条边,如果是 R 就是向右连一条边。
输入数据不会有重复的边且保证正确。
输出格式
输出一行:在第几步的时候结束。
假如 m 步之后也没有结束,则输出一行“draw”。
数据范围
1≤n≤200,
1≤m≤24000
输入样例:
3 5
1 1 D
1 1 R
1 2 D
2 1 R
2 2 D
输出样例:
4
#include<bits/stdc++.h>
using namespace std;
int p[40010];
int n,m;
int get(int x,int y)
{
return x*n+y;
}
int find(int x)
{
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
int main()
{
int ans=0;
cin>>n>>m;
for(int i=0;i<n*n;i++)p[i]=i;
for(int i=1;i<=m;i++)
{
int x,y,b,a;
char s;
cin>>x>>y>>s;
x--,y--;
a=get(x,y);
if(s=='D')b=get(x+1,y);
else b=get(x,y+1);
int pa=find(a),pb=find(b);
if(pa==pb){
ans=i;
break;
}
p[pa]=pb;
}
if(!ans)puts("draw");
else cout<<ans<<endl;
return 0;
}
6 1252 搭配购买
Joe觉得云朵很美,决定去山上的商店买一些云朵。
商店里有 n 朵云,云朵被编号为 1,2,…,n,并且每朵云都有一个价值。
但是商店老板跟他说,一些云朵要搭配来买才好,所以买一朵云则与这朵云有搭配的云都要买。
但是Joe的钱有限,所以他希望买的价值越多越好。
输入格式
第 11 行包含三个整数 n,m,w,表示有 n 朵云,m 个搭配,Joe有 w 的钱。
第 2∼n+1行,每行两个整数 ci,di 表示 i 朵云的价钱和价值。
第 n+2∼n+1+m 行,每行两个整数 ui,vi,表示买 ui 就必须买 vi,同理,如果买 vi 就必须买 ui。
输出格式
一行,表示可以获得的最大价值。
数据范围
1≤n≤10000
0≤m≤5000
1≤w≤10000
1≤ci≤5000
1≤di≤100
1≤ui,vi≤n
输入样例:
5 3 10
3 10
3 10
3 10
5 100
10 1
1 3
3 2
4 2
输出样例:
1
#include<bits/stdc++.h>
using namespace std;
int w[10010],v[10010],f[10010],p[10010];
int n,m,V;
int find(int x)
{
if(p[x]!=x)p[x]=find(p[x]);
return p[x];
}
int main()
{
cin>>n>>m>>V;
for(int i=1;i<=n;i++)p[i]=i;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
while(m--)
{
int a,b;
cin>>a>>b;
a=find(a),b=find(b);
if(a!=b){
v[b]+=v[a];
w[b]+=w[a];
p[a]=b;
}
}
for(int i=1;i<=n;i++){
if(p[i]==i){
for(int j=V;j>=v[i];j--){
f[j]=max(f[j],f[j-v[i]]+w[i]);
}
}
}
cout<<f[V];
return 0;
}
7 1273 天才的记忆
从前有个人名叫 WNB,他有着天才般的记忆力,他珍藏了许多许多的宝藏。
在他离世之后留给后人一个难题(专门考验记忆力的啊!),如果谁能轻松回答出这个问题,便可以继承他的宝藏。
题目是这样的:给你一大串数字(编号为 1 到 N,大小可不一定哦!),在你看过一遍之后,它便消失在你面前,随后问题就出现了,给你 M 个询问,每次询问就给你两个数字 A,B,要求你瞬间就说出属于 A 到 B 这段区间内的最大数。
一天,一位美丽的姐姐从天上飞过,看到这个问题,感到很有意思(主要是据说那个宝藏里面藏着一种美容水,喝了可以让这美丽的姐姐更加迷人),于是她就竭尽全力想解决这个问题。
但是,她每次都以失败告终,因为这数字的个数是在太多了!
于是她请天才的你帮他解决。如果你帮她解决了这个问题,可是会得到很多甜头的哦!
输入格式
第一行一个整数 N 表示数字的个数。
接下来一行为 N 个数,表示数字序列。
第三行读入一个 M,表示你看完那串数后需要被提问的次数。
接下来 M 行,每行都有两个整数 A,B。
输出格式
输出共 M 行,每行输出一个数,表示对一个问题的回答。
数据范围
1≤N≤2×105
1≤M≤104
1≤A≤B≤N
输入样例:
6
34 1 8 123 3 2
4
1 2
1 5
3 4
2 3
输出样例:
34
123
123
8
#include<bits/stdc++.h>
using namespace std;
const int N=200010,M=18;
int n,m;
int w[N],f[N][M];
void init()
{
for(int j=0;j<M;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
if(!j)f[i][j]=w[i];
else f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
}
}
}
int query(int l,int r)
{
int len=r-l+1;
int k=log(len)/log(2);
return max(f[l][k],f[r-(1<<k)+1][k]);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>w[i];
cin>>m;
init();
while(m--)
{
int l,r;
cin>>l>>r;
cout<<query(l,r)<<endl;
}
return 0;
}