模拟赛7.13-7.18(week2)

7.13

100+20+0+0=120pts,rk31。

这辈子跟 31 过不去了哈。

打得简直就是一坨。

T1

\((a,b),a,b \leq n\) 的对数使得 \(a-b=\gcd(a,b)\)

赛时 10min 写完。

正解

注意到 \(a-b=\gcd(a,b)=\gcd(b,a-b)\),令 \(a-b=t\),考虑枚举 \(t\),于是答案为:

\[\sum_{i=1}^n (\lfloor \frac{n}{i} \rfloor -1) \]

直接整除分块,复杂度 \(O(\sqrt n)\)

T2

原题 lg P8097

正解

赛时已经想到并查集,并且已经知道怎么合并了,但是忘了不用把答案赋值到每个点上,不然复杂度会爆。只需选择性地合并然后把答案赋值到连通块的祖先上(赛时想不到这个我是唐氏啊)。

考虑时光倒流,发现由于连边操作的点权值必须为 \(1\),所以反向操作无需删边,然后并查集维护一下即可。时间复杂度 \(O((n+q)\log n)\),没写按秩合并。

Code

#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int> 
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
	inline int read(){
		int x=0,f=1;
		char ch=gc();
		while(!isdigit(ch)){
			if(ch=='-')	f=-f;
			ch=gc();
		}
		while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
		return x*f;
	}
	inline void write(int x){
		if(x<0) pc('-'),x=-x;
		if(x>9)	write(x/10);
		pc(x%10+'0');
	}
}
using namespace IO;
const int N=1e5+10;
int n,q,fa[N],ans[N],cnt,w[N];
inline void init(){for(rg int i=1;i<=n;i++) fa[i]=i,w[i]=1;}
inline int find(int x){return (x==fa[x])?fa[x]:fa[x]=find(fa[x]);}
struct edge{int u,v,del;}e[N<<1];
struct node{char op;int a,b;}qry[N<<1];
signed main(){
//	freopen("114514.in","r",stdin);
//	freopen("114514.out","w",stdout);
	IOS
	cin>>n>>q;init();
	for(rg int i=1;i<=q;i++){
		cin>>qry[i].op;
		if(qry[i].op=='A'){
			cin>>qry[i].a>>qry[i].b;
			e[++cnt].u=qry[i].a,e[cnt].v=qry[i].b;
		}else if(qry[i].op=='D'){
			cin>>qry[i].a;
			w[qry[i].a]=0;
		}else if(qry[i].op=='R'){
			cin>>qry[i].a;
			e[qry[i].a].del=1;
		}
	}
	for(rg int i=1;i<=cnt;i++){
		if(!e[i].del){
			int u=e[i].u,v=e[i].v;
			int fu=find(u),fv=find(v);
			if(fu==fv) continue;
			if(w[fu]||w[fv]) w[fu]=w[fv]=1,ans[fu]=ans[fu]=q;
			else fa[fu]=fv;
		}
	}
	for(rg int i=1;i<=n;i++) if(w[i]) ans[find(i)]=q;
	for(rg int i=q;i>=1;i--){
		if(qry[i].op=='R'){
			int u=e[qry[i].a].u,v=e[qry[i].a].v;
			int fu=find(u),fv=find(v);
			if(fu==fv) continue;
			if(!w[fu]&&!w[fv]) fa[fu]=fv;
			if(w[fu]&&!w[fv]) w[fv]=1,ans[fv]=i-1;
			if(w[fv]&&!w[fu]) w[fu]=1,ans[fu]=i-1;
		}else if(qry[i].op=='D'){
			int u=find(qry[i].a);
			if(!w[u]) w[u]=1,ans[u]=i-1;
		}
	}
	for(rg int i=1;i<=n;i++) cout<<ans[find(i)]<<'\n';
	return 0;
}

T3

原题 lg P7219。

赛时连部分分都不会。

Code

