2025国庆Day2

模拟赛

T1

简单题

离散化+差分即可

或者直接贪心

对可能成为答案的点计算删的区间并取min

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
int ksm(int a,int b,int p){
	if(b==0) return 1;
	if(b==1) return a%p;
	int c=ksm(a,b/2,p);
	c=c*c%p;
	if(b%2==1) c=c*a%p;
	return c%p;	
}
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int L[200005],R[200005],cnt,cn;
double p[800005],an[400005];
signed main()
{
	freopen("interval.in", "r", stdin);
	freopen("interval.out", "w", stdout);
	int T=read();
	while(T--){
		int n=read();
		cn=0;
		for(int i=1;i<=n;i++){
			L[i]=read();
			R[i]=read();
			an[++cn]=L[i];
			an[++cn]=R[i];
		}
		if(n<=1){
			cout<<-1<<'\n';
			continue;
		}
		sort(L+1,L+n+1);
		sort(R+1,R+n+1);
		sort(an+1,an+cn+1);
		cnt=0;
		for(int i=1;i<=cn;i++){
			p[++cnt]=1.0*an[i];
			if(i<cn) p[++cnt]=(an[i]+an[i+1])/2.0;
		}
		int minn=n;
		for(int i=1;i<=cnt;i++){
			int rsu=lower_bound(R+1,R+n+1,p[i])-R-1;
			int lsu=n-(upper_bound(L+1,L+n+1,p[i])-L-1);
			if(rsu&&lsu){
				minn=min(minn,n-rsu-lsu);
			}
		}
		if(minn>=n) cout<<-1<<'\n';
		else cout<<minn<<'\n';
	}
	return 0;
}

T2

结论题

容易发现一开始答案为2^(n+m-1)

因为只要第一行第一列填满,剩余就全部确定了

具体的,a[i,j]=a[1,1]a[1,j]a[i,1]

将每个点拆成1/0

对限制建边

并查集维护联通块个数即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define jiaa(a,b) {a+=b;if(a>=MOD) a-=MOD;}
#define jian(a,b) {a-=b;if(a<0) a+=MOD;}
using namespace std;
int ksm(int a,int b,int p){
	if(b==0) return 1;
	if(b==1) return a%p;
	int c=ksm(a,b/2,p);
	c=c*c%p;
	if(b%2==1) c=c*a%p;
	return c%p;	
}
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
const int MOD=1e9+7;
int ans,n,m,q,sec[600005];
int fa[600005],val[600005];
int find(int x){
	if(fa[x]==x) return x;
	int ff=find(fa[x]);
	val[x]^=val[fa[x]];
	fa[x]=ff;
	return ff;
}
int merge(int x,int y,int c){
	int fx=find(x);
	int fy=find(y);
	if(fx==fy){
		if((val[x]^val[y])!=c) return 2;
		else return 1;
	} 
	fa[fx]=fy;
	val[fx]=val[y]^val[x]^c;
	return 0;
}
signed main()
{
	freopen("color.in", "r", stdin);
	freopen("color.out", "w", stdout);
	n=read(),m=read(),q=read();
	sec[0]=1;
	for(int i=1;i<=n+m;i++) sec[i]=sec[i-1]*2%MOD;
	ans=n+m-1;
	cout<<sec[ans]<<'\n';
	for(int i=1;i<=n+m;i++) fa[i]=i;
	while(q--){
		int x=read(),y=read(),c=read();
		int zh=merge(x,y+n,c);
		if(zh==2) ans=-1;
		else if(!zh){
			if(ans!=-1) ans--;
		} 
		if(ans==-1) cout<<0<<'\n';
		else cout<<sec[ans]<<'\n';
	}
	return 0;
}

T3

枚举d

注意长度为d的区间是一段前缀和后缀拼起来

枚举前缀,哈希维护相同段出现次数

计算答案时O(1)计算相同段的数量变化值

T4

不会

image

笛卡尔树

CF2048F

对b构建笛卡尔树

每次一定选一棵子树

注意到答案最多是 log2(1e18) ≈ 60

因此可以记 gx,k 表示 x 子树内操作了 k 次,最大值最小是多少

dp即可

树状数组

线段树

并查集

CF1713E

和T2相似

每个点拆成两个点

a[x,y] 要么呆在原地,要么跑到 a[y,x]

假设 x > y

• 若 a[x,y]>a[y,x],显然应该交换,则 k = x 或 k = y 其中之一进行操作

• 若 a[x,y]<a[y,x],显然不应该交换,则 k = x 和 k = y 要么都不操作,要么都操作

若是 a[x,y] > a[y,x],则是 (x0, y1), (x1, y0) 两对点之间连边

否则是 (x0, y0), (x1, y1) 两对点连边

贪心维护是否修改即可

trie

P4551

建二进制trie

