总结
今天的 (T2) 算是比较套路的一道题
不知道为什么就去想回滚莫队了
感觉最近的几场考试考场上思考的深度变浅了
要及时调整状态
A.划分序列
分析
对于 (a[i] geq 0) 和 (a[i] leq 0) 的点
可以二分的时候分别判断一下最小段数、最大段数和 (k) 的关系
对于全部的数据,答案显然是单调的
所以仍然是二分答案
利用数状数组维护后缀 (max) 和 后缀 (min)
分别求出能在当前的限制下能划分的最大段数和最小段数
如果 (k) 在这个区间里返回合法,否则不合法
找到最小的一个合法的就是答案
代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e5+5;
int n,a[maxn],k,sum[maxn],sta[maxn],tp,wz[maxn];
int tr1[maxn],tr2[maxn];
int lb(rg int xx){
return xx&-xx;
}
int cx1(rg int wz){
rg int nans=-0x3f3f3f3f;
for(rg int i=wz;i<=n;i+=lb(i)){
nans=std::max(nans,tr1[i]);
}
return nans;
}
void xg1(rg int wz,rg int val){
for(rg int i=wz;i>0;i-=lb(i)){
tr1[i]=std::max(tr1[i],val);
}
}
int cx2(rg int wz){
rg int nans=0x3f3f3f3f;
for(rg int i=wz;i<=n;i+=lb(i)){
nans=std::min(nans,tr2[i]);
}
return nans;
}
void xg2(rg int wz,rg int val){
for(rg int i=wz;i>0;i-=lb(i)){
tr2[i]=std::min(tr2[i],val);
}
}
void init(){
for(rg int i=0;i<=n;i++){
tr1[i]=-0x3f3f3f3f;
tr2[i]=0x3f3f3f3f;
}
}
int getwz(rg int val){
return std::lower_bound(sta+1,sta+1+tp,val)-sta;
}
bool jud(rg int val){
init();
xg1(getwz(0),0),xg2(getwz(0),0);
for(rg int i=1;i<n;i++){
xg1(wz[i],cx1(getwz(sum[i]-val))+1);
xg2(wz[i],cx2(getwz(sum[i]-val))+1);
}
rg int tmp1=cx1(getwz(sum[n]-val))+1;
rg int tmp2=cx2(getwz(sum[n]-val))+1;
return tmp2<=k && tmp1>=k;
}
int main(){
n=read(),k=read();
for(rg int i=1;i<=n;i++) a[i]=read();
for(rg int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
for(rg int i=0;i<=n;i++) sta[++tp]=sum[i];
std::sort(sta+1,sta+1+tp);
tp=std::unique(sta+1,sta+1+tp)-sta-1;
for(rg int i=0;i<=n;i++) wz[i]=std::lower_bound(sta+1,sta+tp+1,sum[i])-sta;
rg int l=-0x7f7f7f7f,r=0x7f7f7f7f,mids;
while(l<=r){
mids=(l+r)>>1;
if(jud(mids)) r=mids-1;
else l=mids+1;
}
printf("%d
",l);
return 0;
}
B.Ring
分析
对于每一个左端点 (i) 维护 (mmax[i]) 表示以 (i) 为左端点,(mmax[i]) 为右端点形成的区间恰好有环,并且 (mmax[i]) 是最小的合法的值
这个东西显然是单调的
双指针扫一下用 (lct) 维护加边断边就行了
代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e6+5;
int n,m,q,x[maxn],y[maxn],mmax[maxn];
struct LCT{
int ch[maxn][2],fa[maxn],rev[maxn],sta[maxn],tp;
void push_down(rg int da){
if(!rev[da]) return;
rg int lc=ch[da][0],rc=ch[da][1];
rev[lc]^=1,rev[rc]^=1,rev[da]^=1;
std::swap(ch[da][0],ch[da][1]);
}
bool isroot(rg int da){
return ch[fa[da]][0]!=da && ch[fa[da]][1]!=da;
}
void xuanzh(rg int x){
rg int y=fa[x];
rg int z=fa[y];
rg int k=(ch[y][1]==x);
if(!isroot(y)) ch[z][ch[z][1]==y]=x;
fa[x]=z;
ch[y][k]=ch[x][k^1];
fa[ch[x][k^1]]=y;
ch[x][k^1]=y;
fa[y]=x;
}
void splay(rg int x){
sta[tp=1]=x;
for(rg int i=x;!isroot(i);i=fa[i]) sta[++tp]=fa[i];
for(rg int i=tp;i>=1;i--) push_down(sta[i]);
while(!isroot(x)){
rg int y=fa[x];
rg int z=fa[y];
if(!isroot(y)) (ch[z][1]==y)^(ch[y][1]==x)?xuanzh(x):xuanzh(y);
xuanzh(x);
}
}
void access(rg int x){
for(rg int y=0;x;y=x,x=fa[x]){
splay(x);
ch[x][1]=y;
}
}
void makeroot(rg int x){
access(x);
splay(x);
rev[x]^=1;
push_down(x);
}
int findroot(rg int x){
access(x);
splay(x);
push_down(x);
while(ch[x][0]){
x=ch[x][0];
push_down(x);
}
splay(x);
return x;
}
void split(rg int x,rg int y){
makeroot(x);
access(y);
splay(y);
}
void link(rg int x,rg int y){
makeroot(x);
fa[x]=y;
}
void cut(rg int x,rg int y){
split(x,y);
fa[x]=ch[y][0]=0;
}
}lct;
int main(){
n=read(),m=read(),q=read();
for(rg int i=1;i<=m;i++) x[i]=read(),y[i]=read();
for(rg int i=1;i<=m;i++) mmax[i]=m+1;
rg int now=1;
for(rg int i=1;i<=m;i++){
while(now<=m && lct.findroot(x[now])!=lct.findroot(y[now])){
lct.link(x[now],y[now]);
now++;
}
mmax[i]=now;
lct.cut(x[i],y[i]);
}
rg int aa,bb;
for(rg int i=1;i<=q;i++){
aa=read(),bb=read();
if(bb>=mmax[aa]) printf(">_<
");
else printf("QAQ
");
}
return 0;
}
C.Match
分析
答案是一个次数比较低的多项式
打出前几项之后用高斯消元求一下系数就行了
代码
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e2+5,mod=1e9+7;
inline int mulmod(rg long long now1,rg int now2){
return now1*=now2,now1>=mod?now1%mod:now1;
}
inline int addmod(rg int now1,rg int now2){
return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
return now1-=now2,now1<0?now1+mod:now1;
}
int ksm(rg int ds,rg int zs){
rg int nans=1;
while(zs){
if(zs&1) nans=mulmod(nans,ds);
ds=mulmod(ds,ds);
zs>>=1;
}
return nans;
}
int a[maxn][maxn],n,ans[maxn],totans;
void gsxy(){
for(rg int i=1;i<=n;i++){
rg int xs=ksm(a[i][i],mod-2);
for(rg int j=i;j<=n+1;j++){
a[i][j]=mulmod(a[i][j],xs);
}
for(rg int j=i+1;j<=n;j++){
xs=a[j][i];
for(rg int k=i;k<=n+1;k++){
a[j][k]=delmod(a[j][k],mulmod(xs,a[i][k]));
}
}
}
ans[n]=a[n][n+1];
for(rg int i=n-1;i>=1;i--){
ans[i]=a[i][n+1];
for(rg int j=i+1;j<=n;j++){
ans[i]=delmod(ans[i],mulmod(ans[j],a[i][j]));
}
}
}
int main(){
n=4;
for(rg int i=1;i<=4;i++){
a[i][1]=1;
a[i][2]=i;
a[i][3]=ksm(i,2);
a[i][4]=ksm(i,3);
}
a[1][5]=0;
a[2][5]=mulmod(333333336,2);
a[3][5]=mulmod(207407410,3);
a[4][5]=mulmod(800000008,4);
a[5][5]=mulmod(746666676,5);
gsxy();
n=read();
for(rg int i=1;i<=4;i++){
totans=addmod(totans,mulmod(ans[i],ksm(n,i-1)));
}
totans=mulmod(totans,ksm(n,mod-2));
printf("%d
",totans);
return 0;
}