#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int> 
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
	inline int read(){
		int x=0,f=1;
		char ch=gc();
		while(!isdigit(ch)){
			if(ch=='-')	f=-f;
			ch=gc();
		}
		while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
		return x*f;
	}
	inline void write(int x){
		if(x<0) pc('-'),x=-x;
		if(x>9)	write(x/10);
		pc(x%10+'0');
	}
}
using namespace IO;
const int N=2e5+10;
vector<int> f[N];
vector<PII> v[N];
int n,m,fal[N],far[N],tree[N];
inline int lbt(int x){return x&-x;}
inline void add(int i, int x){while(i<=n){tree[i]+=x;i+=lbt(i);}}
inline int query(int i){int ans=0; while(i>0){ans+=tree[i];i-=lbt(i);} return ans;}
inline int findl(int x){return (fal[x]==x)?fal[x]:fal[x]=findl(fal[x]);}
inline int findr(int x){return (far[x]==x)?far[x]:far[x]=findr(far[x]);}
signed main(){
//	freopen("114514.in","r",stdin);
//	freopen("114514.out","w",stdout);
	n=read();
	for(rg int i=1;i<=n;i++){
		int x=read();
		f[x].push_back(i);
	}
	for(rg int i=0;i<=n+1;i++) fal[i]=far[i]=i;
	m=read();
	for(rg int i=1;i<=m;i++){
		int x=read(),y=read(),c=read();
		v[y].push_back({x,c});
	}
	int ans=0;
	for(rg int i=1;i<=n;i++){
		for(auto j:v[i]){
			int cost=query(j.fi);
			if(j.se<=cost) ans+=j.se;
			else ans+=cost,add(findl(j.fi)+1,j.se-cost),add(findr(j.fi),cost-j.se);
		}
		for(int j:f[i]) fal[findl(j)]=findl(j-1),far[findr(j)]=findr(j+1);
	}
	write(ans);
	return 0;
}

T4

原题 CF2002H

神秘 dp 套 dp,很抽象,本人并不会。

7.14

100+10+10+0=120pts,rk19。

T1

给定一个序列,将其分成若干个区间,要求区间最小值在区间左端点,最大值在右端点,求最少能分成几段。

赛时 10min 想出做法,写了个分治发现是正确的但是会被极端数据卡,于是考虑 RMQ 结果调了1h 没调出来,浪费了很多时间。

正解

考虑贪心,设区间 min 的最左端出现位置为 \(l\),区间 max 最右端出现位置为 \(r\),如果 \(l<r\) 则取 \([l,r]\) 为一段更优,剩下两段继续分治;否则考虑分成三段分治。区间最值如果线性求,数据足够随机确实是 \(O(n \log n)\),但是存在单调递减的极端数据,所以可以考虑用 ST 表预处理,分治是 \(O(\log n)\)\(O(n)\) 之间,预处理 \(O(n \log n)\)

cmh 给的题解里有 \(O(n)\) 的 dp 做法,需要用到单调栈,此外还有许多大佬有不同的 \(O(n \log n)\) 做法,T1 直接过了一车。

Code

