• “科林明伦杯”哈尔滨理工大学第十届程序设计竞赛(同步赛)


    差一点点就AK的一场。

    差在高度相同的植物合并qwq

    A. 点对最大值 (Nowcoder 5758 A)

    题目大意

    给定一棵树,有点权和边权,找到最大的点对价值。价值为两个点的点权和加上两点之间的边权和。

    解题思路

    考虑分治。

    答案就是在根各子树中的最大值以及跨越根的最大值中取。

    跨越根的最大值就是两个点,它们的点权加上到根节点的边权的和是第一大和第二大的。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N=1e6+8;
    
    int n,num;
    
    int head[N];
    
    int nxt[N*2],to[N*2],cost[N*2];
    
    int val[N];
    
    LL ans=0;
    
    void add(int u,int v,int w){
        num++;
        nxt[num]=head[u];
        to[num]=v;
        cost[num]=w;
        head[u]=num;
        num++;
        nxt[num]=head[v];
        to[num]=u;
        cost[num]=w;
        head[v]=num;
    }
    
    LL DFS(int u,int fa){
        int cnt=0;
        LL qmq=val[u];
        LL qnq=-9147483647;
        LL tmp;
        for(int v,i=head[u];i;i=nxt[i]){
            v=to[i];
            if (v==fa) continue;
            ++cnt;
            tmp=DFS(v,u)+cost[i];
            ans=max(ans,tmp+val[u]);
            if (qmq<=tmp){
                qnq=qmq;
                qmq=tmp;
            }else if (qnq<tmp){
                qnq=tmp;
            }
        }
        ans=max(ans,qmq+qnq);
        return max(qmq,qnq);
    }
    
    int main(void) {
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ii++) {
            read(n);
            num=0;
            for(int a,b,i=1;i<n;++i){
                read(a);
                read(b);
                add(a,i+1,b);
            }
            for(int i=1;i<=n;++i){
                read(val[i]);
            }
            DFS(1,1);
            write(ans,'
    ');
            ans=-9147483647;
            for(int i=0;i<=n;++i) head[i]=0;
        }
        return 0;
    }
    


    B. 减成一 (Nowcoder 5758 B)

    题目大意

    给定一个数组,每次选择一个区间使区间的数减一,问进行多少次可以让所有数变成(1)

    解题思路

    NOIP都考了两次了......

    答案就是差分数组的元素与(0)(max)的和。

    因为,假设相邻两个数(a),(b),如果(a<b)(b)的在减少成(1)的时候,(a)就早就减成(1)了,所以操作次数取大的那个。

    (a>b),那么(a)在减到(1)那一刻,(b)早就变成(1)了,而在(b)变成(1)之后,(b)之后的那些数就不能减,只能等(a)减完再搞(b)后面的数,而(b)后面的数(c)减成1就还需要(c-b)次,因为前(b)次可以和之前减(a)时一起操作。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    int main(void) {
        ios::sync_with_stdio(false); 
        cin.tie(0); cout.tie(0);
        int t;
        cin>>t;
        while(t--){
            LL ans=0;
            int n;
            cin>>n;
            int la=1;
            int qwq=0;
            while(n--){
                cin>>qwq;
                ans+=max(0,qwq-la);
                la=qwq;
            }
            cout<<ans<<endl;
        }
        return 0;
    }
    


    C. 面积 (Nowcoder 5758 C)

    题目大意

    给定正方形边长,算一个图形面积。它由一个正方形和四个以其边长为直径的半圆构成。

    解题思路

    算就可以了。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    const double pi=3.14;
    
    int main(void) {
        ios::sync_with_stdio(false); 
        cin.tie(0); cout.tie(0);
        int t;
        cin>>t;
        while(t--){
            LL x;
            cin>>x;
            double ans=x*x+2*pi*(x*1.0/2)*(x*1.0/2);
            cout<<fixed<<setprecision(2)<<ans<<endl;
        }
        return 0;
    }
    


    D. 扔硬币 (Nowcoder 5758 D)

    题目大意

    同时扔(n)枚硬币,正反概率相同。已知至少有(m)枚硬币反面朝上,问恰有(k)枚硬币正面朝上的概率是多少。

    解题思路

    条件概率。

    (n-m<k)答案就是0.
    否则就是(dfrac{C^{k}_{n}}{sumlimits_{i=0}^{n-m}C_{n}^{m+i}})

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    const int N = 1e5+8;
    
    const LL mo=1e9+7;
    
    LL jie[N+1],invjie[N+1];
    
    LL qpower(LL a,LL b){
        LL qwq=1;
        while(b){
            if (b&1) qwq=qwq*a%mo;
            b>>=1;
            a=a*a%mo;
        }
        return qwq;
    }
    
    LL inv(LL qwq){
        return qpower(qwq,mo-2);
    }
    
    LL C(int n,int m){
        if (n<m) return 0;
        else return jie[n]*invjie[m]%mo*invjie[n-m]%mo;
    }
    
    LL sum(int n,int l,int r){
        LL qwq=0;
        for(int i=l;i<=r;++i){
            qwq=(qwq+C(n,i))%mo;
        }
        return qwq;
    }
    
    int main(void) {
        ios::sync_with_stdio(false); 
        cin.tie(0); cout.tie(0);
        jie[0]=jie[1]=invjie[0]=invjie[1]=1;
        for(int i=2;i<=N-3;++i){
            jie[i]=jie[i-1]*i%mo;
            invjie[i]=inv(jie[i]);
        }
        int t;
        cin>>t;
        while(t--){
            int n,m,k;
            cin>>n>>m>>k;
            LL ans=0;
            if (n-m>=k) ans=C(n,k)*inv(sum(n,m,n))%mo;
            cout<<ans<<endl;
        }
        return 0;
    }
    


    E. 赛马 (Nowcoder 5758 E)

    题目大意

    田忌赛马。已知对方马的出场顺序和战斗力,安排自己的马的出场顺序得到最大赢数。

    解题思路

    对方马战力从小到大依次匹配我方大于其战斗力的最小战斗力的马。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    int main(void) {
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ii++) {
            int n;
            read(n);
            multiset<int> qwq;
            for(int u,i=1;i<=n;++i){
                read(u);
                qwq.insert(u);
            }
            vector<int> b(n);
            for(int i=1;i<=n;++i)
                read(b[i-1]);
            sort(b.begin(),b.end());
            int ans=0;
            for(auto i:b){
                auto it=qwq.upper_bound(i);
                if (it!=qwq.end()){
                    ans++;
                    qwq.erase(it);
                }
            }
            write(ans,'
    ');
        }
        return 0;
    }
    


    F. 三角形 (Nowcoder 5758 F)

    题目大意

    将长为(a)的木棒分割成若干段长度为正整数的小木棒,要求任意三段都不能组成三角形,问最多分割成的小木棒的个数。

    解题思路

    斐波那契数列就是恰好不能组成三角形的边长极限。对其求和直到刚好大于等于(a)即可。

    如果分割有剩余的话那这个就不能分割的。

    数很大最好用int128。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    int main(void) {
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ii++) {
            __int128 n;
            read(n);
            __int128 a,b,c;
            a=1;
            b=1;
            n=n-2;
            int cnt=1;
            while(n>=0){
                cnt++;
                c=a+b;
                n=n-c;
                a=b;
                b=c;
            }
            write(cnt,'
    ');
        }
        return 0;
    }
    


    G. 养花 (Nowcoder 5758 G)

    题目大意

    小明有(n)棵植物,所有植物排成一排,植物的初始高度为数组(h),他想让植物的高度都恰好达到(k),小明有(m)瓶药水,但药水分为(4)种:

    • 选择一棵高度为(a_0)的植物变为(b_0)高度的植物
    • 选择一棵高度在([a_1,a_2])区间内的植物变为(b1)高度的植物
    • 选择一棵高度为(a_1)的植物变为([b_1,b_2])区间内某一高度的植物
    • 选择一棵高度在([a_1,a_2])区间内的植物变为([b_1,b_2])区间内某一高度的植物

    由于第(i)瓶药水最多可以使用(C_{i})次,小明想知道他最多让多少棵植物高度达到(k)

    解题思路

    决策类问题,很明显带有反悔的抉择,考虑网络流。

    不同药水之间可以进行转移,即对于某个植物,我们用了某个药水后可以用另一个药水。于是这些药水可以构成一张图,对于一个植物,它可以从某点出发,根据边进行转移,而某些药水可以到达终点高度,于是我们只要能到达那些药水即可。

    首先药水之间相互连边,能够抵达指定高度的药水再与终点连边,边流量都是无穷。

    对于植物,连向能给它施加的药水,边权为无穷。

    再将植物与起点连边,边权为(1),表示只能施加一次该植物。

    我们发现还有药的使用次数,它是流进该药或流出该药的流量和,我们要对它进行限制,于是我们把药拆成两个点,流进点和流出点,两者的连边的流量即为药的使用次数,这样就限制了药的使用次数。

    此时我们发现会(T),因为植物数太多了,但注意到这里我们只关心高度,而高度最多只有(100),那么我们可以把同高度的植物合并。

    设高度为(i)的植物出现了(cnt_i)次,那么起点就连向代表植物高度为(i)的点,边容量就是(cnt_i)

    然后跑一遍网络流,最大流即是答案。

    综上,药拆点,限制药的流量。药流出点与能抵达的药流进点连边,以及和可抵达的终点连边,起点和植物高度连边,植物高度再和相应的药的流进点连边。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N=4e6;
    
    const int M=2e5;
    
    const int INF=1e9+7;
    
    int head[M],nxt[N*2],to[N*2],team[M],dis[M];
    
    LL flow[N*2];
    
    int n,e,st,en,num,m,k;
    
    void add(int u, int v, int w) {
    	num++;
    	nxt[num] = head[u];
    	to[num] = v;
    	flow[num] = w;
    	head[u] = num;
    	num++;
    	nxt[num] = head[v];
    	to[num] = u;
    	flow[num] = 0;
    	head[v] = num;
    }
    
    bool BFS() {
    	int l = 0, r = 1;
    	team[1] = st;
    	for(int i=0;i<=en;++i) dis[i]=0;
    	dis[st] = 1;
    	while (l < r) {
    		int u = team[++l];
    		for (int v, i = head[u]; i; i = nxt[i]) {
    			v = to[i];
    			if (dis[v] == 0 && flow[i]) {
    				dis[v] = dis[u] + 1;
    				team[++r] = v;
    			}
    		}
    	}
    	if (dis[en]) return true;
    	else return false;
    }
    
    LL DFS(int u, LL f) {
    	if (u == en) return f;
    	LL qwq = 0, tmp = 0;
    	for (int v, i = head[u]; i; i = nxt[i]) {
    		v = to[i];
    		if (dis[v] == dis[u] + 1 && flow[i]) {
    			qwq = DFS(v, min(f - tmp, flow[i]));
    			flow[i] -= qwq;
    			flow[i ^ 1] += qwq;
    			tmp += qwq;
    			if (tmp == f) return tmp;
    		}
    	}
    	return tmp;
    }
    
    struct yao{
        int z;
        int cnt;
        int a1,a2,b1,b2;
    }yy[1006];
    
    int cnt[105];
    
    int main(void) {
        int kase; read(kase);
        num=1;
        for (int ii = 1; ii <= kase; ii++) {
            read(n);
            read(m);
            read(k);
            for(int h,i=1;i<=n;++i){
                read(h);
                cnt[h]++;
            }
            for(int i=1;i<=m;++i){
                read(yy[i].z);
                read(yy[i].cnt);
                if (yy[i].z==1){
                    read(yy[i].a1);
                    read(yy[i].b1);
                }else if (yy[i].z==2){
                    read(yy[i].a1);
                    read(yy[i].a2);
                    read(yy[i].b1);
                }else if (yy[i].z==3){
                    read(yy[i].a1);
                    read(yy[i].b1);
                    read(yy[i].b2);
                }else{
                    read(yy[i].a1);
                    read(yy[i].a2);
                    read(yy[i].b1);
                    read(yy[i].b2);
                }
            }
            st=0;
            en=2*m+1+100;
            for(int i=1;i<=100;++i){
                add(st,i+2*m,cnt[i]);
            }
            for(int i=1;i<=m;++i){
                add(2*i-1,2*i,yy[i].cnt);
            }
            for(int i=1;i<=m;++i){
                if (yy[i].z==1||yy[i].z==2){
                    if (yy[i].b1==k) add(i*2,en,INF);
                }else{
                    if (yy[i].b1<=k&&yy[i].b2>=k) add(i*2,en,INF);
                }
            }
            for(int i=1;i<=100;++i){
                for(int j=1;j<=m;++j){
                    if (yy[j].z==1||yy[j].z==3){
                        if (yy[j].a1==i) add(2*m+i,2*j-1,INF);
                    }else{
                        if (yy[j].a1<=i&&yy[j].a2>=i) add(2*m+i,2*j-1,INF);
                    }
                }
            }
            for(int i=1;i<=m;++i){
                for(int j=1;j<=m;++j){
                    if (i==j) continue;
                    if (yy[i].z==1||yy[i].z==2){
                        if (yy[j].z==1||yy[j].z==3){
                            if (yy[i].b1==yy[j].a1) add(2*i,2*j-1,INF);
                        }else{
                            if (yy[i].b1>=yy[j].a1&&yy[i].b1<=yy[j].a2) add(2*i,2*j-1,INF);
                        }
                    }else{
                        if (yy[j].z==1||yy[j].z==3){
                            if (yy[i].b1<=yy[j].a1&&yy[i].b2>=yy[j].a1) add(2*i,2*j-1,INF);
                        }else{
                            if (!(yy[i].b1>yy[j].a2||yy[i].b2<yy[j].a1)) add(2*i,2*j-1,INF);
                        }
                    }
                }
            }
            int ans=0;
            while(BFS()){
                ans+=DFS(st,INF);
            }
            write(ans,'
    ');
            for(int i=0;i<=en;++i) head[i]=0;
            for(int i=1;i<=100;++i) cnt[i]=0;
            num=1;
        }
        return 0;
    }
    


    H. 直线 (Nowcoder 5758 H)

    题目大意

    平面上的(n)条直线最多有多少个交点。

    解题思路

    学过高中数学的都知道答案是(dfrac{n(n-1)}{2})

    (int128)即可。 或者python

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    int main(void) {
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ii++) {
            __int128 n;
            read(n);
            __int128 ans = n*(n-1)/2;
            write(ans,'
    ');
        }
        return 0;
    }
    


    I. 字典序 (Nowcoder 5758 I)

    题目大意

    给定一个(n)个数的数组(A),记(S_i)表示删去第(i)个数(从(1)开始)后的数组,现对这(n)个数组按照字典序从小到大排序,问最后这些数组的是多少。

    解题思路

    对这关于(1-n)的全排列快排即可,我们考虑比较函数。

    设两个位置(i,j),我们考虑如何得知分别删去(i,j)后的两个数组的字典序大小。

    (i<j)

    那么对于(cur<i)位置的数,两个数组是相等的。

    对于(cur>j)位置的数,两个数组也是相等的。

    我们要比较的就是(A[i+1...j])(A[i...j-1])谁大谁小。

    很显然我们可以依次比较,但这会超时。

    注意到这里其实比较的都是当前位置与后一个位置的数的大小关系。

    而我们要找的就是从第(i)位起第一个不是相等的位置,从这个位置就可以判断两者的大小。

    预处理即可。

    (sign[i])表示第(i)位和第(i+1)位的大小关系,相等为(0),大于为(1),小于为(-1)

    (nxt[i])表示从第(i)位起第一个(sign[i])不为(0)的位置。

    如果(i>j)的返回值与其相反。

    (其实做法和之前北大美团杯的交互题非常类似)

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N=1e5+8;
    
    int n;
    
    int a[N];
    
    int ans[N];
    
    int sign[N];
    
    int nxt[N];
    
    bool cmp(int x,int y){
        int mi=x<y?x:y;
        int ma=x+y-mi;
        int cur=nxt[mi];
        int si=x>y;
        if (cur>=ma) return (1^si);
        else if (sign[cur]==-1) return (0^si);
        else return (1^si);
    }
    
    int main(void) {
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ii++) {
            read(n);
            for(int i=1;i<=n;++i){
                read(a[i]);
                ans[i]=i;
            }
            for(int i=1;i<n;++i){
                if (a[i]>a[i+1]) sign[i]=1;
                else if (a[i]==a[i+1]) sign[i]=0;
                else sign[i]=-1;
            }
            int cur=n+8;
            for(int i=n-1;i>0;--i){
                if (sign[i]!=0) cur=i;
                nxt[i]=cur;
            }
            sort(ans+1,ans+1+n,cmp);
            for(int i=1;i<=n;++i){
                printf("%d%c",ans[i],i==n?'
    ':' ');
            }
        }
        return 0;
    }
    


    J. 最大值 (Nowcoder 5758 J)

    题目大意

    给定一个字符串(a),求该字符串长度最大的非前缀子串,使得它恰好为(a)的前缀。

    解题思路

    注意到结果对于长度具有单调性,二分长度(l),拿长度为(l)的前缀进行(KMP)匹配即可。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N = 1e5+8;
    
    char s[N],ss[N];
    
    int nxt[N],len;
    
    char tmp[N];
    
    void mk(){
        nxt[0]=0;
        int k=0;
        for(int i=1;i<len-1;++i){
            while(k>0&&ss[i]!=ss[k]) k=nxt[k-1];
            if (ss[k]==ss[i]) ++k;
            nxt[i]=k;
        }
    }
    
    bool check(int l){
        for(int i=0;i<l;++i){
            tmp[i]=s[i];
        }
        int q=0;
        for(int i=0;i<len-1;++i){
            while(q>0&&ss[i]!=tmp[q]) q=nxt[q-1];
            if (tmp[q]==ss[i]) ++q;
            if (q==l) return true;
        }
        return false;
    }
    
    int main(void) {
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ii++) {
            scanf("%s",s);
            int l=0;
            int r=strlen(s);
            len=r;
            for(int j=0;j<len-1;++j) ss[j]=s[j+1];
            mk();
            while(l+1<r){
                int mid=(l+r)>>1;
                if (check(mid)) l=mid;
                else r=mid;
            }
            write(l,'
    ');
        }
        return 0;
    }
    


  • 相关阅读:
    【bzoj4152】[AMPPZ2014]The Captain 堆优化Dijkstra
    【bzoj4547】Hdu5171 小奇的集合 矩阵乘法
    【bzoj1264】[AHOI2006]基因匹配Match 树状数组
    【bzoj3856】Monster 乱搞
    【bzoj4724】[POI2017]Podzielno 二分
    【bzoj4976】宝石镶嵌 乱搞+dp
    【bzoj4070】[Apio2015]雅加达的摩天楼 set+堆优化Dijkstra
    【bzoj4627】[BeiJing2016]回转寿司 离散化+树状数组
    【bzoj2124】等差子序列 STL-bitset
    【bzoj1996】[Hnoi2010]chorus 合唱队 区间dp
  • 原文地址:https://www.cnblogs.com/Lanly/p/13021499.html
Copyright © 2020-2023  润新知