贪心

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
using namespace std;
int ksm(int a,int b,int p){
	if(b==0) return 1;
	if(b==1) return a%p;
	int c=ksm(a,b/2,p);
	c=c*c%p;
	if(b%2==1) c=c*a%p;
	return c%p;	
}
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
vector<pair<int,int> > tu[100005];
int ch[3100005][2],tot=1;
int val[100005];
void dfs(int x,int fa){
	for(auto [ed,w]:tu[x]){
		if(ed==fa) continue;
		val[ed]=val[x]^w;
		dfs(ed,x);
	}
}
void ins(int val){
	int son=1;
	for(int i=(1<<30);i;i/=2){
		bool f=(val&i);
		if(!ch[son][f]){
			ch[son][f]=++tot;
		}
		son=ch[son][f];
	}
}
int query(int val){
	int ans=0;
	int son=1;
	for(int i=(1<<30);i;i/=2){
		bool f=(val&i);
		if(ch[son][!f]) {
			ans+=i;
			son=ch[son][!f];
		}
		else son=ch[son][f];
	}
	return ans;
}
signed main(){
	int n=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read(),w=read();
		tu[u].push_back({v,w});
		tu[v].push_back({u,w});
	}
	val[1]=0;
	dfs(1,0);
	for(int i=1;i<=n;i++){
		ins(val[i]);
	}
	int maxx=0;
	for(int i=1;i<=n;i++){
		maxx=max(maxx,query(val[i]));
	}
	cout<<maxx<<'\n';
	return 0;
}

平衡树

数据结构优化DP

CF372C

暴力dp

单调队列优化

P9871

强制断开一天

暴力dp

线段树优化

对于一个区间(l,r,v)

转移到r时,(1,l)的位置+v的权值

前缀加线段树即可

注意n巨大

离散化后dp

数据结构二分

形如查找区间第一个比k大的值

//找到 [x,n] 中第一个比 k 大的位置

//mx[p] 表示最大值
int query(int x,int k,int p=1,int l=1,int r=n) {
	if(mx[p]<=k||r<x)return r+1;
	if(l==r)return l;
	int mid=l+r>>1,res=query(x,k,p*2,l,mid);
	if(res!=mid+1)return res;
	return query(x,k,p*2+1,mid+1,r);
}


//找到前缀和中第一个大于等于 k 的位置(线段树)

//s[p] 表示和
int query(int k,int p=1,int l=1,int r=n) {
	if(s[p]<k)return r+1;// 特判
	if(l==r)return l;
	int mid=l+r>>1;
	if(s[p*2]>=k)return query(k,p*2,l,mid);
	return query(k-s[p*2],p*2+1,mid+1,r);
}


//找到前缀和中第一个大于等于 k 的位置(树状数组)

int query(int k) {
	int x=0;
	for(int i=__lg(n);~i;i--)
		if(x+(1<<i)<=n&&c[x+(1<<i)]<k)x+=1<<i,k-=c[x];
	return x+1;
}

P11217

容易发现最多循环log次

枚举循环次数

对于第一次断开的循环,寻找断开的位置

相当于找前缀和第一个大于某个值的位置

可以使用线段树上二分实现

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define lson x<<1
#define rson x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int a[1000005],sec;
struct node{
	int sum;
	int size;
	int add;
	node(){
		sum=size=add=0;
	}
	void chu(int v){
		sum=v;
		size=1;
	}
}tr[4000005];
node operator+(const node &l,const node &r){
	node ans;
	ans.sum=l.sum+r.sum;
	ans.size=l.size+r.size;
	return ans;
}
void color(int l,int r,int x,int v){
	tr[x].add+=v;
	tr[x].sum+=tr[x].size*v;
}
void pushdown(int l,int r,int x){
	int mid=(l+r)>>1;
	color(l,mid,lson,tr[x].add);
	color(mid+1,r,rson,tr[x].add);
	tr[x].add=0;
}
void build(int l,int r,int x){
	if(l==r){
		tr[x].chu(a[l]);
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,lson);
	build(mid+1,r,rson);
	tr[x]=tr[lson]+tr[rson];
}
int query(int l,int r,int x,int k){
	if(l==r) return l;
	pushdown(l,r,x);
	int mid=(l+r)>>1;
	if(tr[lson].sum*(1ll<<(sec-1))>=k) return query(l,mid,lson,k);
	return query(mid+1,r,rson,k-tr[lson].sum*(1ll<<(sec-1)));
}
void modify(int l,int r,int x,int nl,int nr,int v){
	if(nl<=l&&r<=nr){
		color(l,r,x,v);
		return ;
	}
	pushdown(l,r,x);
	int mid=(l+r)>>1;
	if(nl<=mid) modify(l,mid,lson,nl,nr,v);
	if(mid<nr) modify(mid+1,r,rson,nl,nr,v);
	tr[x]=tr[lson]+tr[rson];
}

signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	int n=read(),m=read(),w=read();
	for(int i=1;i<=n;i++) a[i]=read();
	build(1,n,1);
	while(m--){
		int l=read(),r=read(),k=read();
		modify(1,n,1,l,r,k);
		int nw=0;
		int kk=1;
		for(sec=1;;sec++){
			nw+=tr[1].sum*kk;
			if(nw>=w){
				nw-=tr[1].sum*kk;
				break;
			}
			kk*=2;
		}
		int ans=query(1,n,1,w-nw);
		cout<<ans+(sec-1)*n-1<<'\n';
	}
	return 0;
}

