算法 码题集 BD202207 防AK题。困扰了好多天,帮助一下孩子吧

200 阅读3分钟

码题集OJ-防AK题 (matiji.net)

小度是这场比赛的出题人。作为一个良心出题人,他非常不希望这场比赛有人AK。但是作为一个菜鸡,他又没有能力出难题,怎么办呢?无奈的他准备去搬原题。
他发现,很多题目都是从经典题派生而来。具体地,1号题是唯一的一道原创题,其他的每道题都恰好由一道编号小于它的题直接派生而来。他在研究这些题考察的知识点的异同。

小度进行了m次操作,每次,小度会做下列两种操作之一:
1 x y 表示对于题目x,小度发现了一个考察的知识点y。(该题原有的知识点保持不变)
2 x y 表示对于题目x,询问所有由x派生的题目中,考察知识点y的有多少。

注意:

  1. 派生题这个词是出题人随便编的,所以派生题不继承母题考察的知识点。
  2. 操作可能重复输入,遇到重复的1操作请忽略。
格式

输入格式:

输入第一行为一个整数n,表示题目的数量。
第2行为n-1个整数,分别表示第2到n号题由哪道题派生而来。
第3行为一个整数m,表示操作数量。
接下来m行,每行都形如1 x y或者2 x y,含义见上。

输出格式:

对于每一个2操作,输出其答案并且换行。

样例 1

输入:

6
1 1 2 2 3
8
1 4 1
2 2 1
1 6 1
2 1 1
1 4 2
2 2 2
1 4 2
2 2 2

输出:

1
2
1
1

我写的代码 样例1对了,但是提交一直不对

import java.util.Scanner;  
import java.util.*;  
  
class Main {  
    private static int[][] pages;//邻接矩阵,记录边,也就是记录题目之间的关系  
    private static boolean[] flag;//记录题目是否访问过  
    private static List<List<Integer>> vt;//存储题目所发现的知识点  
  
    public static void main(String[] args) {  
  
        Scanner input = new Scanner(System.in);  
        // code here  
        int n = input.nextInt();  
        init(n);  
        for(int i = 0; i < n; i ++) {  
            List<Integer> list = new ArrayList<>();  
            vt.add(list);  
        }  
        //题目下标都是从0开始的,j为题目,从第二题开始  
        for(int j = 1; j < n;j++) {  
            int x = input.nextInt();  
            insertPages(x-1,j,1);//插入边,例2是1的派生题,则插入边为0,1,1权值记为1  
        }  
  
        int n2 = input.nextInt();  
        for(int i = 0; i < n2;i++) {  
            int a = input.nextInt();//记录操作数  
            int x = input.nextInt();  
            int y = input.nextInt();  
            if(a == 1) {//操作数为1时,加入发现的知识点  
                List<Integer> list = vt.get(x-1);  
                if(ish(list,y)) {//ish方法是判断是否重复,题目为已经发现的知识点,插入1次即可  
                list.add(y);  
            }  
            }else{  
                flag = new boolean[n];//这里是每次寻找派生题时,都置为false  
                //index 该题的下标  
                //要寻找的知识点  
                //记录知识点  
                //s 该s变量时记录进入dfs的第一题,它为头节点,也就是要寻找它派生题的知识点,它自己是不需要寻找的  
                System.out.println(dfs(x-1,y,0,x-1));//使用dfs遍历该x题的所有派生题  
            }  
  
        }  
        input.close();  
    }  
  
    /**  
    * 判断是否重复加入知识点  
    * @param list  
    * @param y  
    * @return  
    */  
    public static boolean ish(List<Integer> list,int y) {  
        for(Integer k:list) {  
            if(y == k) {  
            return false;  
            }  
        }  
        return true;  
    }  
  
    /**  
    *  
    * @param index 该题的下标  
    * @param y 要寻找的知识点  
    * @param count 记录知识点  
    * @param s 该s变量时记录进入dfs的第一题,它为头节点,也就是要寻找它派生题的知识点,它自己是不需要寻找的  
    * @return  
    */  
    public static int dfs(int index,int y,int count,int s) {  
        flag[index] = true;  
        if(index != s) {//寻找该题派生题的知识点,它自己是不需要寻找的  
            for(Integer i:vt.get(index)) {  
                if(i == y) {  
                    count ++;//发现知识点就+1  
                }  
            }  
        }  
        int w = getFirstIndex(index);//寻找该题的首个派生题,-1说明已经没有派生题了  
        while(w != -1) {  
            if(!flag[w]) {  
                count = dfs(w,y,count,s);  
            }else{  
                w = getNextIndex(index,w);  
            }  
        }  
        return count;  
    }  
  
    //返回该节点最近的下一个节点  
    public static int getFirstIndex(int v) {  
        for(int i = 0; i < pages.length;i++ ){  
            if(pages[v][i] > 0) {  
                return i;  
            }  
        }  
        return -1;  
    }  
    //寻找其他的邻接节点  
    public static int getNextIndex(int v1,int v2) {  
        for(int i = v2+1;i < pages.length; i++) {  
            if(pages[v1][i] > 0) {  
                return i;  
            }  
        }  
        return -1;  
    }  
    public static void init(int n) {  
        pages = new int[n][n];  
        vt = new ArrayList<>();  
    }  
    public static void insertPages(int v1, int v2, int weigth) {  
        pages[v1][v2] = weigth;  
    }  
}