#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int> 
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
	inline int read(){
		int x=0,f=1;
		char ch=gc();
		while(!isdigit(ch)){
			if(ch=='-')	f=-f;
			ch=gc();
		}
		while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
		return x*f;
	}
	inline void write(int x){
		if(x<0) pc('-'),x=-x;
		if(x>9)	write(x/10);
		pc(x%10+'0');
	}
}
using namespace IO;
const int N=3e5+10;
int n,b[N],lg[N];
PII a[N];
PII fmx[N][21],fmi[N][21];
//vector<int> p[N];
bool cmp(PII x, PII y){
	if(x.fi!=y.fi) return x.fi<y.fi;
	return x.se<y.se;
}
PII cmax(PII x, PII y){return (cmp(x,y)?y:x);}
PII cmin(PII x, PII y){return (cmp(x,y)?x:y);}
inline void init(){
	lg[1]=0;
	for(rg int i=2;i<N;i++) lg[i]=lg[i>>1]+1;
	for(rg int i=1;i<=n;i++) fmx[i][0]=fmi[i][0]=a[i];
	for(rg int j=1;j<=lg[n];j++){
		for(rg int i=1;i+(1<<j)<=n+1;i++){
			fmx[i][j]=cmax(fmx[i][j-1],fmx[i+(1<<(j-1))][j-1]);
			fmi[i][j]=cmin(fmi[i][j-1],fmi[i+(1<<(j-1))][j-1]);
		}
	}
}
PII querymx(int l, int r){int k=lg[r-l+1];return cmax(fmx[l][k],fmx[r-(1<<k)+1][k]);}
PII querymi(int l, int r){int k=lg[r-l+1];return cmin(fmi[l][k],fmi[r-(1<<k)+1][k]);}
inline int solve(int l, int r){
	if(r<l) return 0;
	if(l==r) return 1;
	PII p=querymx(l,r),q=querymi(l,r);
	int mxpos=p.se,mipos=q.se;
//	write(mxpos),pc(' '),write(mipos),puts("");
	if(mipos<mxpos) return solve(l,mipos-1)+solve(mxpos+1,r)+1;
	else return solve(l,mxpos)+solve(mxpos+1,mipos-1)+solve(mipos,r);
}
signed main(){
	freopen("divide.in","r",stdin);
	freopen("divide.out","w",stdout);
	n=read();
	for(rg int i=1;i<=n;i++) a[i].fi=read(),a[i].se=i;
	init();
//	write(querymx(1,n).fi);
	write(solve(1,n));
	return 0;
}

T2

给了你一个 \(n × m\) 的网格,网格的每个格子上写有一个字符,保证其为数字(\(1\)\(9\)),
加号(\(+\)),乘号(\(∗\))中的一个。

每次从左上角走到右下角,只能向右或向下,对于每条合法路径,它的权值为走过的表达式的值,求所有路径的权值和,结果模 \(998244353\)

我甚至爆搜都写不来。

赛时写了个没有运算符的部分分。

正解

考虑 dp,具体地我们维护四个数组,分别为 \(cnt,x,y,z\),表示当前点路径数、上一个加号后到上一个乘号前的乘积,上一个加号后到当前点的总乘积,上一个加号前的和。

分别从 \((i-1,j),(i,j-1)\) 转移,若当前为 \(*\)

\[x(i,j) \leftarrow x(i,j)+y(a,b),y(i,j) \leftarrow 0 \]

若当前为 \(+\)

\[x(i,j) \leftarrow cnt(i,j),z(i,j) \leftarrow z(i,j)+y(a,b),y(i,j) \leftarrow 0 \]

若当前为数:

\[y(i,j) \leftarrow y(i,j)+10y(a,b)+ch(i,j)x(a,b),x(i,j) \leftarrow x(i,j)+x(a,b) \]

时间复杂度 \(O(nm)\)

Code

#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int> 
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
	inline int read(){
		int x=0,f=1;
		char ch=gc();
		while(!isdigit(ch)){
			if(ch=='-')	f=-f;
			ch=gc();
		}
		while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
		return x*f;
	}
	inline void write(int x){
		if(x<0) pc('-'),x=-x;
		if(x>9)	write(x/10);
		pc(x%10+'0');
	}
}
using namespace IO;
const int N=2e3+10,mod=998244353;
int n,m,cnt[N][N],x[N][N],y[N][N],z[N][N];
char ch[N][N];
inline void solve(int a, int b, int c, int d){
	z[c][d]=(z[c][d]+z[a][b])%mod;
	if(ch[c][d]=='+'){
		x[c][d]=cnt[c][d]%mod;
		y[c][d]=0;
		z[c][d]=(z[c][d]+y[a][b])%mod;
	}else if(ch[c][d]=='*'){
		x[c][d]=(x[c][d]+y[a][b])%mod;
		y[c][d]=0;
	}else{
		y[c][d]=(y[c][d]+10*y[a][b]%mod+(ch[c][d]-'0')*x[a][b]%mod)%mod;
		x[c][d]=(x[c][d]+x[a][b])%mod;
	}
}
signed main(){
	freopen("gridwalk.in","r",stdin);
	freopen("gridwalk.out","w",stdout);
	IOS
	cin>>n>>m;
	for(rg int i=1;i<=n;i++) for(rg int j=1;j<=m;j++) cin>>ch[i][j];
	x[1][1]=cnt[1][1]=1,y[1][1]=ch[1][1]-'0',z[1][1]=0;
	for(rg int i=1;i<=n;i++){
		for(rg int j=1;j<=m;j++){
			if(i==1&&j==1) continue;
			cnt[i][j]=(cnt[i-1][j]+cnt[i][j-1])%mod;
			if(i==1) solve(i,j-1,i,j);
			else if(j==1) solve(i-1,j,i,j);
			else solve(i-1,j,i,j),solve(i,j-1,i,j);
		}
	}
	cout<<(y[n][m]+z[n][m])%mod;
	return 0;
}

