CCCC_L2解题报告

题目链接:https://www.patest.cn/contests/gplt


数组索引应用

L2_002 链表去重

题目要求补充: 若没有需要删除的节点,则不输出删除节点。
思路:通过自身地址和next遍历链表,对每一个进行节点的值进行判断保留与否,将其地址放入res数组或者del数组
tip:直接利用数组的索引作地址
复杂度:O(n)

#include <bits/stdc++.h>
using namespace std;

const int maxn = 100005;
const int maxv = 10005;

int llist[2][maxn],res[maxn],del[maxn];
int  ab[maxv];

int main(){
//  freopen("002.txt","r",stdin); 
    int st,n;
    scanf("%d%d",&st,&n);
    for(int i = 0;i < n;i++){
        int add;
        scanf("%d",&add);
        scanf("%d%d",&llist[0][add],&llist[1][add]);   //list[0]存值,list[1]存下一个节点 
    }

    /*
    *通过自身地址和next遍历链表,对每一个进行节点的值进行判断 
    */
    int inr = 0, ind = 0;    
    int next = st;
    while(next != -1){
        int val = abs(llist[0][next]);
        if(ab[val]) del[ind++] = next;
        else {ab[val] = 1; res[inr++] = next; }
        next = llist[1][next];
    }

    del[ind] = -1;
    res[inr] = -1;


    for(int i = 0;i < inr-1; i++) printf("%05d %d %05d\n",res[i],llist[0][res[i]],res[i+1]);
    printf("%05d %d %d\n",res[inr-1],llist[0][res[inr-1]],res[inr]);
    if(ind>0){   //当有删除节点时才输出 
        for(int i = 0;i < ind-1; i++) printf("%05d %d %05d\n",del[i],llist[0][del[i]],del[i+1]);
        printf("%05d %d %d\n",del[ind-1],llist[0][del[ind-1]],del[ind]);
    }

} 

L2_018 多项式A除以B

思路:模拟多项式除法的步骤。
rs是商多项式,rm是余多项式,mid是运算时的中间多项式。伪代码:

rm = A;
while(当rm的最高次大于等于除式B的最高次时){
rs的第i项 cur = rm最高次系数 / B最高次系数;
mid 的各系数 = cur * B的各系数;
rm = rm - mid;
}

在数据结构上,用数组存储多项式,索引为指数,对应的值为该项的系数。
如rs[3] = -1 表示 -x^3

#include <bits/stdc++.h>
using namespace std;

const int maxn = 100000;

struct mul{
    int high;
    double C[maxn]; 
}A,B,rs,rm;

void read(int n ,mul& mm){   //读数 
    for(int i=0;i<n;i++){
        int s1,s2;
        scanf("%d%d",&s1,&s2);
        mm.C[s1] =(double) s2;
        if(i==0) mm.high = s1;
    }

}

double ff(double cc){   //对系数进行四舍五入计算 
    int bb = (int)(abs(cc)*100);
    int aa = bb % 10;
    bb = aa>=5? bb+10-aa: bb - aa; 
    return cc >= 0? bb/100.0 : -1*bb/100.0;
}

int main(){
//  freopen("018.txt","r",stdin);
    int n;
    scanf("%d",&n);
    read(n,A);
    scanf("%d",&n);
    read(n,B);
    rs.high = A.high - B.high;   //结果的最高位 
    int b = rs.high;
    rm = A;         //rm是每次的被除式 
    for(int i=A.high;i>=B.high;i--){    //i为当前被除式的最高项 
        double sh =  rm.C[i] / B.C[B.high];  //当前的商 
        rs.C[b] = sh;
        mul mid;   //中间多项式 
        mid.high = i;
        for(int j = B.high;j >= 0;j--){
            mid.C[b + j] = B.C[j] * sh;
        }
        for(int k = rm.high;k >= 0;k--){
            rm.C[k] -=mid.C[k];
        }
        rm.high = i-1;  
        b--;
    }

    int cnt1=0,cnt2=0;   //对商和余数的系数进行处理,并计数 
    for(int i = rs.high; i>=0;i--) {
        double cc = rs.C[i];
        rs.C[i] = ff(cc);
        if(rs.C[i]!=0.0) cnt1++;
    }

    for(int i = rm.high; i>=0;i--) {
        double cc = rm.C[i];
        rm.C[i] = ff(cc);
        if(rm.C[i]!=0.0) cnt2++;
    }

    //输出 
    if(cnt1==0) printf("0 0 0.0");   
    else {
        printf("%d",cnt1);
        for(int i = rs.high; i>=0;i--){
          if(rs.C[i]!=0.0) printf(" %d %.1f",i,rs.C[i]);
        }
    }

    printf("\n");
    if(cnt2==0 ) printf("0 0 0.0"); 
    else{
      printf("%d",cnt2);
      for(int i = rm.high; i>=0;i--){
       if(rm.C[i]!=0.0) printf(" %d %.1f",i,rm.C[i]);
      }
    }

}

L2_004 这是二叉搜索树吗

思路一:假定给定序列是所给二叉树的前序遍历结果A,将序列从小到大排序可得“二叉搜索树”的中序遍历结果B。如果该树是二叉搜索树,则根据A B 可以顺利建树; 若不能,将序列从大到小排序得到“二叉搜索树的镜像树”的中序遍历结果C。同理若其镜像树为二叉搜索树,则根据A C可以顺利建树。
复杂度:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<functional>
using namespace std;

const int maxn=1005;

int pre[maxn],in[maxn],lef[maxn],rig[maxn];
bool isBST=true;

int Tree(int L1,int R1,int L2,int R2){    //依据先序和中序建树 
    if(L1>R1) return -1;
    int root=pre[L1];
    int lenl,lenr,i;
    for(i=L2;i<=R2;i++)  if(root==in[i]) {lenl=i-L2;break;}
    if(i>R2) {
        isBST=false;
        return -1;
    }
    lenr=R2-L2-lenl;
    lef[L1]=Tree(L1+1,L1+lenl,L2,L2+lenl-1);
    rig[L1]=Tree(R1-lenr+1,R1,R2-lenr+1,R2);
    return L1;  
}

void postTra(int root){
    if(root==-1) return;
    postTra(lef[root]);
    postTra(rig[root]);
    if(root==0)printf("%d",pre[root]);  
    else printf("%d ",pre[root]);
}

