[题解]CodeChef JUNE Challenge 17

这次比赛打的很有趣啊。
Challenge大战!

A Good Set

题意简述

定义“好集合”为:集合中元素两两不同,权值均在 [1,500] ,且不存在三个元素 a,b,c 使得 a+b=c
输出大小为 n 的“好集合”

数据范围

1T,n100

思路

一开始想二进制之类的balabala……
最后发现输出最大的前 n 个数不就行了……
Naive!

代码

    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>
    void read(T &x)
    {
        char ch=getchar();
        for (x=0;ch<'0'||ch>'9';ch=getchar());
        for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
    }
    int T,n;
    int main()
    {
        read(T);
        while (T--)
        {
            read(n);
            for (int i=1;i<=n;i++)
                printf("%d%c",501-i," \n"[i==n]);
        }
        return 0;
    } 

Xenny and Coin Rankings

题意简述

给二维坐标定义了一个序。
大体图形是这样子的:

74218539610

然后问你一个矩形范围内标号最大的点是多少。

数据范围

1T100
1u,v109

思路

标号最大的肯定是右上角的啊…
公式题。

代码

    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>
    void read(T &x)
    {
        char ch=getchar();
        for (x=0;ch<'0'||ch>'9';ch=getchar());
        for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
    }
    typedef long long ll;
    int T,u,v,x;
    ll ans;
    int main()
    {
        read(T);
        while (T--)
        {
            read(u),read(v);
            x=u+v;
            ans=1LL*(1+x)*x/2;
            ans+=u+1;
            printf("%lld\n",ans);
        }
        return 0;
    } 

Chef and the Feast

题意简述

n 盘菜,每盘菜有美味度ai,你可以分若干次吃完。
每次吃,假设吃掉 k 盘,你将获得ki=1ai×k的幸福度。
最大化幸福度之和。

数据范围

1T8
1n105
0|ai|108

思路

贪心。
一开始想的正的都在一起选,负的一个一个选。
wawawawawa。
后来撕烤了一下。
猜了个结论:一起选的一定是从大到小连续的一段区间,剩下的单独选。
排序,从大到小选,如果加上这盘菜还能使答案 >0 就选……
正确性证明见wa爷爷blog:http://wronganswer.blog.uoj.ac/blog/2667

代码

    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>
    void read(T &x)
    {
        char ch=getchar();
        int f=1;
        for (x=0;ch!='-'&&(ch<'0'||ch>'9');ch=getchar());
        if (ch=='-') f=-1,ch=getchar();
        for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
        x*=f;
    }
    typedef long long ll;
    int T,n;
    int seq[100010];
    ll u,sum,ans,size;
    int main()
    {
        read(T);
        while (T--)
        {
            read(n);
            for (int i=1;i<=n;i++)
                read(seq[i]);
            sort(seq+1,seq+n+1);
            sum=0,ans=0,size=0;
            for (int i=n;i>=1;i--)
                if (seq[i]*size+sum >=0)
                    sum+=seq[i],size++;
                else
                {
                    for (int j=i;j>=1;j--)
                        ans+=seq[j];
                    break;
                }
            ans+=sum*size;
            printf("%lld\n",ans);
        }
        return 0;
    } 

Pairwise union of sets

题意简述

给你 n 个集合。
每个集合的元素[1,k]
问有多少对集合,它们的并集为 1 k的全集。

数据范围

1T10
1n,k2500
sizei10000

思路

看到题第一感随手打了个bitset…
TLE……
加了个优化,如果两个集合的size之和都到不了 k 就不计算它们的并。
TLE……
随眼一瞟看到元素个数之和10000
按照 k 大小分类,小的就bitset开小一点……
然后就A了……
O(Tmin(n2,(10000k2)2)k64)
然后就过了……

