模拟挂成\(10\)分??ios
每次更新答案的时候位置搞错了。算法
想到了多是线段树动态开点,但没写出来,由于标记下传不会。。。数组
理解错了题目含义。学习
选出的\(m\)个物品中,至少要有\(k\)个是\(A\)喜欢的,至少\(k\)个是\(B\)喜欢的优化
那么很显然只要知足了上面的限制条件,俩人都不喜欢的也能选。。。spa
但考场上没想到这层code
就凉了blog
正解变骗分,\(15\)分排序
搞完上面两个题目以后没剩多少时间,就随便扔了个东西上去。。图片
也不知道写的是个啥
/* * 第一眼感受是线段树 * 想了想,感受线段树太浪费了,bitset应该能够搞 * 可是。。。看了数据以后 * 长度为10^18的01序列 * 这。。作个头啊作。 * * 第三种操做是0变1,1变0 * * 对于n,m<=1000的数据,能够模拟 * 对于只有1操做的状况,考虑维护全0区间和全1区间的分界点 * 对于只有1,2操做的状况,由于是强制变成0或1,因此一样只考虑分界点 * 对于n<=10^5的状况,线段树应该能够?可是不会维护。 */ #include <cstdio> #include <algorithm> inline long long read() { long long n=0;int w=1;register char c=getchar(); while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0' && c<='9')n=n*10+c-'0',c=getchar(); return n*w; } const int M=1e5+1,qwq[]={0,0,1}; int a[100001],ty[M]; long long l[M],r[M]; std::pair<int,int> pr[M]; int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int m,flag=0;long long n=0; m=read(); for(int i=1;i<=m;++i) { ty[i]=read(),l[i]=read(),r[i]=read(); n=std::max(n,r[i]); flag=std::max(flag,ty[i]); } if(flag==1) { int id=1,cnt=0; for(int i=1;i<=m;++i) { if(l[i]<=id && r[i]>=id) { int x=r[i]+1; for(int j=1;j<i;++j) if(l[j]<=x && r[j]>=x) x=r[j]+1; id=x; } printf("%d\n",id); } } else if(n<=100000) { int id=1; for(int x,i=1;i<=m;++i) { if(ty[i]!=3) { x=(ty[i]==1?1:0); for(int j=l[i];j<=r[i];++j) a[j]=x; } else for(int j=l[i];j<=r[i];++j) a[j]^=1; if(l[i]<=id && id<=r[i]) { for(;l[i]<=r[i];++l[i]) if(!a[l[i]]) { id=l[i]; break; } } printf("%d\n",id); } } else while(m--) puts("1"); fclose(stdin),fclose(stdout); return 0; }
/* * 分红两人都喜欢的,只有A喜欢的,只有B喜欢的 * 而后枚举从两我的都喜欢的物品里面选k个最便宜的 * 而后贪心选剩下的部分 * * 大样例跑不出来。。 */ #include <cstdio> #include <algorithm> inline int read() { int n=0,w=1;register char c=getchar(); while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9')n=n*10+c-'0',c=getchar(); return n*w; } const int N=2e5+1; int n,m,k,a[N],b[N],v[N],both[N]; /* int aaa,bbb; void dfs(int now,int step,int aa,int bb) { if(aa>k || bb>k)return ; if(step==m) { if(aa==k && bb==k) ++ans; return ; } if(aaa==now) { ++aaa; if(bbb==now) { ++bbb; dfs(now+1,step+1,aa+1,bb+1); dfs(now+1,step,aa,bb); --bbb; } else { dfs(now+1,step+1,aa+1,bb); dfs(now+1,step,aa,bb); } --aaa; } else if(bb==now) { ++bbb; dfs(now+1,step+1,aa,bb+1); dfs(now+1,step,aa,bb); --bbb; } } */ inline bool cmp(const int &x,const int &y) {return v[x]<v[y];} int main() { freopen("b.in","r",stdin); freopen("b.out","w",stdout); n=read(),m=read(),k=read(); for(int i=1;i<=n;++i) v[i]=read(); int aa=read(),bb; for(int i=1;i<=aa;++i) a[i]=read(); bb=read(); for(int i=1;i<=bb;++i) b[i]=read(); std::sort(a+1,a+1+aa); std::sort(b+1,b+1+bb); //哪些俩人都喜欢 int aaa=1,bbb=1,emp=0; while(aaa<=aa && bbb<=bb) { if(a[aaa]>b[bbb]) ++bbb; else if(a[aaa]<b[bbb]) ++aaa; else { both[++emp]=a[aaa]; ++aaa,++bbb; } } /* for(int i=1;i<=emp;++i) printf("%d ",both[i]); */ // std::sort(both+1,both+1+emp,cmp); long long ans=0; for(int i=1;i<=k;++i) ans+=v[both[i]]; if(emp>k) {//先把都喜欢的减掉 m-=k; k=0; } else { m-=emp; k-=emp; } //判断剩下的是否是够 if(m<(k<<1)) { printf("-1"); fclose(stdin);fclose(stdout); return 0; } int cnt=1;emp=0; for(int i=1;i<=aa;++i) { if(a[i]!=both[cnt]) a[++emp]=a[i]; else ++cnt; } aa=emp; cnt=1,emp=0; for(int i=1;i<=bb;++i) { if(b[i]!=both[cnt]) b[++emp]=b[i]; else ++cnt; } bb=emp; if(k>aa || k>bb) { printf("-1"); fclose(stdin);fclose(stdout); return 0; } /* for(int i=1;i<=aa;++i) printf("%d ",a[i]); puts(""); for(int i=1;i<=bb;++i) printf("%d ",b[i]); */ std::sort(a+1,a+1+aa,cmp); std::sort(b+1,b+1+bb,cmp); //而后贪心选剩下的 for(int i=1;i<=k;++i) ans+=v[a[i]]+v[b[i]]; int i=k+1,j=k+1; while(i+j<m) { if(v[a[i]]>v[b[j]]) ans+=v[b[j++]]; else ans+=v[a[i++]]; } printf("%lld",ans); fclose(stdin);fclose(stdout); return 0; }
#include<cstdio> #include<iostream> #include<fstream> #include<cmath> #include<cstring> #include<algorithm> using namespace std; #define Set(a) memset(a,0,sizeof(a)) #define F(i,a,b) for(int i=a;i<=b;++i) #define UF(i,a,b) for(int i=a;i>=b;--i) #define openf(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout) typedef long long LL; typedef long long ll; typedef unsigned long long ULL; typedef unsigned long long ull; const int maxn=400+10; const int maxm=5e4+10; ll n,m; ll u[maxm],v[maxm]; bool used[maxn],a[maxn][maxn]; ll f[maxn],xx; ll tt; void check() { xx=0; Set(f); F(i,1,n) if(!used[i]) f[++xx]=i; F(i,1,xx) F(j,1,i) if(f[i]!=f[j]&&!a[f[i]][f[j]]) {a[f[i]][f[j]]=1;++tt;} } void dfs(int step) { if(step==m+1) check(); ll u1=u[step],v1=v[step]; if(!used[u1]&&!used[v1]){used[u1]=1;dfs(step+1);used[u1]=0;used[v1]=1;dfs(step+1);used[v1]=0;} if( used[u1]&&!used[v1]){used[v1]=1;dfs(step+1);used[v1]=0;} if(!used[u1]&& used[v1]){used[u1]=1;dfs(step+1);used[u1]=0;} if( used[u1]&& used[v1])dfs(step+1); return; } int main() { openf(c); scanf("%d%d",&n,&m); F(i,1,m) scanf("%d%d",&u[i],&v[i]); Set(used); Set(a); dfs(1); printf("%lld",tt); return 0; }
离散化线段树
对每一个区间,维护一个\(tag\),维护的是最左边的\(0\)和最左边的\(1\)的位置
那么\(1\)操做就是把这个区间的\(tag\)变成区间左端点
而后考虑标记下传
\(1\)操做\(+\)任意操做仍是\(1\)操做
\(2\)操做\(+\)任意操做仍是\(2\)操做
\(3\)操做\(+1\)操做变成\(2\)操做
\(3\)操做\(+2\)操做变成\(1\)操做
\(3\)操做\(+3\)操做变成没有操做
而后直接离散化+线段树就好了,不须要动态开点
把全部的物品分红:两人都喜欢的,\(A\)喜欢的,\(B\)喜欢的,都不喜欢的
枚举两人都喜欢的物品选了多少个(假设是\(x\)),那么再在\(A\)喜欢的物品中和\(B\)喜欢的点钟至少都还要选\(k-x\)个(用贪心的思路想,这里显然要选权值前\(k-x\)小的那些),而后若是仍是没有选够\(m\)个,就在两人都不喜欢的物品中选最小的那些直到知足条件
那么从小到大枚举\(x\),不停求和就好了
对这个问题,考虑一个更通常的问题:假设\(f_k(S)=f_{k-1}(S\cup \{v_i\})\)表示\(k\)我的来过以后,\(S\)集合内的苹果都还存活的几率是否\(\gt 0\),讨论获得三种状况
那么如今将全部的\(f_m(\{p\})=1\)都求出来,顺便求出\(g(p)\)是从\(f_m(\{p\})\)运行上面算法获得的最终集合
观察获得\((u,v)\)合法的条件就是\(f_m(\{u\})=1,f_m(\{v\})=1,g(u)\cap g(v)=\varnothing\)
直接枚举,便可获得答案
给定一棵\(n\)个节点的树,你能够进行\(n−1\)次操做,每次操做步骤以下:
求一个操做序列使得\(ans\)最大。
解:
先把直径拿出来,将直径外的点一个一个的和直径中的某一个端点配对并删掉,最后再将直径删掉。
证实:
若是当前直径外已经没有点了,那么显然只能将直径的端点删掉;不然必定不会去删直径的端点。
由于先删一个直径外的点再删直径端点必定是不劣于先删直径端点再删这个直径外的点的。
好比有这样三个点\(x,y,z\),\(x,z\)是该树直径的两端,
假设删除顺序是\(x,y\),那么\(ans+=dis[x][z]+dis[y][z]\)
假设删除顺序是\(y,x\),那么\(ans+=\max(dis[x][y],dis[y][z])+dis[x][z]\)
给定一张\(n\)个点\(m\)条边的带权无向联通图,\(q\)次询问,每次询问\(u_i\)到\(v_i\)的最短路长度
\(n,q\le 10^5,m-n\le 20\)
解:
注意到\(m-n\le 20\),那么这张图其实就是一个树多了屈指可数的几条非树边
先随便搞一棵生成树,那么会有几条边不在这个生成树上,标记那些有非树边链接的点
那么被标记的点最多\(40\)个,先跑\(40\)遍单源最短路求出这些被标记的点到其他全部点的最短路
对于一个询问,若是最短路(跑\(LCA\)来求)只通过生成树上的边,那么直接计算;若通过了被标记的点,就查看走这个点链接的非树边是否会对答案形成影响,尝试更新就好了
给定一棵\(n\)个节点的树,定义叶子是度数为\(1\)的节点,点集\(S\)是好的若是知足任意两个\(S\)中的点之间的距离\(\le k\),如今要求将全部叶子分红若干不相交的好的集合,请问最少分红多少个好的集合。
\(n,k\le 10^6\)
解:
讲师说这类题目通常均可以贪心,我也不知道是哪类题目。。。(话说树上题目有什么分类的么orz)
先随便选一个根,方便起见选一个不是叶子的根,而后能够自底向上贪心。
设\(f_p\)表示\(p\)为根的子树内已完成的好的集合的数量,\(g_p\)表示未完成的集合中离\(p\)最远的叶子的距离。
考虑如何计算\(f_p\)和\(g_p\)。
首先将全部儿子的\(f\)先求和,而后将全部儿子的\(g\)排序,检查 一下最大的\(g\)和次大的\(g\)相加是否大于\(k\),若是大于,那么就将最大的\(g\)删掉,并把\(f_p\)加上\(1\)。不然就令\(g_p\)等于最大的\(g+1\)。
给定\(n\)个节点,每一个节点上有一个数字,要求用这些节点构建一棵二叉搜索树,知足有边相邻的两个节点上的数字互质
\(n\le 600\)
解:
先排序,令\(f[i][j][k]\)表示第\(i\)个点到第\(j\)个点以\(k\)为根是否可行
转移的时候首先检查是否存在\(u\in [i,k-1]\)知足\(f[i][k-1][u]=1\)且\(u\)上的数字跟\(k\)上的数字互质
右侧同理,检查是否存在\(v\in [k+1,j]\)知足\(f[k+1][j][v]=1\)且\(v\)上的数字跟\(k\)上的数字互质
可是这样是\(O(n^4)\)的(数组是\(n^3\)的,转移是\(n\)的)
考虑优化:
注意到上面的作法有一些重复运算。因而设\(L[i][j]\)表示是否存在\(k\in [i,j]\)知足\(f[i][j][k]=1\)且\(k\)上的数字和\(j+1\)上的数字互质,相似地定义\(R[i][j]\)