模拟赛 2026

模拟赛 2026

新一年新气象,戒 P 从我做起!

1.2 NOIP 模拟赛 二五

97+100+0+0=197 pts

HHHOJ 爆炸硬控我 3h,导致 T3 无法上交。最后让 zzx 帮我交了一发。

T1

正解 cout<<0; 是个啥?

T2

lg-P4551

经典题,没什么好讲的。

T3

初始有一个大小为 \(n\) 的整数集合 \(\{a_1,a_2,\dots,a_n\}\)

规定在集合中的元素 \(x,y\) 之间建立起一个双向联系的花费为 \(|x−y|\),并定义一个集合 \(S\) 的代价为\(S\) 中每个元素都与至少一个其他元素建立联系的最小花费总和。(特殊地,对于大小小于 \(2\) 的集合,代价为 \(0\)

现在有 \(q\) 次操作,每次向集合内添加一个元素或从集合中删去一个元素,要求在每次操作后求出当前集合的代价。

对于 \(100\%\) 的数据,\(1\le n,q\le 2\times 10^5,0\le a_i,x\le 10^9\)

先考虑不修改怎么做。

可以先将集合 \(a\) 从小到大排序,会发现对于每一个元素都是将其与相邻的两个元素其中 \(1\) 个或 \(2\) 个建立联系时最优。

考虑 dp,设 \(f_{i,0/1}\) 表示前 \(i\) 个数,除了 \(i\) 之外已经建立联系,\(i\) 是否与前一个数建立联系。有 \(f_{i,0}=f_{i-1,1},f_{i,1}=\min(f_{i-1,0},f_{i-1,1})+|a_i-a_{i-1}|\)

若加上修改,考虑用权值线段树维护。

先离散化,需要考虑如何合并两个区间。

由于中间的数肯定是已经建立联系的,只需要考虑一个区间 \(P\) 在集合中的数的左右的值,为 \(ls_p,rs_p\)。设为 \(f_{p,0/1,0/1}\)\(0/1\) 的定义同上。

pre(p,i,j)=min({
	pre(p<<1,i,0)+pre(p<<1|1,0,j)+ct(rs(p<<1),ls(p<<1|1)),
	pre(p<<1,i,0)+pre(p<<1|1,1,j)+ct(rs(p<<1),ls(p<<1|1)),
	pre(p<<1,i,1)+pre(p<<1|1,0,j)+ct(rs(p<<1),ls(p<<1|1)),
	pre(p<<1,i,1)+pre(p<<1|1,1,j)
});

\(pre\) 表示 \(f\)\(p<<1,p<<1|1\) 分别代表左右节点,\(ct(x,y)\) 表示离散化后为 \(x,y\) 的值建立联系的代价。

在注意分讨一下特殊情况就行了。

Code

点击查看代码
#define ll long long
#define MC(x,y) memcpy(x,y,sizeof(x))
const int N=4e5+20,M=1e5+20;
const ll INF=1ll<<60,mod=998244353;
namespace H_H{
    int n,m,tot,a[N],v[N];
    struct qry{
        int op,x;
    }q[N];
    bool vis[N];
    struct Tree{
        int l,r,ls,rs;
        ll pre[2][2];
        #define l(p) t[p].l
        #define r(p) t[p].r
        #define ls(p) t[p].ls
        #define rs(p) t[p].rs
        #define pre(p,x,y) t[p].pre[x][y]
    }t[N<<2];
    inline ll ct(int x,int y){
        return abs(v[x]-v[y]);
    }
    inline void up(int p){
		//无左右节点 
        while(!ls(p<<1) && !ls(p<<1|1)) return ls(p)=rs(p)=0,pre(p,0,0)=0,pre(p,0,1)=pre(p,1,0)=pre(p,1,1)=INF,void();
        //无左节点
		while(!ls(p<<1)) return ls(p)=ls(p<<1|1),rs(p)=rs(p<<1|1),MC(t[p].pre,t[p<<1|1].pre),void();
        //无右节点
		while(!ls(p<<1|1)) return ls(p)=ls(p<<1),rs(p)=rs(p<<1),MC(t[p].pre,t[p<<1].pre),void();
        //左右节点都只有 1 个节点
		while(ls(p<<1)==rs(p<<1) && ls(p<<1|1)==rs(p<<1|1)){
            ls(p)=ls(p<<1);rs(p)=rs(p<<1|1);
            pre(p,0,0)=0,pre(p,0,1)=pre(p,1,0)=INF,pre(p,1,1)=ct(ls(p),rs(p));
            return ;
        }
		//左只有一个
        while(ls(p<<1)==rs(p<<1)){
            ls(p)=ls(p<<1),rs(p)=rs(p<<1|1);
            pre(p,0,0)=pre(p<<1|1,1,0);
            pre(p,1,0)=min({pre(p<<1|1,1,0),pre(p<<1|1,0,0)})+ct(rs(p<<1),ls(p<<1|1));
            pre(p,0,1)=pre(p<<1|1,1,1);
            pre(p,1,1)=min({pre(p<<1|1,1,1),pre(p<<1|1,0,1)})+ct(rs(p<<1),ls(p<<1|1));
            return ;
        }
		//右只有一个
        while(ls(p<<1|1)==rs(p<<1|1)){
            ls(p)=ls(p<<1),rs(p)=rs(p<<1|1);
            pre(p,0,0)=pre(p<<1,0,1);
            pre(p,0,1)=min({pre(p<<1,0,1),pre(p<<1,0,0)})+ct(rs(p<<1),ls(p<<1|1));
            pre(p,1,0)=pre(p<<1,1,1);
            pre(p,1,1)=min({pre(p<<1,1,1),pre(p<<1,1,0)})+ct(rs(p<<1),ls(p<<1|1));
            return ;
        }
		//一般
        for(int i=0;i<=1;i++){
            for(int j=0;j<=1;j++){
                pre(p,i,j)=min({pre(p<<1,i,0)+pre(p<<1|1,0,j)+ct(rs(p<<1),ls(p<<1|1)),
                                pre(p<<1,i,0)+pre(p<<1|1,1,j)+ct(rs(p<<1),ls(p<<1|1)),
                                pre(p<<1,i,1)+pre(p<<1|1,0,j)+ct(rs(p<<1),ls(p<<1|1)),
                                pre(p<<1,i,1)+pre(p<<1|1,1,j)});
            }
        }
        ls(p)=ls(p<<1),rs(p)=rs(p<<1|1);
    }
    void Build(int p=1,int l=1,int r=tot){
        l(p)=l,r(p)=r;
        if(l==r){
            pre(p,0,0)=0;
            pre(p,0,1)=pre(p,1,0)=pre(p,1,1)=INF;
            return ;
        }
        int mid=(l+r)>>1;
        Build(p<<1,l,mid);Build(p<<1|1,mid+1,r);
        up(p);
    }
    void update(int p,int op,int d){
        if(l(p)==r(p)){
            if(op==1) ls(p)=rs(p)=d;
            if(op==2) ls(p)=rs(p)=0;
            return ;
        }
        int mid=(l(p)+r(p))>>1;
        if(d<=mid) update(p<<1,op,d);
        else update(p<<1|1,op,d);
        up(p);
    }
    int main(){
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            v[++tot]=a[i];
        }
        sort(a+1,a+1+n);
        for(int i=1;i<=m;i++){
            cin>>q[i].op>>q[i].x;
            v[++tot]=q[i].x;
        }
        sort(v+1,v+1+tot);
        tot=unique(v+1,v+1+tot)-v-1;
        for(int i=1;i<=n;i++){
            a[i]=lower_bound(v+1,v+1+tot,a[i])-v;
        }
        for(int i=1;i<=m;i++){
            q[i].x=lower_bound(v+1,v+1+tot,q[i].x)-v;
        }
        Build();
        for(int i=1;i<=n;i++) update(1,1,a[i]);
        int tt=n;
        for(int i=1;i<=m;i++){
            update(1,q[i].op,q[i].x);
            if(q[i].op==2) tt--;
            else tt++;
            if(tt<2) cout<<0<<"\n";
            else cout<<t[1].pre[1][1]<<"\n";
        }
        return 0;
    }
}

