COCI ACM —— 线段树+枚举

2024-04-06 23:38
文章标签 枚举 acm 线段 coci

本文主要是介绍COCI ACM —— 线段树+枚举,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

This way

题意:

现在哟n道题目,有三个人对这些题目打分(1~5),现在要将这n道题目划分为3个连续的区间,分配给这三个人去做,每个人的值就是他对于他负责的区间的打分的值的和。问你所有人的值的和最小是多少。

题解:

那么其实这道题的解法很明显,首先每个人有先后顺序,但是只有3个人,所以可以暴力枚举每个人的先后情况。
然后对于当前的情况,我们可以枚举第一个人负责的区间到哪,然后找到后面区间的最优分配方法,那么设后面的每个位置的值是 b i − c i bi-ci bici,然后维护一个前缀和,找到其中的最小值就是第二个人到这是最优的。
然后一开始写的线段树退化了,需要用线段树优化一样去每个位置都维护最小值的位置和值。然后好像要用快读。
然后好像有6n的方法可以去做,但是我懒得想。

#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
namespace Fast_IO { //orz laofuconst int MAXL((1 << 18) + 1); int iof, iotp;char ioif[MAXL], * ioiS, * ioiT, ioof[MAXL], * iooS = ioof, * iooT = ioof + MAXL - 1, ioc, iost[55];char Getchar() {if (ioiS == ioiT) {ioiS = ioif; ioiT = ioiS + fread(ioif, 1, MAXL, stdin); return (ioiS == ioiT ? EOF : *ioiS++);}else return (*ioiS++);}void Write() { fwrite(ioof, 1, iooS - ioof, stdout); iooS = ioof; }void Putchar(char x) { *iooS++ = x; if (iooS == iooT)Write(); }inline int read() {int x = 0; for (iof = 1, ioc = Getchar(); ioc<'0' || ioc>'9';)iof = ioc == '-' ? -1 : 1, ioc = Getchar();for (x = 0; ioc <= '9' && ioc >= '0'; ioc = Getchar())x = (x << 3) + (x << 1) + (ioc ^ 48); return x * iof;}inline long long read_ll() {long long x = 0; for (iof = 1, ioc = Getchar(); ioc<'0' || ioc>'9';)iof = ioc == '-' ? -1 : 1, ioc = Getchar();for (x = 0; ioc <= '9' && ioc >= '0'; ioc = Getchar())x = (x << 3) + (x << 1) + (ioc ^ 48); return x * iof;}template <class Int>void Print(Int x, char ch = '\0') {if (!x)Putchar('0'); if (x < 0)Putchar('-'), x = -x; while (x)iost[++iotp] = x % 10 + '0', x /= 10;while (iotp)Putchar(iost[iotp--]); if (ch)Putchar(ch);}void Getstr(char* s, int& l) {for (ioc = Getchar(); ioc <'a' || ioc>'z';)ioc = Getchar();for (l = 0; ioc <= 'z' && ioc >= 'a'; ioc = Getchar())s[l++] = ioc; s[l] = 0;}void Putstr(const char* s) { for (int i = 0, n = strlen(s); i < n; ++i)Putchar(s[i]); }
} // namespace Fast_IO
using namespace Fast_IO;
const int N=15e4+5;
pa mi[N*4];
int f[N*4],a[5][N],sum[5][N],p[5];
void push_down(int root){if(!f[root])return ;mi[root<<1].second+=f[root];mi[root<<1|1].second+=f[root];f[root<<1]+=f[root];f[root<<1|1]+=f[root];f[root]=0;
}
void build(int l,int r,int root,int p,int v){if(l==r){mi[root]={l,v};return ;}int mid=l+r>>1;if(mid>=p)build(l,mid,root<<1,p,v);elsebuild(mid+1,r,root<<1|1,p,v);if(mi[root<<1].second<mi[root<<1|1].second)mi[root]=mi[root<<1];elsemi[root]=mi[root<<1|1];
}
pa query(int l,int r,int root,int ql,int qr){if(l>=ql&&r<=qr)return mi[root];push_down(root);int mid=l+r>>1;pa ans={0,1e9};if(mid>=ql)ans=query(l,mid,root<<1,ql,qr);if(mid<qr){pa ne=query(mid+1,r,root<<1|1,ql,qr);if(ne.second<ans.second)ans=ne;}return ans;
}
int main()
{int n;n=read();for(int i=1;i<=3;i++)for(int j=1;j<=n;j++)a[i][j]=read(),sum[i][j]=sum[i][j-1]+a[i][j];for(int i=1;i<=3;i++)p[i]=i;int ans=1e9;do{//for(int i=1;i<N*4;i++)//mi[i]=1e9;memset(f,0,sizeof(f));int s=0;for(int i=1;i<=n;i++){build(1,n,1,i,s+a[p[2]][i]-a[p[3]][i]);s+=a[p[2]][i]-a[p[3]][i];}s=0;for(int i=1;i<=n-2;i++){s+=a[p[1]][i];//update(1,n,1,i,n,a[p[3]][i]-a[p[2]][i]);pa mm=query(1,n,1,i+1,n-1);ans=min(ans,s+sum[p[2]][mm.first]-sum[p[2]][i]+sum[p[3]][n]-sum[p[3]][mm.first]);}}while(next_permutation(p+1,p+1+3));printf("%d\n",ans);return 0;
}

这篇关于COCI ACM —— 线段树+枚举的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/881133

相关文章

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

hdu 2489 (dfs枚举 + prim)

题意: 对于一棵顶点和边都有权值的树,使用下面的等式来计算Ratio 给定一个n 个顶点的完全图及它所有顶点和边的权值,找到一个该图含有m 个顶点的子图,并且让这个子图的Ratio 值在所有m 个顶点的树中最小。 解析: 因为数据量不大,先用dfs枚举搭配出m个子节点,算出点和,然后套个prim算出边和,每次比较大小即可。 dfs没有写好,A的老泪纵横。 错在把index在d

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

HDU4737线段树

题目大意:给定一系列数,F(i,j)表示对从ai到aj连续求或运算,(i<=j)求F(i,j)<=m的总数。 const int Max_N = 100008 ;int sum[Max_N<<2] , x[Max_N] ;int n , m ;void push_up(int t){sum[t] = sum[t<<1] | sum[t<<1|1] ;}void upd

zoj 1721 判断2条线段(完全)相交

给出起点,终点,与一些障碍线段。 求起点到终点的最短路。 枚举2点的距离,然后最短路。 2点可达条件:没有线段与这2点所构成的线段(完全)相交。 const double eps = 1e-8 ;double add(double x , double y){if(fabs(x+y) < eps*(fabs(x) + fabs(y))) return 0 ;return x + y ;