T3

原题 ARC030D

赛时就写了个 10pts 的线段树。

正解是 FHQ 加可持久化等神秘操作,太难了本人并不会。

T4

平面上有 \(2n + 1\) 个点,两个点可以构成一组配对当且仅当它们的横坐标或纵坐标相等。
对于从 \(1\)\(2n + 1\) 的每个 \(i\),询问在去掉第 \(i\) 个点后,剩下 \(2n\) 个点是否能配成 \(n\) 组配对,
满足每个点恰好出现在一组配对中。每次询问间互相独立。

本人并不会,但是拜谢 ljx 巨佬的神秘做法,简单易懂%%%。

7.15

100+0+0+0=100pts,鉴定为废物。

T1 10min 写完又他妈在死磕 T2,但是还是脑子不够,推不出答案,然后红温效应导致暴力也不想写了。

今天讲课是 TJM。

T1

原题 AT_tenka1_2012_final_a

贪心即可,过于简单。

T2

原题 lg P7322

想了很久,但并不知道如何求每个数的贡献。

正解

\([k,n]\) 每个数的贡献,\(i\) 为最大值的区间个数为个数为 \(A_{i-1}^{m-1}m(n-m+1)!\),但是有重复,考虑去重,重复数为 \(A_{i-1}^m(m-1)(n-m)!\)

直接 \(O(n)\) 求和即可。

Code