T4

AT_agc004_f [AGC004F] Namori

神仙题。

首先 \(n\) 为奇数肯定无解。

先考虑 \(m=n-1\) 的情况。

可以先将其黑白染色,将其转化为每次交换黑白点,使每个节点的黑白点互换。

对于以 \(i\) 为跟的子树,设其子树内(包括它自己)的黑白点个数分别是 \(cw_i,cb_i\)。则对于 \(i\)\(fa_i\) 的边,至少要交换 \(|cw_i-cb_i|=c_i\) 次,将内部所有多余的子交换出去才行。可以证明存在这种方案满足条件。

此时答案为 \(\sum\limits_{i=1}^n |c_i|\)

\(m=n\) 时,图是一个基环树,将其按奇偶环分类讨论。

  1. 偶环:此时任然可以奇偶染色,此时考虑多出来的边 \(A,B\) 对答案的贡献。
    对于这条边,设其交换了 \(x\) 次,设为 \(A\rightarrow B\)(反过来则 \(x<0\)),对于 \(B\) 不经过 \(A\) 到环顶端的路径,贡献为 \(|c_i+x|=|-c_i-x|\),对于 \(A\) 不经过 \(B\) 到环顶端的边,贡献为 \(|c_i-x|\)
    可以将这些 \(c_i\)\(-c_i\) 看作数轴上的点,要求一个 \(x\) 使其到这些点的距离之和最小,当 \(x\)\(c\) 的中位数时最小。

  2. 奇环:此时会存在一条环上的边的两点颜色相同,需要考虑交换这两个点的贡献。
    当交换这两个点时,若这两个点颜色相同,会使这两个点颜色同时取反,当两种颜色数量不同且为偶数时可以将这两个点交换来改变数量,操作数为 \(\frac{|\sum cw-\sum cb|}{2}\),之后就同偶环一样处理。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) ((x)&(-x))