int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&pre[i]);
        in[i]=pre[i];
    }
    sort(in,in+n);    //判断其本身是不是BST 
    Tree(0,n-1,0,n-1);    
    if(isBST) {
        printf("YES\n");
        postTra(0); 
    }


    /*
    *下面,由于Tree中的算法缺陷,当有重复的数的时候,会出错。就是样例那种情况过不了 
    */
    else{     //判断其是不是 BST的镜像 
        isBST=true;
        sort(in,in+n,greater<int>());
        memset(lef,0,sizeof(lef));
        memset(rig,0,sizeof(rig));
        Tree(0,n-1,0,n-1);
        if(isBST){
        printf("YES\n");
        postTra(0);
        }
        else printf("NO");
    } 
} 

思路二:直接利用前序遍历结果建树。假设其本身(其镜像)是二叉搜索树,找到第一个大于等于(小于)根的节点A,则得到左子树和右子树;且有右子树的所有点大于等于(小于)根,这个可以用来判断该前序遍历结果究竟是不是二叉搜索树:若右子树存在某点小于(或大于等于)根,则不是。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1005;
int pre[maxn];
bool isB;
int n;

struct node{
    int val;
    node *lef,*rig;
};

node* build(int l, int r,char c){
    if(l > r) return NULL;
    node *rt = new node();
    if(l == r){
        rt->val = pre[l];
        rt->lef = NULL;
        rt->rig = NULL;
        return rt;
    }

    rt->val = pre[l];
    int p = l + 1;
    if(c == '>'){    //本身是二叉搜索树 
        for(;p <= r; p++) if(pre[p] >= pre[l]) break;
        for(int j = p; j < n; j++) if(pre[j] < pre[l]){ isB = false; return NULL;}
    }

    else if(c == '<'){   //其镜像是二叉搜索树 
        for(;p <= r; p++) if(pre[p] < pre[l]) break;
        for(int j = p; j < n; j++) if(pre[j] >= pre[l]){ isB = false; return NULL;}
    }

    rt->lef = build(l + 1,p - 1,c);
    rt->rig = build(p,r,c);
    return rt;
}

void print(node* rt,int i){
    if(rt == NULL) return;
    print(rt->lef, i + 1);
    print(rt->rig, i + 1);
    if(i == 0) printf("%d",rt->val);  //最后打印这个 
    else printf("%d ",rt->val);
}


int main(){
    scanf("%d",&n);
    for(int i = 0; i < n; i++) scanf("%d",&pre[i]);
    isB = true;
    node *root = build(0,n - 1, '>');

    if(isB) {printf("YES\n");print(root,0);}   //判本身 
    else{   //判镜像 
        isB = true;
        root = build(0,n - 1, '<');
        if(isB){printf("YES\n");print(root,0);} 
        else printf("NO");
    }

} 

L2_006 树的遍历

思路:用lef[i] , rig[i]存储编号为i的节点的左右子节点的编号。

#include<cstdio>
#include<queue>
using namespace std;

#define rep(i,n) for(int i=0;i<n;i++)

const int maxn=35;

int lef[maxn],rig[maxn],in[maxn],post[maxn];
int n;

int Tree(int L1,int R1,int L2,int R2){
    if(L1>R1)return -1;
    int root=post[R1];    
    int lenl,lenr;
    for(int i=L2;i<=R2;i++) if(in[i]==root){lenl=i-L2; break;}  
    lenr=R2-L2-lenl;
    lef[R1]=Tree(L1,L1+lenl-1,L2,L2+lenl-1);
    rig[R1]=Tree(R1-lenr,R1-1,R2-lenr+1,R2);
    return R1;  
}
int main(){
//  freopen("data006.txt","r",stdin); 
    scanf("%d",&n);
    rep(i,n) scanf("%d",&post[i]);
    rep(i,n) scanf("%d",&in[i]);
    Tree(0,n-1,0,n-1);    

    queue<int>q;
    q.push(post[n-1]);
    while(!q.empty()){
        int t=q.front();
        q.pop();
        int k;
        rep(i,n)if(post[i]==t){k=i;break;}       
        if(lef[k]!=-1) q.push(post[lef[k]]);
        if(rig[k]!=-1) q.push(post[rig[k]]);
        if(t==post[n-1]) printf("%d",t);
        else printf(" %d",t);
    }
}

指针版:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 35;
int post[maxn],in[maxn];

struct Node{
    int val;
    Node *lef,*rig;
};

Node *root;

Node* build(int l1,int r1,int l2,int r2){
    if(l1 > r1) return NULL;
    Node *t = new Node();
    if(l1 == r1){
        t->val = post[r1];
        t->lef = NULL;
        t->rig = NULL;
        return t;
    }
    int k = post[r1],lenl,lenr;
    for(lenl = l2; lenl <= r2;lenl++) if(in[lenl] == k) break;
    lenl -= l2; lenr = r2 - l2 - lenl;
    t->val = k;
    t->lef = build(l1,l1 + lenl - 1,l2,l2 + lenl - 1);
    t->rig = build(r1 - lenr,r1 - 1,r2 - lenr + 1,r2);
    return t;
}

void BFS(){
    queue<Node*>que;
    que.push(root);
    while(!que.empty()){
        Node* cur = que.front();que.pop();
        if(cur == root) printf("%d",cur->val);
        else printf(" %d",cur->val);
        if(cur->lef!=NULL) que.push(cur->lef);
        if(cur->rig!=NULL) que.push(cur->rig);
    }
}
int main(){
//  freopen("data006.txt","r",stdin);
    int n;
    scanf("%d",&n);
    for(int i = 0;i < n;i++) scanf("%d",&post[i]);
    for(int i = 0;i < n;i++) scanf("%d",&in[i]);
    root = NULL;
    root = build(0,n-1,0,n-1);
    BFS();
    return 0;
}

L2_011 玩转二叉树

思路:BFS时先让右子树进入队列以实现翻转

#include <cstdio>
#include <queue>
using namespace std;

const int maxn=35;

#define rep(i,n) for(int i=0;i<n;i++)
int in[maxn],pre[maxn],lef[maxn],rig[maxn];

