一场结果擦不忍睹的模拟赛
在T1花了太多时间,结果T1崩到只有10分
后面俩题暴力分还是很可观的
推荐一篇优秀的题解博客:
2015-9-13 NOIP模拟赛解题报告(by hzwer)
【题目背景】
小奇要开采一些矿物,它驾驶着一台带有钻头(初始能力值w)的飞船,按既定路线依次飞过喵星系的n个星球。
【问题描述】
星球分为2类:资源型和维修型。
1.资源型:含矿物质量a[i],若选择开采,则得到a[i]p的金钱,之后钻头损耗k%,即p=p(1-0.01k)
2.维修型:维护费用b[i],若选择维修,则支付b[i]p的金钱,之后钻头修复c%,即p=p(1+0.01c)
(p为钻头当前能力值)
注:维修后钻头的能力值可以超过初始值
请你帮它决策最大化这个收入
【输入格式】
第一行4个整数n,k,c,w。
以下n行,每行2个整数type,x。
type为1则代表其为资源型星球,x为其矿物质含量a[i];
type为2则代表其为维修型星球,x为其维护费用b[i];
【输出格式】
输出一行一个实数(保留两位小数),表示要求的结果。
【样例输入】
5 50 50 10
1 10
1 20
2 10
2 20
1 30
【样例输出】
375.00
【数据范围】
对于30%的数据 n<=100
对于50%的数据 n<=1000,k=100
对于100%的数据 n<=100000,0<=k,c,w,a[i],b[i]<=100
保证答案不超过10^9
一只优秀的动归题
一只优秀的经典动归题
我们可以写出ans的表达式:
设 K=1-0.01k,C=1+0.01c
那么
ans=val1*w+val2 * w *K - val3 * w * C * K+val4 * w * C * K * K+…
(只是举个例子)
vali 是指答案中包含的星球的值,i不是星球的真实编号
又注意到当前的决策只对后面的开采有影响,对前面无影响,那么何不倒序,直接将当前计算的ans乘以K或C不就完成了对后面的影响,再加上或减去星球的val值即可
注意上面的算式,仿佛还可以提一个公因数w
这样,算法就出来了
至于状态转移,对于每一个星球,都有选与不选两个选择
而选择只对后面有影响,只用比较选与不选的大小就可以了
if(type==1) ans=max(ans,ans*k+val);
else ans=max(ans,ans*c-val);
/********************
User:Mandy.H.Y
Language:c++
Problem:exam1
********************/
//一只优秀的动归题
//一只优秀的经典动归题
#include
using namespace std;
const int maxn=1e5+5;
int n;
double k,c,w;
struct node
{
int type,val;
}a[maxn];
template<typename T>inline void read(T &x)
{
x=0;bool f=0;char C=getchar();
while(C<'0'||C>'9') {f|=(C=='-'); C=getchar();}
while(C>='0'&&C<='9') {x=(x<<3)+(x<<1)+(C^48); C=getchar();}
if(f) x=-x;
}
void readdata()
{
scanf("%d%lf%lf%lf",&n,&k,&c,&w);
for(int i=1;i<=n;++i)
{
read(a[i].type);read(a[i].val);
}
}
void work()
{
//把w看成1,最后再乘回来
double ans=0;
k=1-0.01*k;c=1+0.01*c;
//优秀的倒序做法,手推就知道了
for(int i=n;i>=1;--i)
{
int type=a[i].type;
int val=a[i].val;
if(type==1) ans=max(ans,ans*k+val);
else ans=max(ans,ans*c-val);
}
printf("%.2lf",ans*w);//注意别忘了乘回来
}
int main()
{
readdata();
work();
return 0;
}
【题目背景】
小奇总是在数学课上思考奇怪的问题。
【问题描述】
给定一个长度为n的数列,以及m次询问,每次给出三个数l,r和P,询问 (a[l’] + a[l’+1] + … + a[r’]) mod P的最小值。
其中l <= l’ <= r’ <= r。
即模意义下的区间子串和最小值。
【输入格式】
第一行包含两个正整数n和m,表示数列的长度和询问的个数。
第二行为n个整数,为a[1]…a[n]。
接下来m行,每行三个数l,r和P,代表一次询问。
【输出格式】
对于每次询问,输出一行一个整数表示要求的结果
【样例输入】
4 2
8 15 9 9
1 3 10
1 4 17
【样例输出】
2
1
【数据范围】
对于20%的数据 n<=100,m<=100,p<=200
对于40%的数据 n<=200,m<=1000,p<=500
对于70%的数据 n<=100000,m<=10000,p<=200
对于100%的数据 n<=500000,m<=10000,p<=500,1<=a[i]<=10^9
50分
纯暴力枚举
for(int j=l;j<=r;++j)//枚举区间起点
{
int sum=0;
for(int k=j;k<=r;++k)//枚举区间终点
{
ans=min(ans,(sum+a[k])%p);
sum=(sum+a[k])%p;
if(ans==0) break;
}
}
90分
多加了一句话if(ans==0) break;
for(int j=l;j<=r;++j)
{
int sum=0;
for(int k=j;k<=r;++k)
{
ans=min(ans,(sum+a[k])%p);
sum=(sum+a[k])%p;
if(ans==0) break;
}
if(ans==0) break;
}
100分
两种方法不过都有一个核心:找前驱
是L到当前位置(j)这段区间的和模p的余数(lef)的前驱
也就是之前求的余数(从L到 j 之前的位置的区间的和模p的余数)中与lef最接近的小于等于lef的余数(记为k)
则ans=min(ans,lef-k);
lef-k就是(k余数所对的位置到 j )这段区间的和的余数
如果已经大于lef的,那么这段区间(k余数所对的位置到j)的余数lef-k+p是大于lef的
方法一:枚举找前驱
for(int j=l;j<=r;++j)
{
int lef=(sum[j]-sum[l-1])%p;
//也可以读入的时候不处理,这里是sum[j]=(sum[j-1]+a[j])%p
//不过注意初始化sum[l-1]=0; sum也可以直接开成int
ans=min(ans,lef);//比较自身
if(ans==0) break;
for(int k=lef;k>=1;--k)
//枚举小于lef的余数,找到已经算过的第一个小于lef的余数
//如果p很大,这样做仿佛不划算,可以splay等
//相当于求lef余数的前驱,注意要到零,比较lef本身
//如果k已经大于lef的,那么这段区间(k余数所对的位置到j)的余数lef-k+p是大于lef的
if(vis[k])
{
ans=min(ans,lef-k);
break;
//找到前驱,lef-k就是最小的,直接退出
}
vis[lef]=1;
//记得标记余数
if(ans==0) break;
//如果ans等于零,那么不会再有最优解
//因为p值小,很容易出现零
}
方法二:平衡树找前驱
听说替罪羊树这些都行
方法一
/********************
User:Mandy.H.Y
Language:c++
Problem:exam2
********************/
#include
using namespace std;
const int maxn=5e5+5;
int n,m;
int a[maxn];
long long sum[maxn];
//维护前缀和,即sum[i]=sum[1]+sum[2]+…+sum[i]
//注意long long
bool vis[505];
template<typename T>inline void read(T &x)
{
x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9') {f|=(c=='-'); c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+(c^48); c=getchar();}
if(f) x=-x;
}
template<typename T>void putch(const T x)
{
if(x>9) putch(x/10);
putchar((x%10)|48);
}
template<typename T>inline void put(const T x)
{
if(x<0) putchar('-'),putch(-x);
else putch(x);
}
void docu()
{
freopen("ex2.txt","r",stdin);
}
void readdata()
{
read(n);read(m);
for(int i=1;i<=n;++i)
{
read(a[i]);
sum[i]=a[i]+sum[i-1];
}
}
void work()
{
for(int i=1;i<=m;++i)
{
int l,r,p,ans;
read(l);read(r);read(p);
ans=p+1;
if(r-l+1>p) {printf("0\n");continue;}
//抽屉原理
//如果区间长度大于p那么至少有两个余数相同或等于零
//如果sum[i]%p==sum[j]%p,根据同余定理,(sum[i]-sum[j])%p==sum[i]%p-sum[j]%p==0
//故必有区间[i,j]的和模p等于0
for(int j=1;j<=p;++j)vis[j]=0;//清空余数标记
vis[0]=1;
for(int j=l;j<=r;++j)
{
int lef=(sum[j]-sum[l-1])%p;
//也可以读入的时候不处理,这里是sum[j]=(sum[j-1]+a[j])%p
//不过注意初始化sum[l-1]=0; sum也可以直接开成int
ans=min(ans,lef);//比较自身
if(ans==0) break;
for(int k=lef;k>=1;--k)
//枚举小于lef的余数,找到已经算过的第一个小于lef的余数
//如果p很大,这样做仿佛不划算,可以splay等
//相当于求lef余数的前驱,注意要到零,比较lef本身
//如果k已经大于lef的,那么这段区间(k余数所对的位置到j)的余数lef-k+p是大于lef的
if(vis[k])
{
ans=min(ans,lef-k);
break;
//找到前驱,lef-k就是最小的,直接退出
}
vis[lef]=1;
//记得标记余数
if(ans==0) break;
//如果ans等于零,那么不会再有最优解
//因为p值小,很容易出现零
}
// ans=min(ans,a[r]%p);
put(ans);
putchar('\n');
}
}
int main()
{
// docu();
readdata();
work();
return 0;
}
方法二:Splay
/********************
User:Mandy.H.Y
Languange:c++
Problem:mzoj1384
Algirithm:Splay
********************/
#include
using namespace std;
const int maxn=5e5+5;
const int maxp=500+5;
int n,m,tot,root;
int a[maxn],cnt[maxn],child[maxn][2],father[maxn],val[maxn],size[maxn];
template<typename T>inline void read(T &x)
{
x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9') f|=(c=='-'),c=getchar();
while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
if(f) x=-x;
}
template<typename T>void putch(const T x)
{
if(x>9) putch(x/10);
putchar((x%10)|48);
}
template<typename T>inline void put(const T x)
{
if(x<0) putchar('-'),putch(-x);
else putch(x);
}
void docu()
{
freopen("ex2.txt","r",stdin);
}
void readdata()
{
read(n);read(m);
for(int i=1;i<=n;++i) read(a[i]);
}
bool get_child(int now)
{
return val[now]>val[father[now]];
}
void pushup(int now)
{
size[now]=size[child[now][1]]+size[child[now][0]]+cnt[now];
}
void rotate(int now)
{
int fa=father[now],an=father[fa],pos=get_child(now),son=child[now][pos^1];
father[son]=fa;child[fa][pos]=son;
father[now]=an;child[an][get_child(fa)]=now;
father[fa]=now;child[now][pos^1]=fa;
pushup(fa);pushup(now);
}
void splay(int now,int aim=0)
{
while(father[now]!=aim)
{
int fa=father[now],an=father[fa];
if(an!=aim)
{
if(get_child(now)==get_child(fa)) rotate(fa);
else rotate(now);
}
rotate(now);
}
if(!aim) root=now;
//更新根节点
}
void find(int x)
{
int now=root;
while(val[now]!=x&&child[now][x>val[now]]) now=child[now][x>val[now]];
splay(now);
//提到根上
}
int get_pre(int x)
{
find(x);
if(val[root]<=x) return root;
int now=child[root][0];//因为开始时插入了0,故不存在这里now==0
while(child[now][1]) now=child[now][1];
return now;
}
void insert(int x)
{
int now=root,fa=0;
while(val[now]!=x&&now) fa=now,now=child[now][x>val[now]];
if(now) ++cnt[now];
else
{
now=++tot;
val[now]=x;
father[now]=fa;
child[fa][x>val[fa]]=now;
size[now]=cnt[now]=1;
child[now][0]=child[now][1]=0;
}
splay(now);
}
void work()
{
while(m--)
{
int l,r,p,ans=1e5,lef;
lef=0;
root=0;tot=0;
read(l);read(r);read(p);
if(r-l+1>p) {put(0),putchar('\n');continue;}
insert(0);
//防止找不到前驱时返回了后继
for(int i=l;i<=r;++i)
{
lef=(lef+a[i])%p;
int pre=val[get_pre(lef)];
ans=min(ans,lef-pre);
insert(lef);
if(ans==0) break;
}
put(ans);
putchar('\n');
}
}
int main()
{
// docu();
readdata();
work();
return 0;
}
【题目背景】
开学了,小奇在回地球的路上,遇到了一个棘手的问题。
【问题描述】
简单来说,它要从标号为1的星球到标号为n的星球,某一些星球之间有航线。由于超时空隧道的存在,从一个星球到另一个星球时间可能会倒流,而且,从星球a到b耗费的时间和星球b到a耗费的时间不一定相同。
宇宙法规定:“禁止在出发时间前到达目的地。”
每艘飞船上都有速度调节装置,可以调节飞行的时间。其功能可以使得整次航程中所有两星球间的飞行时间增加或减少相同的整数值。你的任务是帮助它调整速度调节器,找出一条最短时间到达目的地的路径。
【输入格式】
输入文件包含多组数据,第1个数为T,表示数据组数。
对于每组数据,输入第1行为两个正整数n,m,为星球的个数和星球间的路线数。接下来m行,每行三个整数i,j和t,表示由星球i到星球j飞行的时间为t。由i到j最多只会有一条飞行线路。
【输出格式】
输出文件共T行,每组数据输出一行。
如果可以通过调节速度调节器完成任务,则输出一个非负整数,表示由星球1到星球n的最短时间。(注意最短时间要大于或者等于0)。
如果不能由星球1到达星球n,则输出-1。
【样例输入】
1
4 5
1 2 1
1 3 1
2 3 -3
3 1 1
3 4 1
【样例输出】
2
【样例解释】
把速度控制器的值设为1,相当于每个时间值加1,得到的最短路径为1→2→3→4,所需时间为2+(-2)+2=2。
【数据范围】
1,2号测试点,保证所有星球出度不超过1
3,4号测试点,n<=10
5,6号测试点,-100<=t<=100
对于100%的数据T<=10,n<=100,m<=n*(n-1),-100000<=t<=100000
数据随机和构造结合生成
排除了负环的影响就可以正常做最短路啦
我觉得吧,题面有问题
标程相当于先求最短路,再求出不为负的时间
(也就是把最短路加时间加到正)
而如果是真的要求最短时间的话,因为有调速器的存在,最短的时间不一定在最短路上
没什么好说的
方法一:spfa
见文章前面的博客链接里
直接用spfa判负环
方法二:dfs(判负环)+spfa
比方法一略快
/********************
User:Mandy.H.Y
Language:c++
Problem:exam3
********************/
#include
using namespace std;
const int maxn=105;
const int maxm=10000+5;
int n,m,t,size=0,size1=0,mid,ans;
int first[maxn],dis[maxn],first1[maxn],vis[maxn],q[maxn];
bool vis1[maxn];
struct Edge
{
int v,w,nt;
}edge[maxm],edge1[maxm];
template<typename T>inline void read(T &x)
{
x=0;bool f=0;char c=getchar();
while(c<'0'||c>'9') {f|=(c=='-'); c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+(c^48); c=getchar();}
if(f) x=-x;
}
template<typename T>void putch(const T x)
{
if(x>9) putch(x/10);
putchar((x%10)|48);
}
template<typename T>inline void put(const T x)
{
if(x<0) putchar('-'),putch(-x);
else putch(x);
}
void docu()
{
freopen("1.txt","r",stdin);
}
bool circle(int u)
{
vis1[u]=1;
for(int i=first[u];i;i=edge[i].nt)
{
int v=edge[i].v;
int w=edge[i].w;
if(dis[u]+w+mid<dis[v]&&vis[v]==2)
{
if(vis1[v]) return 1;
dis[v]=dis[u]+w+mid;
if(circle(v)) return 1;
}
}
vis1[u]=0;
//撤标记
return 0;
}
void spfa()
{
for(int i=1;i<=n;++i)dis[i]=0x3f3f3f3f;
int l=0,r=0;
dis[1]=0;q[r++]=1;
//可以循环使用队列
//因为入队点数不超过n
while(l!=r)
{
int u=q[l++];
if(l>n) l=0;
for(int i=first[u];i;i=edge[i].nt)
{
int v=edge[i].v;
int w=edge[i].w;
if(dis[u]+w+mid<dis[v]&&vis[v]==2)
{
dis[v]=dis[u]+w+mid;
if(!vis1[v])
{
int u1=q[l];
if(dis[v]>dis[u1])
{
q[r++]=v;
if(r>n) r=0;
}
else
{
--l;
if(l<0) l=n;
q[l]=v;
}
vis1[v]=1;
}
}
}
vis1[u]=0;
//标记放后面防止自环是负环
}
}
bool judge()
{
/*
直接这样写是超时的,
因为所有的1到 n道路上的点都遍历了一遍
递归很深
而且因为要撤标记, 会重复跑很多遍
其实我觉得这里跑一遍广搜版的spfa会好一点
for(int i=1;i<=n;++i)dis[i]=0x3f3f3f3f,vis1[i]=0;
dis[1]=0;
if(circle(1)) return 0;
*/
memset(vis1,0,sizeof(vis1));
//可以放外面,因为递归会撤标记除非直接return
//dfs中 return 1后jud会直接return 0,不影响
for(int i=1;i<=n;++i)
//相当于枚举负环的出发点
//所有点的初始距离都是无限大
//当然第一个枚举到的一定是一个负边
//因为要撤标记,遍历完这个点后,标记会归零
//所以从这个点出发,又松弛回了这个点,一定有负环(从这个点出发回到这个点的路径和为负)
if(vis[i]==2)
{
for(int j=1;j<=n;++j)dis[j]=1e9;
if(circle(i)) return 0;
}
//不需要再初始化mark因为递归中已经撤了标记
spfa();
if(dis[n]<0||dis[n]==0x3f3f3f3f) return 0;
else
{
ans=dis[n];
return 1;
}
}
void dfs2(int u)
{
vis[u]=2;
for(int i=first1[u];i;i=edge1[i].nt)
{
int v=edge1[i].v;
if(vis[v]==1) dfs2(v);
}
}
void dfs1(int u)
{
vis[u]=1;
for(int i=first[u];i;i=edge[i].nt)
{
int v=edge[i].v;
if(!vis[v]) dfs1(v);
}
}
void eadd1(int u,int v,int w)
{
edge1[++size1].v=v;
edge1[size1].w=w;
edge1[size1].nt=first1[u];
first1[u]=size1;
}
void eadd(int u,int v,int w)
{
edge[++size].v=v;
edge[size].w=w;
edge[size].nt=first[u];
first[u]=size;
}
void work()
{
read(t);
// int times1=clock();
while(t--)
{
// int times=clock();
read(n);read(m);size=size1=0;
ans=-1;
for(int i=1;i<=n;++i) {first[i]=vis[i]=0,first1[i]=vis1[i]=0;}
/*
memset(vis,0,sizeof(vis));
memset(first,0,sizeof(first));
memset(first1,0,sizeof(first1));
*/
for(int i=1;i<=m;++i)
{
int u,v,w;
read(u);read(v);read(w);
eadd(u,v,w);
eadd1(v,u,w);
//反向建边
}
// printf("time1(init):%.3lf\n",double(clock()-times)/CLOCKS_PER_SEC);
// times=clock();
dfs1(1);
if(!vis[n]) {printf("-1\n");continue;}
dfs2(n);
//反向遍历一遍
//从1能到达、自身能到达n的点标记为2
// printf("time2(dfs):%.3lf\n",double(clock()-times)/CLOCKS_PER_SEC);
// times=clock();
int l=-1e5,r=1e5;
// int cnt=0;
while(l<=r)
{
// ++cnt;
mid=(l+r)>>1;
if(judge()) r=mid-1;
else l=mid+1;
}
// printf("time3(judge):%.3lf\n",double(clock()-times)/CLOCKS_PER_SEC);
// printf("judge次数:%d\n",cnt);
printf("%d\n",ans);
}
// printf("\n总时间 :%.3lf\n",double(clock()-times1)/CLOCKS_PER_SEC);
}
int main()
{
// docu();
work();
return 0;
}
方法三(floyd传递闭包判联通)
#include
#define inf 1e9
using namespace std;
const int maxn=102;
const int maxm=10002;
int t,n,m,l,r,ans;
struct EDGE
{
int v,w,nxt;
}edge[maxm];
int size=0;
int head[maxn],vis[maxn][maxn],dis[maxn],used[maxn],tot[maxn];
void add(int u,int v,int w)
{
edge[size].v=v;
edge[size].w=w;
edge[size].nxt=head[u];
head[u]=size++;
}
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch<='9'&&ch>='0'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
inline void floyd()
{
for(int i=1;i<=n;i++)vis[i][i]=1;
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
vis[i][j]=vis[i][j]|(vis[i][k]&vis[k][j]);//floyd传递闭包
}
inline void init()
{
freopen("MZOJ1385.in","r",stdin);
}
inline void initdata()
{
size=0;
for(int i=1;i<=101;i++)
head[i]=-1,used[i]=0,tot[i]=0;
memset(edge,sizeof(edge),0);
memset(vis,sizeof(vis),0);
}
inline bool spfa(int mid)
{
deque<int>q;
for(int i=1;i<=101;i++)
dis[i]=inf,used[i]=0,tot[i]=0;
dis[1]=0;
tot[1]=1;
q.push_back(1);
used[1]=1;
while(!q.empty())
{
int u=q.front();
q.pop_front();
used[u]=0;
for(int i=head[u];~i;i=edge[i].nxt)
{
int v=edge[i].v,w=edge[i].w+mid;
if(dis[v]>dis[u]+w && vis[v][n])
{
dis[v]=dis[u]+w;
tot[v]=tot[u]+1;
if(tot[v]>n){return false;}
if(!used[v])
{
if(!q.empty())
{
if(dis[v]<dis[q.front()])q.push_front(v);
else q.push_back(v);
}
else q.push_back(v);
used[v]=1;
}
}
}
}
return true;
}
inline bool judge(int mid)
{
if(!spfa(mid))return false;
if(dis[n]<0)return false;
if(dis[n]==inf)return false;
return true;
}
inline void readdata()
{
t=read();
while(t--)
{
n=read(),m=read();
initdata();
for(int i=1;i<=m;i++)
{
int u=read(),v=read(),w=read();
add(u,v,w);
vis[u][v]=1;
}
floyd();
if(!vis[1][n])
{
printf("-1\n");
continue;
}
l=-500000,r=500000;
ans=-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(judge(mid))
{
r=mid-1,ans=dis[n];
}
else l=mid+1;
}
printf("%d\n",ans);
}
}
int main()
{
//init();
readdata();
return 0;
}
/********************
ID:胡芸 Mandy
language:c++
problem:luogu3385
********************/
#include
#define M 4005
#define N 4005
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define up(i,l,r) for(int i=l;i<=r;++i)
#define down(i,l,r) for(int i=r;i>=l;--i)
//听说SPFA有优化
//手写队列
//https://www.zhihu.com/question/268382638
//https://www.zhihu.com/question/292283275
//https://blog.csdn.net/JacaJava/article/details/82528037
//卡spfa
//https://blog.csdn.net/yfzcsc/article/details/77623365
//生成数据
//https://www.luogu.org/discuss/show/11410
using namespace std;
int n,m,s,size,f[N],d[N],t;
bool vis[N];
int used[N];
struct Edge
{
int v,w,nt;
}e[M<<1];
template<typename T>inline void read(T &x)
{
x=0;bool f=0; char c=getchar();
while(c<'0'||c>'9') {f|=c=='-';c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+(c^48); c=getchar();}
if(f)x=-x;
}
void docu()
{
freopen("1.txt","r",stdin);
// freopen("shortestpath.out","r",stdout);
}
void eadd(int u,int v,int w)
{
e[++size].v=v;
e[size].w=w;
e[size].nt=f[u];
f[u]=size;
}
bool SPFA()
{
queue<int>q;
q.push(1);
used[1]=1;
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0;
for(int i=f[u];i;i=e[i].nt)
{
int v=e[i].v,w=e[i].w;
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
if(!vis[v])
{
q.push(v);
vis[v]=1;
used[v]=used[u]+1;//路径上最长的路径就是n,再长就一定有负环
if(used[u]>n) return 0;
}
}
}
}
return 1;
}
void work()
{
read(t);
up(j,1,t)
{
size=0;
read(n);
read(m);
for(int i=1;i<=n;++i){f[i]=0;d[i]=0x3f3f3f3f;used[i]=0;vis[i]=0;}
d[1]=0;
up(i,1,m)
{
int u,v,w;
read(u);read(v);read(w);
if(w<0) eadd(u,v,w);
else eadd(u,v,w),eadd(v,u,w);
}
if(!SPFA()) printf("YE5\n");//不是YES
else printf("N0\n");//不是NO
}
}
int main()
{
// docu();
work();
return 0;
}