using namespace std;
const int N=1e5+20,M=1e5+20;
const ll INF=1ll<<60,mod=998244353;
namespace H_H{
	int n,m,col[N],w[N],k[N],num[N],tot,s,A,B;
	vector<int> to[N];
	bool odd;
	void dfs0(int u,int fa){
		s+=col[u];
		for(int v:to[u]){
			if(v==fa) continue;
			if(col[v]){
				odd=col[v]==col[u];
				A=u,B=v;
			}
			else col[v]=-col[u],dfs0(v,u);
		}
	}
	void dfs1(int u,int fa){
		w[u]+=col[u];
		for(int v:to[u]){
			if(v==fa || (A==u && B==v) || (A==v && B==u)) continue;
			dfs1(v,u);
			k[u]+=k[v];w[u]+=w[v];
		}
	}
	int main(){
		cin>>n>>m;
		for(int i=1,u,v;i<=m;i++){
			cin>>u>>v;
			to[u].pb(v);
			to[v].pb(u);
		}
		if(n&1) cout<<-1<<"\n",exit(0);
		ll ans=0;
		col[1]=1;dfs0(1,0);
		if(m==n-1){
			if(s) cout<<-1<<"\n",exit(0);
		}
		else{
			if(odd){
				// if(s&1) cout<<-1<<"\n",exit(0);
				ans+=abs(s>>1);
				w[A]-=s>>1,w[B]-=s>>1;
			}
			else{
				if(s) cout<<-1<<"\n",exit(0);
				k[A]=1,k[B]=-1;
			}
		}
		dfs1(1,0);
		for(int i=1;i<=n;i++){
			if(k[i]) num[++tot]=k[i]*w[i];
			else ans+=abs(w[i]);
		}
		num[++tot]=0;
		sort(num+1,num+1+tot);
		int tp=num[(tot+1)>>1];
		for(int i=1;i<=tot;i++) ans+=abs(num[i]-tp);
		cout<<ans<<"\n";
		return 0;
	}
}
int main(){
	IOS;H_H::main();
	return 0;
}

1.7 NOIP 模拟赛 二六

T1

你有一个环。在环上随机挑选 \(n\) 个点,求存在一个大小为 \(\frac{2\pi}{k}\) 的弧能覆盖所有点的概率。

考虑固定一个点,要求剩下 \(n-1\) 满足条件的概率是 \(\frac{1}{k^{n-1}}\),有 \(n\) 个点,所以概率为 \(\frac{n}{k^{n-1}}\)

T2

你有一个大小为 \(n\) 的环,节点标号为 \(1\sim n\)

标号为 \(x\) 的点的权值为 \(f(x)=\sum_{i=1}^x\sum_{j=1}^x[(i\times j)| x]\)\(|\) 表示整除),求环上所有点的权值之和。

\(n\le 10^{12}\)

甚至赛时并没有做出来。

