解题思路
- 首先存在一种贪心策略,一定由弱到强选择击败的敌人
- 保证自己面对一个敌人时,是可能的最大力量
- 考虑没有药水
- 则用
将能到的敌人由弱到强依次挑战,并不断增加力量,直到没法继续扩展
- 若当前力量大于最强的敌人,则可以击败所有人,输出Yes
- 现在有了药水,由于药水在节点上,且为树形结构
- 要拿到某一个药水可能要遇上很强的敌人,导致无法取到该药水提升实力
- 但若先去其他药水,然后再打败一些敌人提升实力,就可能打败那个
- 所以药水的取用顺序会存在一个最优方案
- 考虑到药水最多只有
个
- 所以不用管药水的最优是如何取用的,直接状压出所有顺序
- 先处理出不考虑药水的答案,则将其
中能直接取到的药水,放入状态
- 例
- 对于当前状态
,则可以到达它的状态都被处理了,所以
决策完毕
- 每次从一个已经决策完的状态,
下一个可能的状态,每次多取一个能直接取到的药水 (更多的到后面更新)
- 对于不可能存在的状态,
,不会被更新,直接
- 再过程中,若当前力量大于最强的敌人,则可以击败所有人,,输出Yes
- 若所有可以的顺序决策完(一定包含最优顺序)都不大于最强的敌人,则寄
import java.io.*;
import java.math.BigInteger;
import java.util.*;
//implements Runnable
public class Main {
static long md=(long)998244353;
static long Linf=Long.MAX_VALUE/2;
static int inf=Integer.MAX_VALUE/2;
static int N=1000010;
static int n=0;
static int m=0;
static
class Node{
long x;
long y;
public Node() {
}
public Node(long u,long v) {
x=u;
y=v;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Node now = (Node) o;
return x==now.x&&y==now.y;
}
@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
static int qm2(int a,int b) {
int res=1;
while(b>0) {
if((b&1)==1)res=res*a;
a=a*a;
b>>=1;
}
return res;
}
static
class Edge{
int fr,to,nxt;
public Edge(int u,int v){
fr=u;
to=v;
}
}
static Edge[] e;
static int[] head;
static int cnt=0;
static void addEdge(int fr,int to){
cnt++;
e[cnt]=new Edge(fr,to);
e[cnt].nxt=head[fr];
head[fr]=cnt;
}
static long[] p;
static long[] t;
static long[] s;
static long[] g;
static int[] med;
static long[] f;
static long mx;
static boolean get_fu_nxt(int u){//u已经处理完了,看能往后扩展多取一个药水的情况
boolean[] vis=new boolean[n+1];
PriorityQueue<Node> q=new PriorityQueue<>((o1,o2)->{
if(o1.y-o2.y>0)return 1;
else if(o1.y-o2.y<0)return -1;
else return 0;
});
q.add(new Node(1,0));//id,s
while(!q.isEmpty()){//f[u]为药水选取状态为u的最大power,看在其能到的点中有没有药水
Node now=q.peek();q.poll();
long ns=now.y;
int x=(int)now.x;
if(ns>f[u])break;
vis[x]=true;
for(int i=head[x];i>0;i=e[i].nxt){
int v=e[i].to;
if(t[v]==1||((u>>med[v])&1)==1)q.add(new Node(v,s[v]));
else vis[v]=true;//可以取到的药水
}
}
for(int j=1;j<=n;++j){
if(med[j]!=-1&&vis[j]&&((u>>med[j])&1)==0){//拿一个药水,更新状态
long sum=f[u]*g[j];
int nu=u|(1<<med[j]);
while(!q.isEmpty())q.poll();
q.add(new Node(1,0));
while(!q.isEmpty()){
Node now=q.peek();q.poll();
long ns=now.y;
int x=(int)now.x;
if(ns>sum)break;
if(ns!=0)sum=Math.min(inf,sum+g[x]);
if(sum>=mx)return true;
for(int i=head[x];i>0;i=e[i].nxt){
int v=e[i].to;
if(!vis[v])q.add(new Node(v,s[v]));//新扩展的点用以更新状态
else{
if(t[v]==2&&((nu>>med[v])&1)==0)continue;
q.add(new Node(v,0));//在u到过的点,不更新只用于扩展找新点
}
}
}
f[nu]=Math.max(f[nu],sum);//更新状态
}
}
return false;
}
static boolean get_f0(){
PriorityQueue<Node> q=new PriorityQueue<>((o1,o2)->{//贪心策略,先拿小的
if(o1.y-o2.y>0)return 1;
else if(o1.y-o2.y<0)return -1;
else return 0;
});
q.add(new Node(1,0));//id,s
while(!q.isEmpty()){
Node now=q.peek();q.poll();
long ns=now.y;
int x=(int)now.x;
if(ns>f[0])break;
f[0]=Math.min(inf,f[0]+g[x]);
if(f[0]>=mx)return true;
for(int i=head[x];i>0;i=e[i].nxt){
int v=e[i].to;
if(t[v]==1)q.add(new Node(v,s[v]));
//状态为0,不拿药水
}
}
return false;
}
static void solve() throws Exception{
AReader input=new AReader();
// Scanner input=new Scanner(System.in);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
String al="abcdefghijklmnopqrstuvwxyz";
char[] ac=al.toCharArray();
// int T=input.nextInt();
// while(T>0) {
// T--;
// }
m=-1;mx=0;
n=input.nextInt();
p=new long[n+1];
t=new long[n+1];
s=new long[n+1];
g=new long[n+1];
med=new int[n+1];
e=new Edge[n+1];
head=new int[n+1];
cnt=0;
for(int i=2;i<=n;++i){
p[i]=input.nextLong();
t[i]=input.nextLong();
s[i]=input.nextLong();
g[i]=input.nextLong();
addEdge((int)p[i],i);
mx=Math.max(mx,s[i]);
}
for(int i=2;i<=n;++i){
if(t[i]==2){
m++;
med[i]=m;
}else med[i]=-1;
}
int st=qm2(2,m+1);
f=new long[st+1];
f[0]=1;
if(get_f0())out.println("Yes");
else{
boolean ok=false;
for(int i=0;i<st;++i){
if(f[i]==0)continue;//该状态不存在
if(get_fu_nxt(i)){
ok=true;
break;
}
}
if(ok)out.println("Yes");
else out.println("No");
}
out.flush();
out.close();
}
public static void main(String[] args) throws Exception{
solve();
}
// public static final void main(String[] args) throws Exception {
// new Thread(null, new Tx2(), "线程名字", 1 << 27).start();
// }
// @Override
// public void run() {
// try {
// //原本main函数的内容
// solve();
//
// } catch (Exception e) {
// }
// }
static
class AReader{
BufferedReader bf;
StringTokenizer st;
BufferedWriter bw;
public AReader(){
bf=new BufferedReader(new InputStreamReader(System.in));
st=new StringTokenizer("");
bw=new BufferedWriter(new OutputStreamWriter(System.out));
}
public String nextLine() throws IOException{
return bf.readLine();
}
public String next() throws IOException{
while(!st.hasMoreTokens()){
st=new StringTokenizer(bf.readLine());
}
return st.nextToken();
}
public char nextChar() throws IOException{
//确定下一个token只有一个字符的时候再用
return next().charAt(0);
}
public int nextInt() throws IOException{
return Integer.parseInt(next());
}
public long nextLong() throws IOException{
return Long.parseLong(next());
}
public double nextDouble() throws IOException{
return Double.parseDouble(next());
}
public float nextFloat() throws IOException{
return Float.parseFloat(next());
}
public byte nextByte() throws IOException{
return Byte.parseByte(next());
}
public short nextShort() throws IOException{
return Short.parseShort(next());
}
public BigInteger nextBigInteger() throws IOException{
return new BigInteger(next());
}
public void println() throws IOException {
bw.newLine();
}
public void println(int[] arr) throws IOException{
for (int value : arr) {
bw.write(value + " ");
}
println();
}
public void println(int l, int r, int[] arr) throws IOException{
for (int i = l; i <= r; i ++) {
bw.write(arr[i] + " ");
}
println();
}
public void println(int a) throws IOException{
bw.write(String.valueOf(a));
bw.newLine();
}
public void print(int a) throws IOException{
bw.write(String.valueOf(a));
}
public void println(String a) throws IOException{
bw.write(a);
bw.newLine();
}
public void print(String a) throws IOException{
bw.write(a);
}
public void println(long a) throws IOException{
bw.write(String.valueOf(a));
bw.newLine();
}
public void print(long a) throws IOException{
bw.write(String.valueOf(a));
}
public void println(double a) throws IOException{
bw.write(String.valueOf(a));
bw.newLine();
}
public void print(double a) throws IOException{
bw.write(String.valueOf(a));
}
public void print(char a) throws IOException{
bw.write(String.valueOf(a));
}
public void println(char a) throws IOException{
bw.write(String.valueOf(a));
bw.newLine();
}
}
}