代码

    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>
    void read(T &x)
    {
        char ch=getchar();
        for (x=0;ch<'0'||ch>'9';ch=getchar());
        for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
    }
    int T,n,k,ans,u,v;
    int num[2510];
    void work1000()
    {
        bitset<2510> B[2510];
        for (int i=1;i<=n;i++)
            B[i].reset();
        memset(num,0,sizeof(num));
        for (int i=1;i<=n;i++)
        {
            read(u);
            for (int j=1;j<=u;j++)
            {
                read(v);
                B[i].set(v);
                num[i]++;
            }
        }
        ans=0;
        for (int i=1;i<=n;i++)
            for (int j=i+1;j<=n;j++)
                if (num[i]+num[j]>=k && (B[i]|B[j]).count()==k)
                    ans++;
        printf("%d\n",ans);
    }
    void work100()
    {
        bitset<110> B[2510];
        for (int i=1;i<=n;i++)
            B[i].reset();
        memset(num,0,sizeof(num));
        for (int i=1;i<=n;i++)
        {
            read(u);
            for (int j=1;j<=u;j++)
            {
                read(v);
                B[i].set(v);
                num[i]++;
            }
        }
        ans=0;
        for (int i=1;i<=n;i++)
            for (int j=i+1;j<=n;j++)
                if (num[i]+num[j]>=k && (B[i]|B[j]).count()==k)
                    ans++;
        printf("%d\n",ans);
    }
    int main()
    {
        read(T);
        while (T--)
        {
            read(n),read(k);
            if (k<=100)
                work100();
            else
                work1000();
        }
        return 0;
    }  

Triplets

题意简述

给定三个序列 A,B,C f(x,y,z)=(x+y)(y+z)[xy][zy]
i,j,kf(Ai,Bj,Ck)

数据范围

1T10
1n105

思路

two pointers。
都排序之后枚举 j i,k相应移动即可。

代码

    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>
    void read(T &x)
    {
        char ch=getchar();
        for (x=0;ch<'0'||ch>'9';ch=getchar());
        for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
    }
    typedef long long ll;
    #define MAXN 100010
    const int mo=1000000007;
    int T,a,b,c;
    int A[MAXN],B[MAXN],C[MAXN];
    ll suma,sumc,ans;
    int main()
    {
        read(T);
        while (T--)
        {
            read(a),read(b),read(c);
            for (int i=1;i<=a;i++)
                read(A[i]);
            for (int i=1;i<=b;i++)
                read(B[i]);
            for (int i=1;i<=c;i++)
                read(C[i]);
            sort(A+1,A+a+1);
            sort(B+1,B+b+1);
            sort(C+1,C+c+1);
            suma=0,sumc=0,ans=0;
            for (int i=1,j=1,k=1;j<=b;j++)
            {
                while (i<=a && A[i]<=B[j]) suma=(suma+A[i++])%mo;
                while (k<=c && C[k]<=B[j]) sumc=(sumc+C[k++])%mo;
                ans=(ans+1LL*(i-1)*(k-1)%mo*B[j]%mo*B[j] + suma*(k-1)%mo*B[j] + sumc*(i-1)%mo*B[j] + suma*sumc)%mo;
            }
            printf("%lld\n",ans);
        }
        return 0;
    } 

Chef and Prime Queries

题意简述

给出长度为 n 的数列A
Ai=pk11pk22
每次询问 Ri=Lpkk[xpy]

数据范围

1n,q105
1ai,x,y106

思路

稍微转化一下就会发现这是一个矩形和问题。
离线询问,将每个询问拆成 L,R 两个,拆分之后的询问就变成了 [x,y] 区间已有的质数个数累加和是多少。
排序。顺序扫描 A ,添加质数,回答询问。
质因子个数d7
总复杂度 O(Td(q+n)logn)

代码