答案可以转化为 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^n \lfloor \frac{n}{i\times j} \rfloor\),相当于求 \((i,j,k)\) 的个数使其满足 \(i\times j\times k\le n\),直接求就好了。复杂度 \(n^{\frac{2}{3}}\)

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) ((x)&(-x))
using namespace std;
const int N=2e5+20,M=1e5+20;
const ll INF=1ll<<60,mod=998244353;
namespace H_H{
	ll n;
	void solve(){
		cin>>n;
		ll ans=0;
		for(ll i=1;i*i*i<=n;i++){
			for(ll j=i;j*j<=n/i;j++){
				ll k=n/i/j;
				if(i^j) ans+=6ll*(k-j)+3;
				else ans+=3ll*(k-j)+1;
			}
		}
		cout<<ans<<"\n";
	}
	int main(){
		int T=1;
		// cin>>T;
		while(T--) solve();
		return 0;
	}
}
int main(){	
	IOS;H_H::main();
	return 0;
}

T3

你有一个大小为 \(n\) 的环,环上的元素分为 \(0/1\) 两种。

现已知有 \(A\) 个元素旁边存在 \(0\)(即与它相邻的两个元素中有至少一个为 \(0\)),有 \(B\) 个元素旁边存在 \(1\)(即与它相邻的两个元素中有至少一个为 \(1\))。

要求构造一组可能的情况,或判断无解。\(n\le 10^5\)

lg-P7032

赛时因为细节问题并没有过这题。

思维含量不大,但细节死多,全是分讨。。。

对于其中一个点,它左右两边的值有 \(4\) 种情况,为 \(00,01,10,11\),设其个数分别为 \(x_1,x_2,x_3,x_4\),可以列出方程:

\[\begin{cases} x_1+x_2+x_3=A\\ x_2+x_3+x_4=B\\ x_1+x_2+x_3+x_4=n \end{cases} \]

解得:

\[\begin{cases} x_1=n-B\\ x_2+x_3=A+B-n\\ x_4=n-A \end{cases} \]

\(x_1<0\)\(x_2+x_3<0\)\(x_4<0\) 时无解。

会发现这些数相邻的数是无法造成贡献的,只会隔一位造成贡献。因此可以将能互相造成贡献的数拎出来,相当于构造一个 \(01\) 串,满足子串为 \(00,01,10,11\) 的个数分别是 \(x_1,x_2,x_3,x_4\) 个。

现在要考虑能互相造成贡献的数所处的位置。当 \(n\) 为奇数是,假设 \(n=5\),情况如下:

模拟赛 2026-1

此时序列代表的位置是 \([1,3,5,2,4,1]\)

\(n\) 为偶数,则会形成两个序列。假设 \(n=6\),同理 序列为 \([1,3,5,1]\)\([2,4,6,2]\)

会发现对于构造的序列的首尾必须一样。接下来考虑如何构造这个序列。

其实就是 这道题

一开始的自然的想法是枚举 \(x_2,x_3\) 分别的个数。显然复杂度不对。

假设构造的序列长度为 \(len\)

可以将构造转化成将序列末尾加数,对应个数分别是 \(x_1,x_2,x_3,x_4\)

会发现 \(x_2,x_3\) 可以是看作是 \(0,1\) 的一个「转变」,即当加了若干个 \(0\) 之后要加 \(1\) 或是 \(1\) 后面要加 \(0\),贡献在 \(x_2,x_3\) 上。

由于序列首尾相同,所以「转变」的次数必须为偶数次,即 \(x_2+x_3\) 为偶数。

可以考虑一种构造方案,假设序列为 \(A\),令 \(A_1=0\),在后面接 \(x_1\)\(0\),在「转变」一次,在接 \(x_4\)\(1\),最后一直「转变」即可。

void solve(int len,int num_1,int num_3){//长度为 len+1,num_1 相当于 x_1,num_3 相当于 x_4。
	for(int i=1;i<=num_1+1;i++) a[i]=0;
	for(int i=1;i<=num_3+1;i++) a[i+num_1+1]=1;
	for(int i=num_1+num_3+3;i<=len+1;i++) a[i]=a[i-1]^1;
	if(a[1]!=a[len+1]) cout<<"NIE\n",exit(0);//特判不满足首尾相同的条件,因为可能会出现 num_1=num_3=0 的情况。
}

对与 \(n\) 为奇数时是容易构造的,但对于 \(n\) 为偶数时需要构造两个序列,因此需要将 \(x_1,x_4\) 分到两个序列中。

\(x_2+x_3=0\)\(x_2+x_3=2\) 的情况特判掉,此时需要考虑 \(x_2+x_3>2\) 的情况。