int Tree(int L1,int R1,int L2,int R2){
    if(L2>R2) return -1;
    int root=pre[L2];
    int lenl,lenr;
    for(int i=L1;i<=R1;i++) if(in[i]==root){lenl=i-L1;break;}
    lenr=R1-L1-lenl;
    lef[L2]=Tree(L1,L1+lenl-1,L2+1,L2+lenl);
    rig[L2]=Tree(R1-lenr+1,R1,R2-lenr+1,R2); 
    return L2;  
} 

int main(){
//  freopen("data011.txt","r",stdin);
    int n;
    scanf("%d",&n);
    rep(i,n)  scanf("%d",&in[i]);
    rep(i,n)  scanf("%d",&pre[i]);
    Tree(0,n-1,0,n-1);
    queue<int>q;
    q.push(pre[0]);
    while(!q.empty()){
        int cur=q.front();   
        q.pop(); 
        int tt;
        rep(i,n) if(pre[i]==cur){tt=i;break;}
        if(rig[tt]!=-1) q.push(pre[rig[tt]]);  //先让右子树进入队列
        if(lef[tt]!=-1) q.push(pre[lef[tt]]);
        if(cur==pre[0]) printf("%d",cur);
        else printf(" %d",cur);
    }
}

指针版:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 35;
int in[maxn], pre[maxn];

struct node{
    int val;
    node *lef, *rig;
};

node* BT(int l1,int r1,int l2,int r2){ //建树 
    if(l1 > r1)return NULL;
    node *rt = new node();
    if(l1 == r1){
        rt->val = pre[l1];
        rt->lef = NULL;
        rt->rig = NULL;
        return rt; 
    }

    rt->val = pre[l1];
    int lenl = l2, lenr;
    for(;lenl <= r2; lenl++)
        if(in[lenl] == pre[l1]) break;
    lenl = lenl - l2;
    lenr = r2 - l2 -lenl;
    rt->lef = BT(l1 + 1, l1 + lenl, l2, l2 + lenl - 1);
    rt->rig = BT(r1 - lenr + 1, r1, r2 - lenr + 1,r2); 
    return rt;
}

void sswap(node* rt){   //镜面反转
    if(rt == NULL) return;
    if(rt->lef == NULL && rt->rig == NULL) return;
    sswap(rt->lef);
    sswap(rt->rig);
    node *temp = rt->lef;
    rt->lef = rt->rig;
    rt->rig = temp;
}

int main(){
//  freopen("011.txt","r",stdin);
    int n;
    scanf("%d",&n);
    for(int i = 0; i < n; i++) scanf("%d",&in[i]);
    for(int i = 0; i < n; i++) scanf("%d",&pre[i]);
    node* root = new node();
    root = BT(0,n - 1,0,n - 1);
    sswap(root);


    queue<node*>que;  //层序遍历 
    que.push(root);
    while(!que.empty()){
        node* temp = que.front();
        que.pop();
        if(temp->val == root->val) printf("%d",temp->val);
        else printf(" %d",temp->val);
        if(temp->lef != NULL) que.push(temp->lef);
        if(temp->rig != NULL) que.push(temp->rig);
    }
}

set应用

L2_005 集合相似度

思路:Nc、Nt分别可以看做是两个集合的交集和并集的元素个数。
tip:可以使用set_union和set_intersection 得到元素个数, 但是这样会超时。因而使用 card(A+B) = card(A) + card(B) - card(A*B) 得到并集个数

#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;

const int maxn=55;
const int maxk=2005;
set<int>sets[maxn];
set<int> bing,jiao;

void getUnion(int i,int j){    //对集合set进行并交操作,其实并操作可以省去 
    set_union(sets[i].begin(),sets[i].end(),sets[j].begin(),sets[j].end(),inserter(bing,bing.begin()));
    return;
}

void getIntersection(int i,int j){
    set_intersection(sets[i].begin(),sets[i].end(),sets[j].begin(),sets[j].end(),inserter(jiao,jiao.begin()));
    return;
}

int main(){
//  freopen("data005.txt","r",stdin);    
    int n,m,k;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&m);
        for(int j=0;j<m;j++) {
            int nm;
            scanf("%d",&nm);
            sets[i].insert(nm);
        }
    }

    scanf("%d",&k);
    while(k){
        int s1,s2;
        scanf("%d%d",&s1,&s2);
        int cnt1,cnt2,cntb,cntj;
        cnt1=sets[s1].size();
        cnt2=sets[s2].size();
        bing.clear();
        jiao.clear();
//      getUnion(s1,s2);
//      cntb=bing.size();      //如果直接使用union后获取size的话,会超时。
        getIntersection(s1,s2);
        cntj=jiao.size();
        cntb=cnt1+cnt2-cntj;
        double rate=(double)cntj/cntb*100;
        printf("%.2lf%\n",rate);
        k--;
    }
} 

L2_016 愿天下有情人都是失散多年的兄妹

思路:将两个异性五代之内的亲人分别放在数组里,通过交运算判断是否为兄妹。
其中,用BFS获得五代之内的亲人,为控制“五代”,使用了5个queue。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 100005;

struct ps{
    int f,m;
    char s[2];
}p[maxn]; 

set<int> fam[maxn];
set<int> jiao;
int vis[maxn];   //标记已获取过某人i的亲属集合,避免重复获取
queue<int>que[5];

void getFam(int lv){ //BFS获得亲属
    for(int i = 0;  i < 5; i++) 
        while(!que[i].empty()) que[i].pop();
    que[0].push(lv);
    for(int i = 0;i < 5; i++){
        while(!que[i].empty()){
            int cur = que[i].front();
            que[i].pop();
            fam[lv].insert(cur);
            if(i != 4){
                if(p[cur].f!=-1) que[i+1].push(p[cur].f);
                if(p[cur].m!=-1) que[i+1].push(p[cur].m);
            }   
        }
    }
}

void getIntersection(int lv1,int lv2){
    jiao.clear();
    set_intersection(fam[lv1].begin(),fam[lv1].end(),fam[lv2].begin(),fam[lv2].end(),inserter(jiao,jiao.begin()));
}