P6619

容易发现随着温度的增加,2类减少,1类增加

所以树状数组二分找出两条曲线的交点

注意温度离散化

扫描线

P1972

设prei表示上一个ci颜色的位置

i能贡献答案当且仅当

prei<i&&l<=i<=r

转化成扫描线查询矩阵内点的个数

P11363

image

image

(k从大到小枚举)

45度线段覆盖

将矩阵用45度线分成两个三角形

对于一条斜线变成横线(x/y,x-y)

扫描线扫x/y轴线段树维护

dfs序

子树加子树求和=区间加区间求和

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<bits/stdc++.h>
#define int long long
#define lson x<<1
#define rson x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int v[1000005],a[1000005],dfn[1000005],cnt,siz[1000005];
struct node{
	int sum;
	int size;
	int add;
	node(){
		sum=size=add=0;
	}
	void chu(int v){
		sum=v;
		size=1;
	}
}tr[4000005];
node operator+(const node &l,const node &r){
	node ans;
	ans.sum=l.sum+r.sum;
	ans.size=l.size+r.size;
	return ans;
}
void color(int l,int r,int x,int v){
	tr[x].add+=v;
	tr[x].sum+=tr[x].size*v;
}
void pushdown(int l,int r,int x){
	int mid=(l+r)>>1;
	color(l,mid,lson,tr[x].add);
	color(mid+1,r,rson,tr[x].add);
	tr[x].add=0;
}
void build(int l,int r,int x){
	if(l==r){
		tr[x].chu(a[l]);
		return ;
	}
	int mid=(l+r)>>1;
	build(l,mid,lson);
	build(mid+1,r,rson);
	tr[x]=tr[lson]+tr[rson];
}
node query(int l,int r,int x,int nl,int nr){
	if(nl<=l&&r<=nr) return tr[x];
	pushdown(l,r,x);
	int mid=(l+r)>>1;
	if(nl<=mid){
		if(mid<nr) return query(l,mid,lson,nl,nr)+query(mid+1,r,rson,nl,nr);
		else return query(l,mid,lson,nl,nr);
	}
	else return query(mid+1,r,rson,nl,nr);
}
void modify(int l,int r,int x,int nl,int nr,int v){
	if(nl<=l&&r<=nr){
		color(l,r,x,v);
		return ;
	}
	pushdown(l,r,x);
	int mid=(l+r)>>1;
	if(nl<=mid) modify(l,mid,lson,nl,nr,v);
	if(mid<nr) modify(mid+1,r,rson,nl,nr,v);
	tr[x]=tr[lson]+tr[rson];
}
vector<int> tu[1000005];
void dfs(int x,int fa){
	dfn[x]=++cnt;
	siz[x]=1;
	for(auto ed:tu[x]){
		if(ed==fa) continue;
		dfs(ed,x);
		siz[x]+=siz[ed];
	}
}
signed main()
{
	//freopen("filename.in", "r", stdin);
	//freopen("filename.out", "w", stdout);
	int n=read(),m=read(),rt=read();
	for(int i=1;i<=n;i++) v[i]=read();
	for(int i=1;i<n;i++){
		int u=read(),v=read();
		tu[u].push_back(v);
		tu[v].push_back(u);
	}
	dfs(rt,0);
	for(int i=1;i<=n;i++){
		a[dfn[i]]=v[i];
	}
	build(1,n,1);
	while(m--){
		int opt=read(),aa=read();
		if(opt==1){
			int val=read();
			modify(1,n,1,dfn[aa],dfn[aa]+siz[aa]-1,val);
		}
		else{
			cout<<query(1,n,1,dfn[aa],dfn[aa]+siz[aa]-1).sum<<'\n';
		}
	}
	return 0;
}

子树加,单点加,路径求和:

维护出du,表示1~u的路径和

单点加,相当于子树加,线段树区间加即可

子树加:

对于每个点y相当于+v*(depy-depx+1)

拆开线段树维护

杂题

推箱子

首先一定先完成t小的箱子

假设要将箱子 i 从 ai 推到 bi(假设 ai < bi),则箱子 j 会被跟着推动的条件是 j > i 且 aj < bi − i + j

若其被推动,则会移动到 bi − i + j

但这并不好维护

限制可转化成aj-j<bi-i

bi-i是定值,aj-j可线段树维护

一次移动相当于给 j ≥ i 的 j 一起对 bi + i 取 max

因为 ai + i 单调递增所以取到的是一个区间

使用线段树二分找到这个区间

找到过后计算出贡献并赋值即可

三目运算符

找规律

发现s=f(s)的串不含101,110

101操作一次即可变成100

110会将后面的一个0变成1

总是先操作101再操作110

所以答案只跟第一个110的位置(i)有关

答案是n-i+1

若只有101答案为1

若都没有答案为0

实现时线段树维护区间前/后两位数,合并时讨论一下

posted @ 2025-10-03 14:50  gbrrain  阅读(7)  评论(0)    收藏  举报