一种方法是将 \(x_2+x_3\) 平分到两个序列中(若 \(\frac{x_2+x_3}{2}\) 为奇数则各加减 \(1\)),在第一个序列中为 \(mid\) 个。枚举 \(x_1\) 在序列一的个数为 \(tp_1\) 个,要满足 \(\frac{n}{2}-(tp1+mid)\le x_4\)

最后将答案与序列一一对应即可。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) ((x)&(-x))
using namespace std;
const int N=1e5+20,M=1e5+20;
const ll INF=1ll<<60,mod=998244353;
namespace H_H{
	int a[N];
	void solve(int len,int num_1,int num_3){//构造序列
		for(int i=1;i<=num_1+1;i++) a[i]=0;
		a[num_1+2]=1;
		for(int i=1;i<=num_3;i++) a[i+num_1+2]=1;
		for(int i=num_1+num_3+3;i<=len+1;i++) a[i]=a[i-1]^1;
		if(a[1]!=a[len+1]) cout<<"NIE\n",exit(0);
	}
	int n,A,B,ans[N];
	int main(){
		cin>>n>>A>>B;
		if(n==2){//特判 n=2
			if(A==2 && B==0) cout<<"TAK\n",cout<<"00\n",exit(0);
			if(A==1 && B==1) cout<<"TAK\n",cout<<"01\n",exit(0);
			if(A==0 && B==2) cout<<"TAK\n",cout<<"11\n",exit(0);
			cout<<"NIE"<<"\n";
			return 0;
		}
		int num_1=n-B,num_2=A+B-n,num_3=n-A;//num_1 对应 x_1,num_2 对应 x_2+x_3,num_3 对应 x_4。
		if(num_1<0 || num_2<0 || num_3<0 || num_2&1) cout<<"NIE\n",exit(0);//无解
		if(n&1){//n 为奇数
			if(!num_2){//特判 num_2 为 0 的情况
				if(num_1 && num_3) cout<<"NIE\n",exit(0);
				if(num_1) fill(ans+1,ans+1+n,0);
				else fill(ans+1,ans+1+n,1);
			}
			else{
				solve(n,num_1,num_3);
				int tot=0;
				for(int i=1;i<=n;i+=2) ans[i]=a[++tot];
				for(int i=2;i<=n;i+=2) ans[i]=a[++tot];
			}
		}
		else{
			if(!num_2){
				if(!num_3) fill(ans+1,ans+1+n,0);
				else if(!num_1) fill(ans+1,ans+1+n,1);
				else if(num_1!=n/2 && num_3!=n/2) cout<<"NIE\n",exit(0);
				else for(int i=1;i<=n;i+=2) ans[i]=0,ans[i+1]=1;
			}
			else if(num_2==2){//特判
				if(num_1>=n/2){
					num_1-=n/2;
					for(int i=1;i<=n;i+=2) ans[i]=0;
				}
				else if(num_3>=n/2){
					num_3-=n/2;
					for(int i=1;i<=n;i+=2) ans[i]=1;
				}
				else cout<<"NIE\n",exit(0);
				solve(n/2,num_1,num_3);
				for(int i=2,j=0;i<=n;i+=2) ans[i]=a[++j];
			}
			else{
				int tp1=0,tp2=n/2,mid=max(2,n/2-(num_1+num_3));
				if(mid&1) mid++;
				if(mid>num_2) mid-=2;
				tp2-=mid;
				while(num_3<n/2-(tp1+mid)) tp1++,tp2--;
				solve(n/2,tp1,tp2);
				for(int i=1,j=0;i<=n;i+=2) ans[i]=a[++j];
				solve(n/2,num_1-tp1,num_3-tp2);
				for(int i=2,j=0;i<=n;i+=2) ans[i]=a[++j];
			}
		}
		cout<<"TAK"<<"\n";
		for(int i=1;i<=n;i++) cout<<ans[i];
		cout<<"\n";
		return 0;
	}
}
int main(){	
	IOS;H_H::main();
	return 0;
}

T4

AT_agc028_d

赛时并不会做。令 \(n=2N\)

可以考虑每种连通块出现的次数。

将环看作长度为 \(n\) 的序列,那么环上一段区间也对应序列的一段区间。

\(f_{l,r}\) 表示连通块对应在序列上后最左端为 \(l\),最右端为 \(r\) 的内部的方案数。满足 \([l,r]\) 内部的点一定不会往外连。