int main(){
    //freopen("data016.txt","r",stdin);
    int n;
    scanf("%d",&n);
    for(int i = 0;i < n;i++){
        int id, fa, mo;
        scanf("%s %d %d",p[id].s,&fa,&mo);
        p[id].f = fa;
        p[id].m = mo;  //对其父母进行处理
        if(fa != -1)   {p[fa].f = -1; p[fa].m = -1;p[fa].s[0]= 'M';}
        if(mo != -1)   {p[mo].f = -1 ;p[mo].m = -1;p[mo].s[0] ='F';}    
    }

    int m;
    scanf("%d",&m);
    for(int i = 0;i < m;i++){
        int lv1,lv2;
        scanf("%d%d",&lv1,&lv2);
        if(p[lv1].s[0]==p[lv2].s[0]){printf("Never Mind\n");continue;}

        if(!vis[lv1]){
            vis[lv1] = 1;
            getFam(lv1);
        }
        if(!vis[lv2]){
            vis[lv2] = 1;
            getFam(lv2);
        }

        getIntersection(lv1,lv2);
        if(jiao.size()==0) printf("Yes\n");  //是否有相同亲属
        else printf("No\n");

    }

}

L2_019 悄悄关注

思路:用set存储非悄悄关注的名字,找到点赞数在平均値之上的名字,判断是否在set中。
tip:一开始我用集合的并运算进行判断,超时; 后改用set自己的方法就好了

#include <bits/stdc++.h>
using namespace std;

set<string> gz,temp,jiao;
map<string,int> dz;

//void getUnion(){
//  set_union(gz.begin(),gz.end(),temp.begin(),temp.end(),inserter(gz,gz.begin()));
//}
int main(){
//  freopen("002.txt","r",stdin);
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        string name;
        cin>>name;
        gz.insert(name);
    }
    int m;
    scanf("%d",&m);
    int sum=0;
    for(int i=0;i<m;i++){
        string nm;
        int cs;
        cin>>nm>>cs;
        dz[nm]=cs;
        sum+=cs;
    }

    double avg=(double)sum/m;
    bool flag=true;
    map<string,int>::const_iterator it;
    for(it=dz.begin();it!=dz.end();++it)
        if(it->second>avg && gz.find(it->first) == gz.end()){   //将集合运算改成这一句话,就不会超时  
//          temp.clear();
//          temp.insert(it->first);
//          int cntq=gz.size();
//          getUnion();
//          int cnth=gz.size();
            cout<<it->first<<endl;flag=false;
        }
    if(flag) cout<<"Bing Mei You"<<endl;
}

并查集

L2_007 家庭财产

思路一:利用集合来存储每个家庭的成员。
由于各个成员(包括每条信息自身和在信息中出现的)间的亲属关系比较难找,
所以对于每一个人,依据其编号、父母和子女的编号,对现有的所有family进行遍历,看是否能找到其亲属
若在现有的中找到了,则将该条信息中的所有人插入相应的set中;若没有,则新建一个set存储
由于个人的信息给出的顺序会影响到set的建立,即是一家人的两个人以及亲属,由于两个人之间的联系人没有出现,会暂时存于两个set中
所以,这题不能在找到一个set之后,就break,而是对于每一个编号都要遍历所有的set,因为他的亲戚set不只一个 。一个人的信息中的所有编号都要去找其所有的亲属set, 因为所有编号都可能成为纽带
在set建立完以后,就是set的合并了。用的set的交集和并集
复杂度:O(n^3)

#include <cstdio>
#include <set>
#include <algorithm>
#include <cstring> 
using namespace std;

const int maxn=1005;
const int maxk=5;

struct person{   //给出的个人信息 
    int ID[3+maxk];  //i=0:ID, 1:fID, 2:mID, 3~2+k:kids's ID
    int k,hc,ar;
}p[maxn];

struct family{   //家庭信息 
    int minID,mcnt;
    double avrh,avra;
}res[maxn];   //最终的结果 

set<int>fam[maxn];
set<int>jiao;
int visited[maxn]; //标记

void getUnion(int i,int j){  
    set_union(fam[i].begin(),fam[i].end(),fam[j].begin(),fam[j].end(),inserter(fam[i],fam[i].begin()));
} 

void getIntersection(int i,int j){
    set_intersection(fam[i].begin(),fam[i].end(),fam[j].begin(),fam[j].end(),inserter(jiao,jiao.begin()));
}

bool cmp(const family& f1,const family& f2){ //对最后的结果进行排序   (结构体的排序) 
    return f1.avra>f2.avra||(f1.avra==f2.avra&&f1.minID<f2.minID);
}
int main(){
//  freopen("data007.txt","r",stdin);
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){   //输入 
        scanf("%d%d%d%d",&p[i].ID[0],&p[i].ID[1],&p[i].ID[2],&p[i].k);
        for(int j=3;j<3+p[i].k;j++)scanf("%d",&p[i].ID[j]);
        scanf("%d%d",&p[i].hc,&p[i].ar);
    }



    int hcnt=0;   //记录已创建的家庭数目(有的家庭可能是一个) 
    for(int i=0;i<n;i++){     //每个人 
        memset(visited,0,sizeof(visited)); //是为了防止重复插入到一个家庭中 ,只要一个人中有一个编号与家庭A有关系插入后,此人其他编号就不用他们的for循环中插入了 
        bool flag=true;  //用于记录是否存在与此人有联系的家庭 
        for(int j=0;j<3+p[i].k;j++) {  //每个人中的每个编号 
            for(int x=0;x<hcnt;x++){
                if(visited[x]==0){
                    set<int>::iterator it;    //看家庭中有没有和当前编号一样的,如有,则说明是一家人 
                    for(it=fam[x].begin();it!=fam[x].end();++it) 
                        if(*it==p[i].ID[j]){   //插入
                        flag=false;
                        visited[x]=1; 
                        for(int y=0;y<3+p[i].k;y++){   
                            if(p[i].ID[y]!=-1) fam[x].insert(p[i].ID[y]);
                        }
                   }
                }

            }
        } 
        if(flag){  //新建家庭 
            for(int y=0;y<3+p[i].k;y++){
            if(p[i].ID[y]!=-1) fam[hcnt].insert(p[i].ID[y]);
            }
            hcnt++;
        }
    }

    for(int i=0;i<hcnt;i++)   //合并实质上是一个家庭分散在各个set中的成员 
        for(int j=i+1;j<hcnt;j++){
            if(!fam[i].empty()&&!fam[j].empty())
            getIntersection(i,j);
            if(jiao.size()!=0){getUnion(i,j);fam[j].clear();}
            }

    int ans=0;
    for(int i=0;i<hcnt;i++){   //生成家庭信息,即遍历某一个set,统计 
        if(!fam[i].empty()){
            res[ans].minID=*fam[i].begin();
            res[ans].mcnt=fam[i].size();
            double sumh=0,suma=0;
            set<int>::iterator it;
            for(it=fam[i].begin();it!=fam[i].end();++it){
                int j;
                for(j=0;j<n;j++) if(p[j].ID[0]==*it)break;
                sumh+=p[j].hc;
                suma+=p[j].ar;
            }
            res[ans].avrh=(double)sumh/fam[i].size();
            res[ans].avra=(double)suma/fam[i].size();
            ans++;
        }
    }

    sort(res,res+ans,cmp);

    printf("%d\n",ans);   //打印 
    for(int i=0;i<ans;i++){
        printf("%04d %d %.3lf %.3lf\n",res[i].minID,res[i].mcnt,res[i].avrh,res[i].avra);
    }
}