#include<bits/stdc++.h>
using namespace std;
template<typename T>
void read(T &x)
{
    char ch=getchar();
    for (x=0;ch<'0'||ch>'9';ch=getchar());
    for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
}
int n,q,t1,t2,t3,t4,Q_cnt,now,tmp;
int a[100010],ans[100010];
int p_cnt,prime[100010],mn[1000010],go[1000010],ti[1000010],idx[1000010];
bool not_p[1000010];
void sieve(int n)
{
    not_p[1]=1;
    for (int i=2;i<=n;i++)
    {
        if (!not_p[i])
        {
            prime[++p_cnt]=i;
            mn[i]=p_cnt;
            go[i]=1;
            ti[i]=1;
        }
        for (int j=1;i*prime[j]<=n && j<=p_cnt;j++)
        {
            not_p[i*prime[j]]=1;
            if (i%prime[j]==0)
            {
                mn[i*prime[j]]=mn[i];
                go[i*prime[j]]=go[i];
                ti[i*prime[j]]=ti[i]+1;
                break;
            }
            mn[i*prime[j]]=j;
            go[i*prime[j]]=i;
            ti[i*prime[j]]=1;
        }
    }
    p_cnt=0;
    for (int i=1;i<=n;i++)
    {
        if (!not_p[i]) p_cnt++;
        idx[i]=p_cnt;
    }
}
struct BIT{
    static const int size=100000;
    int d[100010];
    BIT()
    {
        memset(d,0,sizeof(d));
    }
    int lowbit(int x)
    {
        return x&(-x);
    }
    void modify(int pos,int val)
    {
        for (;pos<=size;pos+=lowbit(pos))
            d[pos]+=val;
    }
    int query(int l,int r)
    {
        int ret=0;
        for (;r;r-=lowbit(r))
            ret+=d[r];
        for (;l;l-=lowbit(l))
            ret-=d[l];
        return ret;
    }
}T;
struct Query{
    int pos,x,y,ty,id;
    Query(int _pos=0,int _x=0,int _y=0,int _ty=0,int _id=0)
    {
        pos=_pos,x=_x,y=_y,ty=_ty,id=_id;
    }
    bool operator < (const Query &n1) const
    {
        return pos<n1.pos;  
    }
    void print()
    {
        printf("pos=%d,[%d,%d],id=%d\n",pos*ty,x,y,id);
    }
}Q[200010];
int main()
{
    sieve(1000000);

    read(n);
    for (int i=1;i<=n;i++)
        read(a[i]);
    read(q);
    for (int i=1;i<=q;i++)
    {
        read(t1),read(t2),read(t3),read(t4);
        Q[++Q_cnt]=Query(t1-1,idx[t3-1],idx[t4],-1,i);
        Q[++Q_cnt]=Query(t2,idx[t3-1],idx[t4],1,i);
    }
    sort(Q+1,Q+Q_cnt+1);
    now=1;
    for (int i=1;i<=Q_cnt;i++)
    {
        while (now<=Q[i].pos)
        {
            tmp=a[now];
            while (tmp!=1)
            {
                T.modify(mn[tmp],ti[tmp]);
                tmp=go[tmp];
            }
            now++;
        }
        ans[Q[i].id]+=Q[i].ty*T.query(Q[i].x,Q[i].y);
    }
    for (int i=1;i<=q;i++)
        printf("%d\n",ans[i]);
    return 0;
}

Cloning

题意简述

定义两个长度相等的子串是“相似的”:将它们的元素分别排序后,一一对应,最多只有一位不同。
给出长度为 n 的数列A
每次询问两个长度相等子串是否是相似的。

数据范围

1T3
1n,Ai105

思路

hash+主席树。
按照权值哈希,权值为 x 的元素对哈希值的贡献为basex
在主席树上,两个区间一起跑,找到哈希相同的最大前缀和最大后缀。
判断一下即可。