若不考虑 \(l,r\) 在同一个连通块的限制。则 \(f_{l,r}=g(c_{l,r})\),其中 \(c_{l,r}\) 表示 \([l,r]\) 没有被连边的点的个数,\(g(x)\) 表示 \(x\) 个点连边方案数,\(x\) 为奇数时为 \(0\),否则为 \(1\times 3 \times \dots (x-1)\)

若要满足 \(l,r\) 在同一个连通块,可以用容斥来计算。

\(f_{l,r}=g(c_{l,r})-\sum\limits_{k=l}^{r-1} f_{l,k}\times g(c_{k+1,r})\)

最后答案为 \(\sum f_{l,r}\times g(n - 2 K - c_{l,r})\)

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) ((x)&(-x))
using namespace std;
const int N=600+20,M=1e5+20;
const ll INF=1ll<<60,mod=1e9+7;
namespace H_H{
	int n,k,pos[N],c[N];
	ll f[N][N],g[N];
	int get(int l,int r){
		if(l>r) return 0;
		return c[r]-c[l-1];
	}
	int main(){
		cin>>n>>k;
		n<<=1;
		for(int i=1,x,y;i<=k;i++){
			cin>>x>>y;
			pos[x]=y,pos[y]=x;
		}
		g[0]=1;
		for(int i=2;i<=n;i+=2) g[i]=g[i-2]*(i-1)%mod;
		for(int i=1;i<=n;i++) c[i]=c[i-1]+(!pos[i]);
		for(int l=1;l<=n;l++){
			for(int r=l;r<=n;r++){
				if((r-l+1)&1 || get(l,r)&1) continue;
				bool flag=1;
				for(int i=l;i<=r;i++){
					if(pos[i] && (pos[i]<l || pos[i]>r)){
						flag=0;
						break;
					}
				}
				if(!flag) continue;
				f[l][r]=g[get(l,r)];
				for(int i=l;i<r;i++){
					f[l][r]=(f[l][r]-f[l][i]*g[get(i+1,r)]%mod+mod)%mod;
				}
			}
		}
		ll ans=0;
		for(int l=1;l<=n;l++){
			for(int r=l;r<=n;r++){
				ans=(ans+f[l][r]*g[n-2*k-get(l,r)]%mod)%mod;
			}
		}
		cout<<ans<<"\n";
		return 0;
	}
}
int main(){
	freopen("1.in","r",stdin);
	freopen("1.out","w",stdout);
	IOS;H_H::main();
	return 0;
}

1.8 NOIP 模拟赛 二七

这场比赛没打,让我补一下。

T1

lg-P1971 弱化版

莫队也可以过。经典题没什么好讲的。

T2

对一个排列 \(a\),给出 \(m\) 组有序数对 \((x_i,y_i)\),问是否存在一种顺序重新排列这几组数对,使得能依次交换 \(a_{x_i},a_{y_i}\) 来排序。

\(m\) 不是任意交换两数排序中次数的最小值,也输出 NO

\(n\le 2\times 10^5,m\le 4\times 10^5\)

CF1682E

没做,让我先补一下。

T3

lg-P4647

T4

2.7 NOIP 模拟赛 二八

T1

lg-P9525

难度还好,但感觉有点难实现。

相当于要找存在至少两个以上是最大值的,将其删去。可以拿指针或 set 维护。