思路二:并查集 ,将一个家庭中编号小的作为根。
处理上,先完整存储输入,再进行并查操作分离。目的是为了在并操作的同时更新家庭信息。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1005;
const int maxb = 10000;
//int fa[maxn],exs[maxn];   段错误 
int fa[maxb],exs[maxb],vis[maxb];   //exs: 标记编号为i的人是否是题中给出的有房子的人,用于并操作入口; vis: 标记编号为i的人是否存在


struct person{   // 题中给出的有房子的人 
    int ID, num;
    vector<int>mem;   // 这人的亲属 
    double ps,par;
    person(int _num = 1, double _ps = 0.0,double _par = 0.0):num(_num),ps(_ps),par(_par){}
}p[maxb];   //段错误:不是p[maxn]

struct family{
    int minId, fnum;
    double as, aar; 
    family(int _fnum = 0,double _as = 0.0,double _aar = 0.0):fnum(_fnum),as(_as),aar(_aar){};
}fam[maxb];  //段错误:不是p[maxn] 

void setP(int id1 ,int id2){   //进行合并操作的时候,同时进行更新,使根始终保持着整个家庭的信息 
    fa[id2] = id1;
    p[id1].num += p[id2].num; 
    p[id1].ps += p[id2].ps;
    p[id1].par += p[id2].par;
}

int Find(int id){
    return fa[id] == id? id : fa[id] = Find(fa[id]);
}

void Union(int id1, int id2){
    int fa1 = Find(id1);
    int fa2 = Find(id2);
    if(fa1 != fa2){   //要排除这种情况,否则setp会错(自己加自己) 
        if(fa1 > fa2) setP(fa2,fa1);
        else setP(fa1,fa2);
    }
}

bool cmp(const family& a, const family& b) {
    return a.aar > b.aar || (a.aar == b.aar && a.minId < b.minId);
}

int main(){
//  freopen("data007.txt","r",stdin);
    int n;
    scanf("%d",&n);
    for(int i = 0;i < maxb;i++) fa[i] = i;

    for(int i = 0; i < n; i++){    //输入 
        int sid, fid, mid, k;
        scanf("%d%d%d%d",&sid,&fid,&mid,&k);
        exs[sid] = 1;
        vis[sid] = 1;
        p[sid].ID = sid;
        if(fid != -1) {p[sid].mem.push_back(fid);vis[fid] = 1;}
        if(mid != -1) {p[sid].mem.push_back(mid); vis[mid] = 1;}
        int kid;
        for(int j = 0;j < k;j++){
            scanf("%d",&kid);
            p[sid].mem.push_back(kid);
            vis[kid] = 1;
        }
        scanf("%lf%lf",&p[sid].ps,&p[sid].par);

    }


    for(int i = 0; i < maxb;i++)   // U/F 
        if(exs[i])   //只对有房子的进行遍历 
            for(int j = 0;j < p[i].mem.size();j++)
                Union(i,p[i].mem[j]);

    int cnt = 0;
    for(int i = 0; i < maxb;i++)
        if(vis[i])   //每个出现的人都有机会成为根,故均进行考察 
            if(Find(i) == i){
                fam[cnt].minId = i;
                fam[cnt].fnum = p[i].num;
                fam[cnt].as =  p[i].ps / p[i].num;
                fam[cnt].aar = p[i].par /p[i].num;
                cnt++;
            } 

    sort(fam,fam+n,cmp);

    printf("%d\n",cnt);
    for(int i = 0;i < cnt;i++){
        printf("%04d %d %.3lf %.3lf\n", fam[i].minId,fam[i].fnum,fam[i].as,fam[i].aar);
    }

}

L2_010 排座位

思路一:对于不是直接朋友的两人,通过求可达矩阵判断两人直接是否有公共朋友。(一开始不理解“朋友的朋友也是朋友。但敌人的敌人并不一定就是朋友,朋友的敌人也不一定是敌人”的含义,只是处理了矩阵和矩阵逆的乘积)
思路二:并查集。用矩阵存储题面所给的直接关系,用并查集划分朋友。矩阵可能的值为-1,0,1。为“1”时可直接确定输出,0和-1 还要利用并查集确定是否有公共朋友后确定输出。

#include <cstdio>
#include <cstring>
using namespace std;

const int maxn=105;

int fe[maxn][maxn];
int fat[maxn]; 

int find(int nd){
    int f = fat[nd];
    return nd== f? nd:fat[nd]=find(f);
}

void Union(int nd1,int nd2){
    if(fe[nd1][nd2] == 1){
        int f1 = find(nd1);
        int f2 = find(nd2);
        if(f1!=f2) f1>=f2? fat[f1]=f2 : fat[f2]=f1;
    }   
}

bool hcf(int nd1,int nd2){    //不是直接朋友关系的两个人(矩阵中为0 或 -1)是否有共同的朋友 
    return find(nd1)==find(nd2);
}

int main(){
//  freopen("010.txt","r",stdin);
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<m;i++){   //矩阵存储直接关系 
        int x,y,re;
        scanf("%d%d%d",&x,&y,&re);
        fe[x][y]=fe[y][x]=re;
    }

    for(int i=1;i<=n;i++) fat[i]=i;  
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++)
            Union(i,j);
    }

    for(int i=0;i<k;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        if(fe[x][y]==1) printf("No problem\n");
        else if(fe[x][y]==0){
            if(hcf(x,y)) printf("No problem\n");
            else printf("OK\n");
        }
        else{
            if(hcf(x,y)) printf("OK but...\n");
            else printf("No way\n");
        }
    }
} 

L2_013 红色警报

