T1 联
$n$最大到$1e18$,根本没法做,但$m$只有$1e5$,发现有很多区间是一起动的,或者根本没动,所以可以把区间离散化掉,然后线段树区间修改,对于第三种修改,只需要把它分解成一段一段相同的区间,再区间覆盖就可以。
在线段树中维护一个$cnt$,表示区间中$0$的个数,在询问的时候,只需要找到最左端$cnt!=0$的地方,在把离散化后的映射回来输出即可。
要注意的一点是,不仅要每个区间的左右端点,也要把区间右端点$+1$的地方也离散化,留下可能是$0$的位置,并且把$1$位置扔进去,不然可能会找不到答案,离散化后是$3×m$个点,数组要开够。。。
#include#include #include
T2 赛
把$n$个物品分成$4$类,甲乙都喜欢,只有甲喜欢,只有乙喜欢,甲乙都不喜欢,并按权值$sort$
枚举选$i$个甲乙都喜欢的,那么就要最小的$k-i$个甲喜欢的,最小的$k-i$个乙喜欢的,然后还不够的就从剩下的没选的里面选$m-k-k+i$个最小的,但显然会超时
把权值离散化,插到一棵权值线段树里,每次查询最小的$m-k-k+i$个元素的和
随着$i$的递推,新加入的点只有两个,但要注意边界问题,因为这个wa95了一晚上
#include#include #include #include #include #include<set> #include
T3 题
$f[i]$表示$i$这个苹果是否可能剩下,$0$表示不可能,$1$可能剩下,初始全为$1$,$g[i][j](bitset)$表示i这个苹果必须有,最终的状态,$1$表示状态已经确定,$0$表示不确定
枚举每个苹果,让他必须活下来,倒序枚举$m$个人,看最后一个人要吃的苹果,
如果$a$,$b$都不确定,那么他们还是都不确定,
如果$a$确定,那么$j$这个人就只能吃$b$,所以$b$也确定了
如果$b$确定,那么$j$这个人就只能吃$a$,所以$a$也确定了
如果$a$,$b$都已确定,那这个状态就推不回去了(后边的人把他确定了,但他在后边的人前边,所以一定是他先吃,状态一定不合法),所以i一定不会活下来
最后再枚举每个苹果,如果他可能剩下,再去找另一个可能剩下的苹果,看他们两个最终状态是不是交集为空(一个苹果只能被一个人吃),为空$ans++$
#include#include #include using namespace std; int n,m,ans,a[50010],b[50010]; bitset<410>g[410],as; bool f[410]; int read() { int aa=0,bb=1;char cc=getchar(); while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();} while(cc>='0'&&cc<='9'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();} return aa*bb; } int main() { n=read();m=read(); for(int i=1;i<=m;i++) a[i]=read(),b[i]=read(); for(int i=1;i<=n;i++) f[i]=1; for(int i=1;i<=n;i++){ g[i][i]=1; for(int j=m;j>=1;j--){ if(g[i][a[j]]&&g[i][b[j]]){f[i]=0;break;} else if(g[i][a[j]]&&!g[i][b[j]]) g[i][b[j]]=1; else if(!g[i][a[j]]&&g[i][b[j]]) g[i][a[j]]=1; } } for(int i=1;i<=n;i++){ if(f[i]){ for(int j=i+1;j<=n;j++){ if(f[j]){ as=g[i]&g[j]; if(!as.count()) ans++; } } } } printf("%d\n",ans); return 0; }
不想联赛就退役就静下来好好想