Code

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define PLL pair<ll,ll>
#define PII pair<int,int>
//#define Tp template<typename T>
//#define Ts template<typename T,typename ...args>
#define lb(x) ((x)&(-x))
using namespace std;
const int N=2e5+20,M=1e5+20;
const ll INF=1ll<<60,mod=998244353;
namespace H_H{
	struct node{
		int a,b,c;
		inline void input(){cin>>a>>b>>c;}
	}a[N];
	struct node2{
		PII p;int id;
		node2(){p={0,0};id=0;}
		node2(int _a,int _b,int _id){p={_a,_b},id=_id;}
		bool operator <  (const node2&xx) const {return p<xx.p; }
		bool operator == (const node2&xx) const {return p==xx.p;}
		bool operator >  (const node2&xx) const {return p>xx.p; }
	};
	bool vis[N];
	multiset<node2> s[4];
	multiset<int> q[4];
	int n;
	void erase(int id){
		if(vis[id]) return ;
		vis[id]=1;
		q[1].erase(q[1].find(a[id].a));
		q[2].erase(q[2].find(a[id].b));
		q[3].erase(q[3].find(a[id].c));
	}
	bool Del(auto &ss,int A,int B){
		auto bg=ss.lower_bound(node2(A,B,0));
		auto ed=ss.upper_bound(node2(A,B,0));
		for(auto i=bg;i!=ed;i++){
			node2 tp=*i;erase(tp.id);
		}
		if(bg==ed) return 0;
		ss.erase(bg,ed);
		return 1;
	}
	int main(){
		cin>>n;
		for(int i=1;i<=n;i++){
			a[i].input();
			q[1].insert(a[i].a);q[2].insert(a[i].b);q[3].insert(a[i].c);
			s[1].insert(node2(a[i].a,a[i].b,i));
			s[2].insert(node2(a[i].a,a[i].c,i));
			s[3].insert(node2(a[i].b,a[i].c,i));
		}
		bool flag=1;
		while(flag && !q[1].empty() && !q[2].empty() && !q[3].empty()){
			int A=*prev(q[1].end()),B=*prev(q[2].end()),C=*prev(q[3].end());
			flag=Del(s[1],A,B)|Del(s[2],A,C)|Del(s[3],B,C);
		}
		if(q[1].empty() || q[2].empty() || q[3].empty()) cout<<-1<<"\n",exit(0);
		int A=*prev(q[1].end()),B=*prev(q[2].end()),C=*prev(q[3].end());
		cout<<(A+B+C)<<"\n";
		return 0;
	}
}
int main(){	
	IOS;H_H::main();
	return 0;
}

T2

ARC133C

首先当每个数都是 \(k-1\) 时和最大,但是可能不符合限制。考虑如何修改使其满足限制。

对每一行考虑,当全部填 \(k-1\) 时的值为 \(s=(k-1)\times m \% k\),那么我们需要将其选择几个数减小使其与 \(s\) 相同。

若对于某一行 \(i\)。若 \(A_i\le s\),那么就将其减少 \(s-A_i\),否则减少 \(s+k-A_i\)

每一列同理。

按这种方法可能出现行与列减小的值不同。若相差为 \(k\) 的倍数,则存在一种方法满足条件,否则无解。

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define lb(x) ((x)&(-x))
using namespace std;
const int N=2e5+20,M=1e5+20;
const ll INF=1ll<<60,mod=998244353;
namespace H_H{
	int n,m,k;
	int a[N],b[N];
	int main(){
		cin>>n>>m>>k;
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=m;i++) cin>>b[i];
		ll r=1ll*m*(k-1)%k,c=1ll*n*(k-1)%k;
		ll sr=1ll*n*r,sc=1ll*m*c;
		for(int i=1;i<=n;i++){
			sr-=a[i];
			if(r-a[i]<0) sr+=k;
		}
		for(int i=1;i<=m;i++){
			sc-=b[i];
			if(c-b[i]<0) sc+=k;
		}
		if((sr-sc)%k!=0) cout<<-1<<"\n",exit(0);
		cout<<(1ll*(k-1)*n*m-max(sc,sr))<<"\n";
		return 0;
	}
}
int main(){	
	IOS;H_H::main();
	return 0;
}

T3

QOJ-1825

为啥 NOIP 模拟赛会出网络流???

先求出图的最小生成树,那么保留的边一定在这个最小生成树上。

可以建出 Kruskal 重构树,非叶节点的点代表树上的一条边,点的权值就是边的权值,设为 \(v_i\)。对于这个点,若它的左右子树不存在骑士,那么这条边就要被保留。否则删去。

可以考虑网络流建图。源点向每个骑士连 \((1,0)\) 的边,\(1\) 代表流量,\(0\) 代表费用。每个骑士向他可以派去的地方对应的叶节点连一条边 \((1,0)\)。非叶节点向父亲连一条 \((1,-v_i)\) 的边,同时再连一条 \((1,0)\) 的边。树的根节点向汇点连 \((1,-\inf)\) 的边,表示这条边必须走。记得在答案中将这条边的值加回来。