思路:并查集。
当一个城市被删掉时,可能出现三种情况:
1)其他城市的连通关系改变,此时连通支增多;
2)该城市原本便不与其他任何城市相连,删去后不会改变其他城市的连通关系,此时连通支减少;
3)该城市虽然与其他任何城市相连,但其删去后不会改变其他城市的连通关系,比如该城市是一个悬挂点,此时连通支不变;
因此本题可转化为研究删除某节点之后,图的连通支变化。
删除前后,用并查集生成连通支,并确定数量,如果连通支增多,则发出红色警报。

错误方法:

#include <cstdio>
#include <cstring>
using namespace std;

const int maxn=505;
const int maxm=5005;

int ff[2][maxn],ct[maxn][maxn],del[maxn],fat[maxn];   //ff[2]交替使用存储并查集中的根节点 

int find(int i,int *f){
    return f[i]==i? i : f[i]=find(f[i],f);
}

void Union(int i,int j,int *f){
    int fa1 = find(i,f);
    int fa2 = find(j,f);
    if(fa1 == fa2) return;
    fa1>=fa2?f[i]=fa2:f[j]=fa1; 
}

int count(int *f,int nn){   //数连通支数量 
    memset(fat,0,sizeof(fat));
    int cnt=0;
    for(int i=0;i<nn;i++){
        if(f[i]!=-1){
            if(fat[f[i]]==0){
                fat[f[i]]=1;
                cnt++;
            }
        }

    }
    return cnt;
}

int main(){
//  freopen("data013.txt","r",stdin);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        int v1,v2;
        scanf("%d%d",&v1,&v2);
        ct[v1][v2]=ct[v2][v1]=1;
    }

    for(int j=0;j<n;j++) ff[0][j]=j;  //最开始的并查集 
    for(int x=0;x<n;x++)
        for(int y=x+1;y<n;y++){
            if(ct[x][y]==1) Union(x,y,ff[0]);
        }


    int k;
    scanf("%d",&k);
    for(int i=0;i<k;i++){
        int lc;
        scanf("%d",&lc);
        del[lc]=1;    //删除前 
        for(int z=lc+1;z<n;z++)if(ct[lc][z]) ct[lc][z]=0;
        for(int z=0;z<lc;z++)if(ct[z][lc]) ct[z][lc]=0;

        int in=(i+1)%2; //删除后 
        for(int j=0;j<n;j++) if(del[j]!=1)ff[in][j]=j;else ff[in][j]=-1;
        for(int x=0;x<n;x++)
            for(int y=x+1;y<n;y++){
                if(ct[x][y]==1) Union(x,y,ff[in]);
            }

        bool isChanged=false;
        for(int x=0;x<n;x++){
            for(int y=x+1;y<n;y++){
                if(x!=lc&&y!=lc)
                {   int cnt1=count(ff[in],n);           
                    int cnt2=count(ff[(in+1)%2],n);
                    if(cnt1>cnt2){
                        isChanged=true;
                        break;
                    }   
                }

            }
            if(isChanged) break;
        }

        if(isChanged) printf("Red Alert: City %d is lost!\n",lc);
        else printf("City %d is lost.\n",lc);
    }

    if(k==n) printf("Game Over.");
    return 0;
}

正确方法:
从反向考虑。 先假设给出城市全部被删去,有一个初始的图。然后从最后一个城市开始,添加城市,每填一个城市,更新(不是重建)并查集,更新连通块的数目。
ans 用于离线输出。

#include <bits/stdc++.h>
using namespace std;

int const maxn = 505;

int fa[maxn],del[maxn],vis[maxn],ans[maxn];   //del 失守城市序列,vis 标记城市是否处于删除状态  ans添加城市后的连通块数目 
int pa[maxn][maxn];
int cnt;

int Find(int i){
    return fa[i] == i? i: fa[i] = Find(fa[i]);
}

void Union(int i, int j){
    int fa1 = Find(i);
    int fa2 = Find(j);
    if(fa1 != fa2){
        cnt--;   //合并时连通块数目减少 
//      fa1 > fa2 ? fa[fa1] = fa2 : fa[fa2] = fa1;
        fa[fa1] = fa2; 
    }
}

int main(){
//  freopen("data013.txt","r",stdin);
    int n,m,k;
    scanf("%d%d",&n,&m);
    for(int i = 0; i < n; i++) fa[i] = i;
    for(int i = 0; i < m; i++){
        int x,y;
        scanf("%d%d",&x,&y);
        pa[x][y] = pa[y][x] = 1;
    }

    scanf("%d",&k);
    for(int i = 0; i < k; i++){scanf("%d",&del[i]); vis[del[i]] = 1;}

    cnt = n - k;
    for(int i = 0;i < n; i++)    //给出的城市删除后的初始并查集 
        if(vis[i] != 1)
            for(int j = 0; j < n; j++){
                if(vis[j] != 1 && pa[i][j]) Union(i,j);
            }

    ans[k] = cnt;
    for(int i = k - 1; i >= 0; i--){  //每添加一个城市更新并查集 
        int ii = del[i];
        cnt++;
        vis[ii] = 0;
        for(int j = 0; j < n; j++) 
            if(vis[j] != 1 && pa[ii][j]) Union(ii,j);
        ans[i] = cnt;    //添加后连通块数目 
    }

    for(int i = 0; i < k ; i++) printf("%d ",ans[i]);
    printf("\n");

    for(int i = 0; i < k; i++){
        if(ans[i] < ans[i + 1]) printf("Red Alert: City %d is lost!\n",del[i]);
        else printf("City %d is lost.\n",del[i]);
    }
    if(k == n) printf("Game Over.");

}

其他

L2_001 紧急救援

思路:dijkstra算法。
用num[i]和w[i]表示从出发点到i结点拥有的路的条数,以及能够找到的救援队的数目。当进行松弛操作的时候,更新dis[v], num[v],w[v]。
用pre[i]存储前一个结点,使用栈打印路径。

#include <cstdio>
#include <stack>
using namespace std;
const int maxn = 505;
const int inf = 1000;
int n, m, S, D;
int dis[maxn], team[maxn], e[maxn][maxn], num[maxn], w[maxn], pre[maxn];
bool vis[maxn];