代码

        #include<bits/stdc++.h>
        using namespace std;
        template<typename T>
        void read(T &x)
        {
            char ch=getchar();
            for (x=0;ch<'0'||ch>'9';ch=getchar());
            for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
        }
        #define MAXN 100010
        #define MAXV 100000
        typedef unsigned long long ull;
        const ull base=13331;
        ull basepow[MAXN],val1,val2;
        int T,n,q,l1,r1,l2,r2,posl,posr;
        int seq[MAXN];
        namespace PT
        {
            struct Node{
                Node *ch[2];
                ull sum;
                void pushup()
                {
                    sum=ch[0]->sum+ch[1]->sum;
                }
            }*null=new Node;

            Node pool[MAXN*30];
            Node *tail=pool;
            Node *NewNode()
            {
                Node *p=tail++;
                p->ch[0]=p->ch[1]=null;
                p->sum=0;
                return p;
            }
            Node *Root[MAXN];
            void init()
            {
                null->ch[0]=null->ch[1]=null;
                null->sum=0;
                tail=pool;
                for (int i=0;i<=n;i++)
                    Root[i]=NewNode();
            }
            void add(int pos,int l,int r,Node *node,Node *pre)
            {
                if (l==r)
                {
                    node->sum=pre->sum+basepow[pos];
                    return;
                }
                int mid=(l+r)>>1;
                if (pos<=mid)
                {
                    if (node->ch[0]==null)
                        node->ch[0]=NewNode();
                    node->ch[1]=pre->ch[1];
                    add(pos,l,mid,node->ch[0],pre->ch[0]);
                }
                else
                {
                    if (node->ch[1]==null)
                        node->ch[1]=NewNode();
                    node->ch[0]=pre->ch[0];
                    add(pos,mid+1,r,node->ch[1],pre->ch[1]);
                }
                node->pushup();
            }
            int queryl(int l,int r,Node *l1,Node *r1,Node *l2,Node *r2)
            {
                if (l==r) return l;
                int mid=(l+r)>>1;
                if (r1->ch[0]->sum - l1->ch[0]->sum != r2->ch[0]->sum - l2->ch[0]->sum)
                    return queryl(l,mid,l1->ch[0],r1->ch[0],l2->ch[0],r2->ch[0]);
                else
                    return queryl(mid+1,r,l1->ch[1],r1->ch[1],l2->ch[1],r2->ch[1]);
            }
            int queryr(int l,int r,Node *l1,Node *r1,Node *l2,Node *r2)
            {
                if (l==r) return l;
                int mid=(l+r)>>1;
                if (r1->ch[1]->sum - l1->ch[1]->sum != r2->ch[1]->sum - l2->ch[1]->sum)
                    return queryr(mid+1,r,l1->ch[1],r1->ch[1],l2->ch[1],r2->ch[1]);
                else
                    return queryr(l,mid,l1->ch[0],r1->ch[0],l2->ch[0],r2->ch[0]);
            }
            ull judge(int L,int R,int l,int r,Node *l1,Node *r1,Node *l2,Node *r2)
            {
                if (R<l || r<L) return 0;
                if (L<=l && r<=R) return r1->sum - l1->sum + r2->sum - l2->sum;
                int mid=(l+r)>>1;
                return judge(L,R,l,mid,l1->ch[0],r1->ch[0],l2->ch[0],r2->ch[0])+judge(L,R,mid+1,r,l1->ch[1],r1->ch[1],l2->ch[1],r2->ch[1]);
            }
        }
        int main()
        {
            basepow[0]=1;
            for (int i=1;i<=100000;i++)
                basepow[i]=basepow[i-1]*base;
            read(T);    
            while (T--)
            {
                read(n),read(q);
                PT::init();
                for (int i=1;i<=n;i++)
                {
                    read(seq[i]);
                    PT::add(seq[i],1,MAXV,PT::Root[i],PT::Root[i-1]);
                }
                for (int i=1;i<=q;i++)
                {
                    read(l1),read(r1),read(l2),read(r2);
                    val1=PT::Root[r1]->sum - PT::Root[l1-1]->sum;
                    val2=PT::Root[r2]->sum - PT::Root[l2-1]->sum;
                    if (val1==val2)
                        puts("YES");
                    else
                    {
                        posl=PT::queryl(1,MAXV,PT::Root[l1-1],PT::Root[r1],PT::Root[l2-1],PT::Root[r2]);
                        posr=PT::queryr(1,MAXV,PT::Root[l1-1],PT::Root[r1],PT::Root[l2-1],PT::Root[r2]);
                        if ((posl+1>posr-1 || judge(posl+1,posr-1,1,MAXV,PT::Root[l1-1],PT::Root[r1],PT::Root[l2-1],PT::Root[r2])==0) && (val1+basepow[posl]==val2+basepow[posr] || val1+basepow[posr]==val2+basepow[posl]))
                            puts("YES");
                        else
                            puts("NO");
                    }
                }
            }
            return 0;
        }  

Euler Sum

题意简述

ni=1ie

数据范围