#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int> 
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
	inline int read(){
		int x=0,f=1;
		char ch=gc();
		while(!isdigit(ch)){
			if(ch=='-')	f=-f;
			ch=gc();
		}
		while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
		return x*f;
	}
	inline void write(int x){
		if(x<0) pc('-'),x=-x;
		if(x>9)	write(x/10);
		pc(x%10+'0');
	}
}
using namespace IO;
const int N=5e5+10;
const int mod=998244353;
int n,m,fac[N],inv[N];
inline int qpow(int x, int y){
	int ans=1;
	while(y>0){
		if(y&1) ans=ans*x%mod;
		x=x*x%mod,y>>=1;
	}
	return ans%mod;
}
inline void init(){
	fac[0]=1;
	for(rg int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
	inv[n]=qpow(fac[n],mod-2);
	for(rg int i=n;i>=1;i--) inv[i-1]=inv[i]*i%mod;
}
signed main(){
//	freopen("114514.in","r",stdin);
//	freopen("114514.out","w",stdout);
	n=read(),m=read();
	init();
	int ans1=0,ans2=0;
	for(rg int i=m;i<=n;i++){
		ans1=(ans1+fac[i-1]%mod*inv[i-m]%mod)%mod;
		ans2=(ans2+fac[i-1]%mod*inv[i-m-1]%mod)%mod;
	}
	ans1=ans1*fac[n-m+1]%mod*m%mod;
	ans2=ans2*fac[n-m]%mod*(m-1)%mod;
	write((ans1-ans2+mod)%mod);
	return 0;
}

T3

神秘数据结构优化 dp。

T4

原题 lg P10675。

tjm 出的。

本人并不会,机房有巨佬场切了%%%%%%。

7.16

100+100+0+0=200pts,rk7。

第一次上 200。

T1T2

过于简单。

T3

初始有 \(1\) 个金币,进行 \(n\) 次赌博,每次下注任意正整数个,一半的概率失去全部赌注,一半概率获得赌注的两倍,可以使用 \(m\) 次技能预测赌局结果并改变,求最优决策下期望获得的最多金币数。

样例都没看懂,我是飞舞。

70pts

拜谢巨佬 ycy(lg:I_LOVE_FG)。

\(f_{i,j}\) 为第 \(i\) 局用了 \(j\) 次技能获得期望最大值,考虑转移,有:

\[f_{i,j} \leftarrow \frac{1}{2} \times 2 f_{i-1,j-1}+\frac{1}{2} \times 2f_{i-1,j} \]

正是杨辉三角,于是 \(f_{i,j}=C_i^j\),答案即为:

\[\sum_{i=0}^m C_n^i \]

时间复杂度 \(O(\sum m)\)

正解是莫队,参考 AT_tenka1_2014_final_d

T4

树上 dp,本人并不会。

7.18

80+10+0+0=90pts,rk114514。

ryh 怎么又 AK 了。

T1

给定非负整数 \(n,m\),你需要求出在平面直角坐标系上从 \((0,0)\) 出发,每步只能进行以下两种操作中的一种:

  • 使 \(x\) 坐标增加 \(1\)
  • 使 \(y\) 坐标增加 \(1\)

你需要求出在不跨过直线 \(x=y\) 的情况下到达 \((n,m)\) 的方案数,对 \(998244353\) 取模。

正解

考虑反射容斥,钦定 \(n>m\),答案为 \(C_{n+m}^n-C_{n+m}^{m-1}\)

注意 \(n=m\) 时要乘个二,都为零时答案为 \(1\)

赛时这两个没特判挂了 20pts,警钟撅烂。

T2

称一个长度为 \(n\) 的序列 \(a_1,\dots,a_n\) 合法当且仅当对于任意 \(i(2\le i\le n-1)\) 都满足以下两个条件中的至少一个:

  • \(\forall 1\le j\le i-1,a_j\le a_i\)
  • \(\forall i+1\le j\le n,a_j\le a_i\)

现在给你一个长度为 \(n\) 的序列 \(a_1,\dots,a_n\),你需要求出至少执行多少次以下操作才能使序列 \(a_1,\dots,a_n\) 合法:

  • 选定 \(i(1\le i\le n-1)\),交换 \(a_i,a_{i+1}\)

赛时想了 3h30min 的逆序对,发现是假的。原因是枚举最大值位置时,存在最大值两边互换更优的情况。一车人错的一样。

正解

考虑最终序列一定是一个单峰序列,考虑对每个数算贡献,其实是 \([1,i]\) 大于 \(a_i\) 的数个数与 \([i+1,n]\) 取 min,直接离散化加权值树状数组维护。

Code

#include<bits/stdc++.h>
#define int long long
#define gc getchar
#define pc putchar
#define rg register
#define LB lower_bound
#define UB upper_bound
#define PII pair<int, int> 
#define PDI pair<double, int>
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using db=double;using ll=long long;
using ull=unsigned long long;
using namespace std;
const ll INF=1e18;
const int inf=0x3f3f3f3f;
namespace IO{
	inline int read(){
		int x=0,f=1;
		char ch=gc();
		while(!isdigit(ch)){
			if(ch=='-')	f=-f;
			ch=gc();
		}
		while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=gc();
		return x*f;
	}
	inline void write(int x){
		if(x<0) pc('-'),x=-x;
		if(x>9)	write(x/10);
		pc(x%10+'0');
	}
}
using namespace IO;
const int N=3e5+10;
inline int lbt(int x){return x&-x;}
int n,a[N],b[N],p[N],q[N];
struct BIT{
	int tree[N];
	inline void clear(){memset(tree,0,sizeof tree);}
	inline void add(int x, int d){while(x<=n){tree[x]+=d,x+=lbt(x);}}
	inline int query(int x){int ans=0;while(x>0){ans+=tree[x],x-=lbt(x);}return ans;}
}T;
inline void solve(){
	for(rg int i=1;i<=n;i++) p[i]=T.query(n)-T.query(a[i]),T.add(a[i],1);
	T.clear();
	for(rg int i=n;i>=1;i--) q[i]=T.query(n)-T.query(a[i]),T.add(a[i],1);
}
signed main(){
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	n=read();
	for(rg int i=1;i<=n;i++) a[i]=read(),b[i]=a[i];
	sort(b+1,b+1+n);
	int tot=unique(b+1,b+1+n)-b-1;
	for(rg int i=1;i<=n;i++) a[i]=LB(b+1,b+1+tot,a[i])-b;
	solve();
	int ans=0;
	for(rg int i=1;i<=n;i++) ans+=min(p[i],q[i]);
	write(ans);
	return 0;
}
/*
7
2 8 4 8 5 3 6
*/

T3

给定一张 \(n\) 个点 \(n\) 条边的有向图,第 \(i(1\le i\le n)\) 条边为 \(i\rightarrow p_i\)

你可以进行如下操作:

  • 选择 \(i(1\le i\le n)\),将 \(p_i\) 修改为 \(\{1,2,\dots,n\}\) 中的任意一个数,代价为 \(c_i\)

你需要进行一系列操作使得最后得到的 \(\forall 1\le i\le n,i\rightarrow p_i\) 的图强连通,并最小化代价之和。

我不会,膜拜 tyh 爆切本题。

T4

原题 lg P12485。

TJM 出的,本人太菜并不会。

7.18

有史以来打的最差的一次,小基班 rk-1,不愧是我,纯废物。

T1

lg P8106

过于简单。

凭啥评绿。

T2

赛时想了个唐人做法,以为是对的,结果未知原因写炸了。

正解

按照奇偶性做,是一个类似括号匹配的东,用栈维护并统计最值。

std

#include<bits/stdc++.h>
#define fir first
#define sec second
#define all(x) begin(x),end(x)
using namespace std;
typedef long long ll;
typedef unsigned uint;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<int,int> pii;
template<typename type>
inline void chmin(type &x,const type &y)
{
	if(y<x)
		x=y;
}
template<typename type>
inline void chmax(type &x,const type &y)
{
	if(x<y)
		x=y;
}
constexpr int Max=1e6+10;
int n,a[Max],s[Max],o=1,ans[Max];
signed main()
{
	freopen("domino.in","r",stdin),freopen("domino.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(nullptr),cout.tie(nullptr);
	auto solve=[&]()->void
	{
		cin>>n;
		int top=0;
		for(int i=1;i<=n;++i)
		{
			cin>>a[i];
			if(top&&(a[i]&1)==(s[top]&1))
			{
				chmax(s[top-1],max(a[i],s[top])+1);
				--top;
			}
			else
				s[++top]=a[i];
			if(top>1)
				ans[i]=-1;
			else
				if(top==1)
					ans[i]=max(s[0]-((s[0]^s[1])&1),s[1]);
				else
					ans[i]=s[0]-1;
		}
		if(o==1)
			cout<<ans[n]<<"\n";
		else
			for(int i=1;i<=n;++i)
				cout<<ans[i]<<" \n"[i==n];
		fill(s,s+n+1,0);
	};
	int t;
	cin>>t>>o;
	while(t--)
		solve();
	return 0;
}

T3

给定非负整数 \(k\),求出长度为 \(n\) 的逆序对数恰好为 \(k\) 的排列的个数。

有个 70pts 的 \(O(nk)\) dp,我直接没写,这是怎么回事呢?

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int f[5010][5010];
int n,k;
int main(){
	scanf("%d%d",&n,&k);
	f[1][0]=1;
	for(int i=2;i<=n;i++){
		for(int j=1;j<=k;j++)	f[i-1][j]=(f[i-1][j]+f[i-1][j-1])%mod;
		for(int j=0;j<i;j++)	f[i][j]=f[i-1][j];
		for(int j=i;j<=k;j++)	f[i][j]=(f[i-1][j]-f[i-1][j-i]+mod)%mod;
	}
	printf("%d\n",f[n][k]);
	return 0;
}

T4

神秘淀粉质。

\[End \]

posted @ 2025-07-20 18:50  bbbzzx  阅读(13)  评论(0)    收藏  举报