int main() {
    scanf("%d%d%d%d", &n, &m, &S, &D);
    for(int i = 0; i < n; i++)
        scanf("%d", &team[i]);
    for(int i = 0;i < n;i++) dis[i] = inf;
    int a, b, c;
    while(m) {
        scanf("%d%d%d", &a, &b, &c);
        e[a][b] = c;
        e[b][a] = c;
        m--;
    }

    dis[S] = 0;
    w[S] = team[S];
    num[S] = 1;
    for(int i = 0; i < n; i++) {     
        int x, minn = inf;
        for(int j = 0; j < n; j++)  // 找dis最小的点
            if(!vis[j] && dis[j] < minn)  minn = dis[x=j];

        vis[x] = true;
        for(int v = 0; v < n; v++) {
            if(!vis[v] && e[x][v] != 0) {
                if(dis[x] + e[x][v] < dis[v]) {
                    dis[v] = dis[x] + e[x][v];
                    num[v] = num[x];
                    w[v] = w[x] + team[v];
                    pre[v] = x;
                } else if(dis[x] + e[x][v] == dis[v]) {
                    num[v] = num[v] + num[x];
                    if(w[x] + team[v] > w[v]) {
                        w[v] = w[x] + team[v];
                        pre[v] = x;
                    }
                }
            }
        }
    }
    printf("%d %d\n", num[D], w[D]);

    stack<int>path; //打印 
    path.push(D);
    int temp=D;
    while(pre[temp]!=S){
        temp=pre[temp];
        path.push(temp);
    }
    printf("%d",S);
    while(!path.empty()){printf(" %d",path.top());path.pop();} 
    return 0;
}

L2_003 月饼

思路:贪心。每次选取单位价格最高的月饼进行售卖。
注意点:1. 题面给的样例是整数,但并不能就完全定义成整数,应仔细分析各种量的类型
2.要考虑到需求量大于所有月饼的量之和。之前没有考虑,故会重复的加上第一种月饼的量,因为order[i]初始的是0
复杂度:O(nlogn)

#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn=1005;

double amt[maxn],prc[maxn];
int ord[maxn];


bool cmp(const int i,const int j){
    double a=prc[i]/amt[i];
    double b=prc[j]/amt[j];
    if(a!=b) return a>b;
    else return amt[i]<amt[j];
} 

int main(){
    int n;
    double m;
    scanf("%d%lf",&n,&m);
    for(int i=0;i<n;i++) scanf("%lf",&amt[i]);
    for(int i=0;i<n;i++) scanf("%lf",&prc[i]);
    for(int i=0;i<n;i++) ord[i]=i;
    sort(ord,ord+n,cmp);    //间接排序,排索引

    int cnt=0;
    double rest=m,sum=0;
    while(rest>=amt[ord[cnt]]&&cnt<n){    //考虑需求量大于所有月饼的量之和 
        sum=sum+prc[ord[cnt]];
        rest=rest-amt[ord[cnt]];
        cnt++;
    }

    if(cnt!=n){
      double part=rest/amt[ord[cnt]]*prc[ord[cnt]];
      sum=sum+part; 
    }
    printf("%.2lf",sum);
}

L2_008 最长对称子串

思路一:枚举对称中心然后向外拓展,拓展到不能再拓展为止。由于子串字符是奇数个和偶数个不大一样,所以分了类。
复杂度:O(n^2)
思路二:Manacher 算法

#include<cstdio>
#include<iostream>
using namespace std;

int main(){
//  freopen("data008.txt","r",stdin);
    string str;
    getline(cin,str);
    int len=str.length();


    int omaxm=0,omaxl=0;  //奇数个 
    for(int i=0;i<len;i++){
        int j=0;
        while(i-j>=0&&i+j<=len){
            if(str[i-j]==str[i+j]) j++;
            else break;
        }
        if(2*j-1>omaxl){omaxl=2*j-1;omaxm=i;}
    }

    int emaxm=0,emaxl=0;  //偶数个 
    for(int i=0;i<len-1;i++){
        int j=0;
        while(i-j>=0&&i+j+1<=len){
            if(str[i-j]==str[i+j+1]) j++;
            else break;
        }
        if(2*j>emaxl){emaxl=2*j;emaxm=i;}   
    }

    printf("%d",emaxl>omaxl?emaxl:omaxl);
} 

L2_009 抢红包

思路:在读入的时候就可以对发红包的人(发出的钱)和抢到红包的人(对抢到的红包个数和红包金额进行计数)进行处理。

#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn=10005;
const int maxk=25;

struct person{
    int mon,cnt;
}p[maxn];

int ind[maxn];

bool cmp(int i,int j){
    return p[i].mon>p[j].mon||(p[i].mon==p[j].mon&&p[i].cnt>p[j].cnt)||(p[i].mon==p[j].mon&&p[i].cnt==p[j].cnt&&i<j);
}

int main(){
//  freopen("data009.txt","r",stdin);
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        ind[i]=i;
        int k;
        scanf("%d",&k);
        int sum=0;
        for(int j=0;j<k;j++){
        int ID,amt;
        scanf("%d%d",&ID,&amt); 
        p[ID].mon+=amt;
        p[ID].cnt++;
        sum+=amt;
        }
        p[i].mon-=sum;
    }

    sort(ind+1,ind+n+1,cmp);

    for(int i=1;i<=n;i++){
        printf("%d %.2lf\n",ind[i],p[ind[i]].mon/100.0);
    }
}

L2_012 关于堆的判断

思路:先建堆,在用下标关系进行判断。 在寻找某值对应的下标时,采用类似于二叉树搜索的方法减少搜索规模。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1005;
int n,c1,c2;
int H[maxn];

void sswap(int i ,int j){
    int t = H[i];
    H[i] = H[j];
    H[j] = t;
}

void buildHeap(){    //建堆 
    for(int i = 1;i <= n;i++){
        int p = i;
        while(p > 1){
            if(H[p] < H[p/2]){sswap(p,p/2); p = p / 2;}
            else break; 
        }
    }
}

int find(int st, int k){   //找某个元素的下标 
    int i = st;
    if( 2 * i > n) if(H[i] == k) return i;   //该节点为叶节点 
    if( 2 * i == n) {if(H[i] == k) return i; else if(H[2 * i] == k) return 2 * i;}  //该节点只有左子节点 
    while(2 * i < n){   //该节点有两个子节点 
        if(H[i] == k) return i; 
        if(H[2 * i] > k && H[2 * i + 1] <= k) return find(2 * i + 1, k);   //若有一边堆顶元素大于(不能是大于等于),则可将范围缩小至1/2 
        else if(H[2 * i] <= k && H[2 * i + 1] > k) return find(2 * i, k);
        else{   //两个子堆的堆顶都小于该元素 
            int in = find(2 * i,k);
            if(in != -1) return in;
            else {return find(2 * i + 1,k);}
        }
    }
    return -1;
}