1n104000
代码长度限制1KB = =(真的大丈夫?

思路

首先语言肯定选个自带高精度的…现学了一下PY…
然后应该用什么算法呢…找了半天e的性质无果…
忘了墩爷还是YJQ随口一句类欧几里得…好嘛…现学一下类欧。
类欧好像有两种形式,一种是分数型 ax+bc 这样…然后你就转换一下枚举顺序balabala
另一种是一般型 kx 这样…然后你就每次讨论一下 k >1就减掉一个下取整,否则就去取个倒数酱…
然后我就xjb用泰勒展开强行把 e 拟合成分数,跑了一下分数型…然后就过了…
考后想了下可不可以用ae+bce+dx这样的形式做,下取整和取倒数都很好实现,还没试……
吐槽一下PY还限制递归深度……

代码

    import sys
    sys.setrecursionlimit(1000000000)
    def solve(A,B,C,n):
        if (A==0):
            return (n+1)*(B//C)
        if (A>=C):
            return solve(A%C,B,C,n)+n*(n+1)//2*(A//C)
        if (B>=C):
            return solve(A,B%C,C,n)+(n+1)*(B//C)
        m=(A*n+B)//C
        return n*m-solve(C,C-B-1,A,m-1)

    n=int(input())
    C=1
    A=1
    lim=3000
    for i in range (1,lim):
        C*=i
    for i in range (1,lim):
        A=(A*i)+1
    print(solve(A,0,C,n)) 

Persistent oak

题意简述

给你一棵树,树上每个节点都有一个重量承受度 wi
当其子树的重量之和 >wi ,那么这棵子树就会断掉…如果有多个节点满足,选择最远离树根的节点。
有两个操作:
1.某个节点长出了重量为 x 的果实。
2.询问一棵子树内的果实数量,同时清空。
所有操作可持久化,每次操作会告诉你前驱版本的编号…

数据范围

1T10
1n,q105
1wi,x107

思路

先来考虑不持久化怎么做。
树链剖分,维护 sum 维护 wisum 的最小值维护一个加标记和一个清空标记。
1.操作就在链上跳,找到第一个满足要求的操作,子树清空,找不到就将到根的路径所有节点维护的那个最小值减去一个值。
2.子树查询 sum ,子树清空。
可持久化的话…加标记标记永久化一下。
清空的话…直接连到0号版本的树上(还有这种操作?.jpg),然后再将从父亲到根的路径都加上sum。

代码

    #include<bits/stdc++.h>
    using namespace std;
    template<typename T>
    void read(T &x,char ch=getchar())
    {
        for (x=0;ch<'0'||ch>'9';ch=getchar());
        for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
    }
    #define MAXN 100010
    #define INF 1LL<<60
    typedef long long ll;
    int T,n,m,ord,sta,opt,u,v,t;
    int seq[MAXN],size[MAXN],son[MAXN],fa[MAXN],top[MAXN],deep[MAXN],dfn[MAXN],rdfn[MAXN];
    ll w[MAXN],FK;
    namespace PT
    {
        struct Node{
            Node *ch[2];
            ll mn,add,sum;
            void pushup()
            {
                mn=min(ch[0]->mn,ch[1]->mn)+add;
                sum=ch[0]->sum+ch[1]->sum;
            }
        }*null=new Node;

        Node pool[MAXN*400];
        Node *tail;
        Node *ver[MAXN];

        Node *NewNode(Node *f=0)
        {
            Node *p=tail++;
            if (f)
            {
                p->ch[0]=f->ch[0];p->ch[1]=f->ch[1];
                p->mn=f->mn;
                p->add=f->add;
                p->sum=f->sum;
            }
            else
                p->ch[0]=p->ch[1]=null;
            return p;
        }
        Node *build(int l,int r)
        {
            Node *ret=NewNode();
            if (l==r)
            {
                ret->mn=w[seq[l]];
                ret->add=0;
                ret->sum=0;
                return ret;
            }
            int mid=(l+r)>>1;
            ret->ch[0]=build(l,mid);
            ret->ch[1]=build(mid+1,r);
            ret->pushup();
            return ret;
        }
        void init()
        {
            tail=pool;
            null->ch[0]=null->ch[1]=null;
            null->mn=INF;
            null->sum=0;
            null->add=0;
            ver[0]=build(1,n);
        }
        void add(int pos,int l,int r,Node* &now,ll val)
        {
            now=NewNode(now);
            if (l==r)
            {
                now->sum+=val;
                return;
            }
            int mid=(l+r)>>1;
            if (pos<=mid)
                add(pos,l,mid,now->ch[0],val);
            else
                add(pos,mid+1,r,now->ch[1],val);
            now->pushup();
        }
        void Iadd(int L,int R,int l,int r,Node* &now,ll val)
        {
            now=NewNode(now);
            if (L<=l && r<=R)
            {
                now->add+=val;
                now->mn+=val;
                return;
            }
            int mid=(l+r)>>1;
            if (L<=mid)
                Iadd(L,R,l,mid,now->ch[0],val);
            if (R>mid)
                Iadd(L,R,mid+1,r,now->ch[1],val);
            now->pushup();
        }
        void clear(int L,int R,int l,int r,ll tag,Node* &now,Node *pre)
        {
            if (L<=l && r<=R)
            {
                FK+=now->sum;
                now=NewNode(pre);
                now->add-=tag;
                now->mn-=tag;
                return;
            }
            now=NewNode(now);
            int mid=(l+r)>>1;
            if (L<=mid)
                clear(L,R,l,mid,tag+now->add,now->ch[0],pre->ch[0]);
            if (R>mid)
                clear(L,R,mid+1,r,tag+now->add,now->ch[1],pre->ch[1]);
            now->pushup();
        }
        int query_mn(int L,int R,int l,int r,Node *now,ll val)
        {
            if (now->mn >= val) return 0;
            if (l==r) return seq[l];
            int mid=(l+r)>>1;
            if (R>mid)
            {
                int ret=query_mn(L,R,mid+1,r,now->ch[1],val-now->add);
                if (ret) return ret;
            }
            if (L<=mid)
                return query_mn(L,R,l,mid,now->ch[0],val-now->add);
            return 0;
        }
        void debug(int l,int r,Node *now)
        {
            printf("[%d,%d]:mn=%lld,add=%lld,sum=%lld\n",l,r,now->mn,now->add,now->sum);
            if (l==r)
                return;
            int mid=(l+r)>>1;
            debug(l,mid,now->ch[0]);
            debug(mid+1,r,now->ch[1]);
        }
    }
    struct edge{
        int s,t,next;
    }e[MAXN<<1];
    int head[MAXN],cnt;
    void addedge(int s,int t)
    {
        e[cnt].s=s;e[cnt].t=t;e[cnt].next=head[s];head[s]=cnt++;
        e[cnt].t=s;e[cnt].s=t;e[cnt].next=head[t];head[t]=cnt++;
    }
    void dfs(int node,int lastfa,int de)
    {
        fa[node]=lastfa;
        deep[node]=de;
        size[node]=1;
        son[node]=0;
        for (int i=head[node];i!=-1;i=e[i].next)
            if (e[i].t!=lastfa)
            {
                dfs(e[i].t,node,de+1);
                size[node]+=size[e[i].t];
                if (size[e[i].t]>size[son[node]])
                    son[node]=e[i].t;
            }
    }
    void dfs2(int node,int lastfa,int tp)
    {
        top[node]=tp;
        dfn[node]=++ord;
        seq[ord]=node;
        if (son[node])
            dfs2(son[node],node,tp);
        for (int i=head[node];i!=-1;i=e[i].next)
            if (e[i].t!=lastfa && e[i].t!=son[node])
                dfs2(e[i].t,node,e[i].t);
        rdfn[node]=ord;
    }
    int get_mn(int sta,int u,ll v)
    {
        while (u)
        {
            int ret=PT::query_mn(dfn[top[u]],dfn[u],1,n,PT::ver[sta],v);
            if (ret) return ret;
            u=fa[top[u]];
        }
        return 0;
    }
    void mo_add(int sta,int u,ll v)
    {
        while (u)
        {
            PT::Iadd(dfn[top[u]],dfn[u],1,n,PT::ver[sta],v);
            u=fa[top[u]];
        }
        return;
    }
    int main()
    {
        read(T);
        while (T--)
        {
            read(n),read(m);n++;
            memset(head,0xff,sizeof(head));
            cnt=0;
            for (int i=2;i<=n;i++)
            {
                read(u);u++;
                addedge(i,u);
                read(w[i]);
            }
            w[1]=INF;
            dfs(1,0,1);
            ord=0;
            dfs2(1,1,1);
            PT::init();
            for (int i=1;i<=m;i++)
            {
                read(sta),read(opt),read(u);u++;
                if (opt==1)
                {
                    read(v);
                    t=get_mn(sta,u,v);
                    printf("%d\n",max(0,t-1));
                    if (t)
                    {
                        PT::ver[i]=PT::ver[sta];
                        FK=0;
                        PT::clear(dfn[t],rdfn[t],1,n,0,PT::ver[i],PT::ver[0]);
                        mo_add(i,fa[t],FK);
                    }
                    else
                    {
                        PT::ver[i]=PT::ver[sta];
                        PT::add(dfn[u],1,n,PT::ver[i],v);
                        mo_add(i,u,-v);
                    }
                }
                else
                {
                    PT::ver[i]=PT::ver[sta];
                    FK=0;
                    PT::clear(dfn[u],rdfn[u],1,n,0,PT::ver[i],PT::ver[0]);
                    printf("%lld\n",FK);
                    mo_add(i,fa[u],FK);
                }
            }
        }
        return 0;
    }   

Saboteur (Challenge)

题意简述

给你一个图,让你删除某些点,使得剩下的点形成一棵树。
最小化删除权值和。

数据范围

1n104
1m5×104
子任务:
1. n20
2. n40
3. n=m
4. mn+3
5.完全图
6.轮子图
7.随机图

思路

这题甩锅给同学了,代码就不发了……
子任务1暴力,子任务3基环树DP一下,子任务4缩一缩点,子任务5保留两个权值最大的,子任务6两种情况,中间点删了就贪心,不删就DP一下。
子任务7
随机一个加点顺序,如果不成环就加,最后选权值最大的树。
多次随机,贪心优化。
子任务2用随机跑了。
被日本min_25踩了…代码1千行…
OI选手和人家完全没法比啊……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值