点击查看代码
#include<bits/stdc++.h>
#define IOS cin.tie(0),cout.tie(0),ios::sync_with_stdio(0)
#define ll long long
#define db double
#define pb push_back
#define eb emplace_back
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define PLL pair<ll,ll>
#define PII pair<int,int>
//#define Tp template<typename T>
//#define Ts template<typename T,typename ...args>
#define lb(x) ((x)&(-x))
using namespace std;
const int N=1e5+20,M=1e6+20;
const ll INF=1ll<<60,mod=998244353;
ll Mx;
namespace SSP{
	struct edge{int to,nxt;ll w,c;}e[M];
	int head[N],cnt=1;
	void add(int u,int v,ll w,ll c){
		e[++cnt]={v,head[u],w,c};
		head[u]=cnt;
	}
	void con(int u,int v,ll w,ll c){
		add(u,v,w,c);
		add(v,u,0,-c);
	}
	int s,t,Ns[N];
	ll cost,d[N];
	bool vis[N];
	bool spfa(){
		MS(d,0x3f);MS(vis,0);MC(Ns,head);
		Mx=d[1];
		queue<int> q;
		q.push(s);vis[s]=1;d[s]=0;
		bool flag=0;
		while(!q.empty()){
			int u=q.front();q.pop();
			vis[u]=0;
			for(int i=head[u];i;i=e[i].nxt){
				int v=e[i].to;ll w=e[i].w;
				if(!w || d[v]<=d[u]+e[i].c) continue;
				d[v]=d[u]+e[i].c;
				if(!vis[v]) q.push(v),vis[v]=1;
				if(v==t) flag=1;
			}
		}
		return flag;
	}
	ll dfs(int u,ll Sum){
		if(u==t) return Sum;
		ll pus=0,k;
		vis[u]=1;
		for(int i=Ns[u];i;i=e[i].nxt){
			Ns[u]=i;
			int v=e[i].to;ll w=e[i].w;
			if(vis[v] || !w || d[v]!=d[u]+e[i].c) continue;
			k=dfs(v,min(w,Sum));
			if(!k) d[v]=INF;
			pus+=k,Sum-=k;e[i].w-=k,e[i^1].w+=k;cost+=k*e[i].c;
			if(!Sum) break;
		}
		vis[u]=0;
		return pus;
	}
	PLL calc(){
		ll flow=0,x=0;cost=0;
		while(spfa()) while((x=dfs(s,INF))) flow+=x;
		return {flow,cost};
	}
}
namespace H_H{
	int n,m,k;
    int ff[N],val[N];
	bool vis[N];
	struct edge{
		int u,v,w;
        bool operator < (const edge &xx)const {
            return w<xx.w;
        }
	}e[M];
    int find(int xx){
        if(xx==ff[xx]) return xx;
        return ff[xx]=find(ff[xx]);
    }
	int main(){
		cin>>n>>m>>k;
        iota(ff+1,ff+1+n,1);
        for(int i=1,u,v,w;i<=m;i++){
            cin>>u>>v>>w;
            e[i]={u,v,w};
        }
        sort(e+1,e+1+m);
        ll sum=0;
		int cnt=n;
        for(int i=1;i<=m;i++){
            int fu=find(e[i].u),fv=find(e[i].v);
            if(fu==fv) continue;
            sum+=e[i].w;
            val[++cnt]=e[i].w;
			ff[cnt]=ff[fu]=ff[fv]=cnt;
			SSP::con(fu,cnt,1,0);
			SSP::con(fv,cnt,1,0);
        }
		SSP::s=cnt+k+1,SSP::t=cnt+k+2;
		for(int i=n+1;i<=cnt+1;i++) SSP::con(i,SSP::t,1,-val[i]);
		for(int i=1,num;i<=k;i++){
			cin>>num;
			SSP::con(SSP::s,cnt+i,1,0);
			for(int j=1,x;j<=num;j++){
				cin>>x;vis[x]=1;
				SSP::con(cnt+i,x,1,0);
			}
		}
		for(int i=1;i<=n;i++) vis[find(i)]|=vis[i];
		for(int i=1;i<=cnt;i++){
			if(i==find(i)) SSP::con(i,SSP::t,1,-mod),sum+=mod;
		}
		auto tp=SSP::calc();
		if(sum+tp.second>=mod || tp.first<k) cout<<-1<<"\n",exit(0);
		// cout<<sum<<"\n";
		if(sum+tp.second==1) cout<<-1<<"\n",exit(0);
		cout<<sum+tp.second<<"\n";
		return 0;
	}
}
int main(){	
	IOS;H_H::main();
	return 0;
}

T4

CF1603D

posted @ 2026-01-03 19:10  tyh_27  阅读(14)  评论(0)    收藏  举报