int main(){
    freopen("data012.txt","r",stdin);
    int m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i++) scanf("%d",&H[i]);
    buildHeap();
    string str0;
    getline(cin,str0);
    while(m){
        bool flag = false;
        string str,sub;
        getline(cin,str);
        stringstream ss(str);
        if(str[str.length()-1]=='t'){ss >> c1;if(c1 == H[1]) flag = true;}

        else if(str[str.length()-1] == 's') {
            ss >> c1;
            ss >> sub;
            ss >> c2;
            int in1,in2;
            in1 = find(1,c1); in2 = find(1,c2);
            if( in1/2 == in2/2) flag = true;
            }

        else {
            ss >> c1;
            bool isp = true;
            for(int i = 0; i < 4;i++){
                ss >> sub;
                if(i == 1 && sub[0] == 'a') isp = false;
            }
            ss >> c2;
            int in1,in2;
            in1 = find(1,c1); in2 = find(1,c2);

            if(isp){if(in1 == in2 / 2) flag = true;}  //之前少了括号 
            else if(in2 == in1 / 2) flag = true;
        }

         printf("%c\n",flag?'T':'F');
        m--;
    }
}

L2_014 列车调度

思路:用一个数组存储每条铁轨最后的列车编号,规定每次处理待加入的列车时,要么加入一条新铁轨,要么插入到已存在铁轨中,此铁轨满足最后的列车编号与之最接近且略小于它。这样就可以使用二分法确定插入位置了。
tip:一开始是遍历已有铁轨,将列车插入到第一个小于它的铁轨上,超时。

#include <cstdio>
#include <vector>
using namespace std;

const int maxn=100005;

int rail[maxn];

int main(){
    int n,cnt=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        int x;
        scanf("%d",&x);
        if(cnt==0||rail[cnt-1]<x) rail[cnt++]=x;
        else{
             int l=0, r=cnt-1;
             while(l<r){
                 int mid=l+(r-l)/2;
                 if(rail[mid]>x)
                     r=mid-1;
                 else l=mid+1;
             }
             rail[l]=x;
         }
    }
    printf("%d",cnt);
}

L2_015 互评成绩

思路:冒泡排序

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;

const int maxn=10005;

double grade[maxn];

void swap(int x,int y){
    double tmp=grade[x];
    grade[x]=grade[y];
    grade[y]=tmp;
}

void bubble(int m,int n){   //冒泡排序进行前m个的排序 
    for(int i=0;i<m;i++){
        for(int j=n-1;j>i;j--)
            if(grade[j]>grade[j-1]) swap(j,j-1);
    }
} 

int main(){
//  freopen("data015.txt","r",stdin);
    int n,m,k;
    scanf("%d%d%d",&n,&k,&m);
    for(int i=0;i<n;i++){
        double pg[10];
        memset(pg,0,sizeof(pg));
        for(int j=0;j<k;j++) scanf("%lf",&pg[j]);
        sort(pg,pg+k);
        double sum=0.0;
        for(int x=1;x<k-1;x++) sum+=pg[x];
        grade[i]=sum/(k-2);
    }

    bubble(m,n);
    printf("%.3lf",grade[m-1]);
    for(int i=m-2;i>=0;i--) printf(" %.3lf",grade[i]);

}

L2_017 人以群分

思路:先排序,后分组:偶数个人均分,奇数个人使活跃度大的一组多一人。

#include <bits/stdc++.h>
using namespace std;

const int maxn=100005;
const int INF=-2100000000;
int p[maxn];
bool cmp(int i,int j){
  return i>j;
}
int main(){
//  freopen("data017.txt","r",stdin);
  int n;
  cin>>n;
  for(int i=0;i<n;i++){
    cin>>p[i];
  }
  sort(p,p+n,cmp);


  int summ=0;
  for(int i=0;i<n;i++) summ+=p[i];


  if(n%2==0){
    int sum1=0;
    for(int j=0;j<=(n-1)/2;j++){
      sum1+=p[j];
    }
    int sum2=summ-sum1;
    int dif=sum1-sum2;
    printf("Outgoing #: %d\n",n/2);
  printf("Introverted #: %d\n",n/2);
  printf("Diff = %d",dif);
  }

  else{
    int sum1=0;
    for(int j=0;j<=(n-1)/2;j++){
      sum1+=p[j];
    }
    int sum2=summ-sum1;
    int dif1=sum1-sum2;


    printf("Outgoing #: %d\n",n/2+1);
    printf("Introverted #: %d\n",n/2);
    printf("Diff = %d",dif1);
    }  
  }

L2_020 功夫传人

思路:计算出每位得道者与祖师爷之间相隔的代数,进而得到其功力。

#include <bits/stdc++.h>
using namespace std;

const int maxn=100005;
int sf[maxn];   //每人的师傅 
int ddz[maxn];  //得道者的索引 
int glzz[maxn];   //每位得道者功力增长 

int ds;

void findds(int i){    //确定得道者与祖师爷之间相隔代数 
    if(sf[i]==0) {ds++;return;}
    ds++;
    findds(sf[i]);
}

int main(){
    sf[0]=0;
//  freopen("002.txt","r",stdin);
    int n;
    double z,r;
    scanf("%d %lf %lf",&n,&z,&r);
    int cnt=0;
    for(int i=0;i<n;i++){
        int k;
        scanf("%d",&k);
        if(k==0){ddz[cnt++]=i;scanf("%d",&glzz[i]);}
        else{
        for(int j=0;j<k;j++){
            int x;
            scanf("%d",&x);
            sf[x]=i;
        }   

        }

    }



    double sum=0.0;
    for(int i=0;i<cnt;i++){
        ds=0;
        findds(ddz[i]);
        double ysgl=z;
        for(int j=0;j<ds;j++){
            ysgl=ysgl*(100-r)/100;
        }
        ysgl*=glzz[ddz[i]];
        sum+=ysgl;      
    }
    int summ=(int)(sum);
    printf("%d",summ);  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值