Codeforces 1197 简要题解

文章目录

    • A题
    • B题
    • C题
    • D题
    • E题
    • F题

比赛传送门

A题

传送门
排个序然后取 a [ n − 1 ] − 1 a[n-1]-1 a[n1]1 n − 2 n-2 n2的最小值。
代码:

#include
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
	#define gc getchar
	int ans=0;
	bool f=1;
	char ch=gc();
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return f?ans:-ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=2e5+5;
int n,a[N];
int main(){
	for(ri tt=read();tt;--tt){
		n=read();
		for(ri i=1;i<=n;++i)a[i]=read();
		sort(a+1,a+n+1);
		cout<<min(n-2,a[n-1]-1)<<'\n';
	}
	return 0;
} 

B题

传送门
随便写个双向链表然后按照题意模拟。
代码:

#include
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
	#define gc getchar
	int ans=0;
	bool f=1;
	char ch=gc();
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return f?ans:-ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=2e5+5;
int n,a[N],pos[N],pre[N],suf[N];
int main(){
	n=read();
	for(ri i=1;i<=n;++i)a[i]=read(),pos[a[i]]=i;
	for(ri i=1;i<=n;++i)pre[a[i]]=a[i-1],suf[a[i]]=a[i+1];
	pre[0]=a[1],suf[n+1]=a[n];
	int t;
	for(t=n;t;--t){
		if(pre[t]!=t-1&&suf[t]!=t-1)break;
		suf[pre[t]]=suf[t];
		pre[suf[t]]=pre[t];
	}
	if(t>1)puts("No");
	else puts("Yes");
	return 0;
} 

C题

传送门
考虑转化问题。
变成求 a i − a i − 1 a_i-a_{i-1} aiai1的前 k − 1 k-1 k1大值,然后用 a n − a 1 a_n-a_1 ana1减去它们的和即可。
代码:

#include
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
	#define gc getchar
	int ans=0;
	bool f=1;
	char ch=gc();
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return f?ans:-ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=3e5+5;
int n,k,a[N];
int main(){
	n=read(),k=read();
	for(ri i=1;i<=n;++i)a[i]=read();
	n=unique(a+1,a+n+1)-a-1;
	if(n<=k)return puts("0"),0;
	int ans=a[n]-a[1];
	for(ri i=2;i<=n;++i)a[i-1]=a[i]-a[i-1];
	sort(a+1,a+n);
	for(ri i=n-1,j=1;j<k;--i,++j)ans-=a[i];
	cout<<ans;
	return 0;
} 

D题

传送门
这题应该可以 O ( n ) O(n) O(n),但博主太懒了于是写了个 O ( n m ) O(nm) O(nm)的,设 f i f_i fi表示区间左端点为 i i i时的最优值。
考虑从右向左枚举这个 i i i
于是每次对于一个左端点 i i i只需要考虑一下两种情况:

  1. ⌈ r − l + 1 m ⌉ = 1 \lceil\frac{r-l+1}{m}\rceil=1 mrl+1=1,直接 O ( m ) O(m) O(m)暴力枚举即可。
  2. ⌈ r − l + 1 m ⌉ > 1 \lceil\frac{r-l+1}{m}\rceil>1 mrl+1>1,那么 i i i ~ i + m − 1 i+m-1 i+m1都被选了,相当于在 i + m i+m i+m ~ n n n中选择 ⌈ r − l + 1 m ⌉ − 1 \lceil\frac{r-l+1}{m}\rceil-1 mrl+11组,那么可以根据 f i + m f_{i+m} fi+m得出最优值, O ( 1 ) O(1) O(1)转移。

两种情况取个最大值即可。

#include
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
	#define gc getchar
	int ans=0;
	bool f=1;
	char ch=gc();
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return f?ans:-ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=3e5+5;
int n,m,k,a[N];
ll f[N],sum[N];
int main(){
	n=read(),m=read(),k=read();
	for(ri i=1;i<=n;++i)a[i]=read(),sum[i]=sum[i-1]+a[i];
	ll ans=0;
	for(ri i=n;i;--i){
		f[i]=0;
		for(ri j=i,up=min(i+m-1,n);j<=up;++j)f[i]=max(f[i],sum[j]-sum[i-1]-k);
		if(i+m-1<n)f[i]=max(f[i],sum[i+m-1]-sum[i-1]-k+f[i+m]);
		ans=max(ans,f[i]);
	}
	cout<<ans;
	return 0;
} 

E题

传送门
离散化后有个边界写错了 w a wa wa了三次。。。
先把左右端点离散化了。
考虑从左往右扫
考虑对于一个区间 [ l i , r i ] [l_i,r_i] [li,ri],只有满足 r j ≤ l i r_j\le l_i rjli的区间才能对它产生影响,且根据题意,我们对每个区间要维护这么一些信息。

  1. 从起始到当前这个区间的总空位数的最小值是多少
  2. 满足从起始到当前这个区间的总空位数等于最小值的方案数是多少

设对于点 p p p,有 k k k种方案在 p p p结束,且总空位数最小值等于 d i s dis dis,那么如果 p p p可以转移到 q q q,总空位数应该变成 d i s + q − p = d i s − p + q dis+q-p=dis-p+q dis+qp=disp+q,由此我们发现如果从一个 r j r_j rj可以转移到一个 l i l_i li,一定满足 d i s r j − r j = min ⁡ r k ≤ l i { d i s r k − r k } dis_{r_j}-r_j=\min_{r_k\le l_i}\{dis_{r_k}-r_k\} disrjrj=minrkli{disrkrk},于是我们在线段树上面维护 d i s p − p dis_{p}-p dispp的最小值与这个最小值的出现次数即可。

代码:

#include
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
	#define gc getchar
	int ans=0;
	bool f=1;
	char ch=gc();
	while(!isdigit(ch))f^=ch=='-',ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return f?ans:-ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int mod=1e9+7;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=2e5+5,M=4e5+5;
int n,mp[M],sig=0;
pii a[N];
struct Node{
	int a,b;
	friend inline Node operator+(const Node&a,const Node&b){
		if(a.a<b.a)return a;
		if(a.a>b.a)return b;
		return (Node){a.a,add(a.b,b.b)};
	}
};
namespace sgt{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (l+r>>1)
	Node T[M<<2];
	inline void build(int p,int l,int r){
		T[p]=(Node){0x3f3f3f3f,0};
		if(l==r)return;
		build(lc,l,mid),build(rc,mid+1,r);
	}
	inline void update(int p,int l,int r,int k,Node v){
		if(l==r){T[p]=T[p]+v;return;}
		k<=mid?update(lc,l,mid,k,v):update(rc,mid+1,r,k,v);
		T[p]=T[lc]+T[rc];
	}
	inline Node query(int p,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr)return T[p];
		if(qr<=mid)return query(lc,l,mid,ql,qr);
		if(ql>mid)return query(rc,mid+1,r,ql,qr);
		return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
	}
	#undef lc
	#undef rc
}
int main(){
	n=read();
	for(ri i=1;i<=n;++i){
		a[i].se=read();
		a[i].fi=read();
		mp[++sig]=a[i].fi;
		mp[++sig]=a[i].se;
	}
	sort(mp+1,mp+sig+1);
	sort(a+1,a+n+1);
	sig=unique(mp+1,mp+sig+1)-mp-1;
	sgt::build(1,1,sig);
	int mx=mp[lower_bound(mp+1,mp+sig+1,a[n].fi)-mp];
	for(ri p1,p2,i=1;i<=n;++i){
		if(a[i].se>mx)continue;
		p1=lower_bound(mp+1,mp+sig+1,a[i].fi)-mp;
		p2=lower_bound(mp+1,mp+sig+1,a[i].se)-mp;
		Node t=sgt::query(1,1,sig,1,p1);
		if(t.a==0x3f3f3f3f)sgt::update(1,1,sig,p2,(Node){a[i].fi-a[i].se,1});
		else sgt::update(1,1,sig,p2,(Node){t.a+a[i].fi-a[i].se,t.b});
	}
	Node ans=(Node){0x3f3f3f3f,0};
	for(ri p1,p2,i=1;i<=n;++i){
		if(a[i].se<=mx)continue;
		p1=lower_bound(mp+1,mp+sig+1,a[i].fi)-mp;
		p2=lower_bound(mp+1,mp+sig+1,a[i].se)-mp;
		Node t=sgt::query(1,1,sig,1,p1);
		if(t.a==0x3f3f3f3f)ans=ans+(Node){t.a,1};
		else ans=ans+(Node){t.a+a[i].fi,t.b};
	}
	cout<<ans.b;
	return 0;
}

F题

传送门
感觉是道我要想很久的题,然而比赛时由于看错题了没做出来,下来看题解突然发现自己看错题。。。
考虑只有一条布袋咋做。
发现由于最多只能走 3 3 3步。我们可以设计状态 f i , 0 / 1 , 0 / 1 , 0 / 1 f_{i,0/1,0/1,0/1} fi,0/1,0/1,0/1表示前 i i i个格子,从最后三个格子出发的 n p np np状态为 0 / 1 , 0 / 1 , 0 / 1 0/1,0/1,0/1 0/1,0/1,0/1的方案数,这样可以根据题目中的限制数组写出状态转移时。
但是由于布条的长度很大,直接做会 T L E TLE TLE,于是可以矩阵快速幂优化,发现每个给出了颜色的地方跟没有颜色的地方转移式不太一样,于是我们对于每一种颜色预处理出转移式,然后每两个涂色格子之间的空格子用矩阵快速幂转移即可,复杂度是 O ( m ∗ a 3 l o g 2 a n ) O(m*a^3log_2a_n) O(ma3log2an)的, a a a是矩阵边长。
能不能优化时间复杂度呢?
显然是可以的,由于最后我们只用求一个向量而不是整个矩阵的值,因此我们可以预处理出 2 k 2^k 2k个空格子的矩阵,然后每次用倍增转移,这样可以做到 O ( m ∗ a 2 ∗ l o g 2 a n ) O(m*a^2*log_2a_n) O(ma2log2an)的时间复杂度。

现在考虑有多个布条的情况。
发现只需要把 n p np np的状态变成 s g sg sg函数值然后最后用类似分组背包的方法转移即可,由于这个题里的 s g sg sg值是不超过 3 3 3的,因此总复杂度是 O ( m a 2 l o g 2 a n ) O(ma^2log_2a_n) O(ma2log2an),其中 a = 64 a=64 a=64
代码:

#include
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
	static char buf[rlen],*ib,*ob;
	(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
	return ib==ob?-1:*ib++;
}
inline int read(){
	int ans=0;
	char ch=gc();
	while(!isdigit(ch))ch=gc();
	while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
	return ans;
}
const int mod=998244353;
typedef long long ll;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
struct Mat{
	int a[64][64],n,m;
	Mat(int k=0,int n0=64,int m0=64){
		n=n0,m=m0;
		for(ri i=0;i<n0;++i)for(ri j=0;j<m0;++j)a[i][j]=i==j?k:0;
	}
	friend inline Mat operator+(const Mat&a,const Mat&b){
		Mat ret(0,a.n,b.m);
		for(ri i=0;i<a.n;++i)for(ri j=0;j<a.m;++j)ret.a[i][j]=add(a.a[i][j],b.a[i][j]);
		return ret;
	}
	friend inline Mat operator*(const Mat&a,const Mat&b){
		Mat ret(0,a.n,b.m);
		for(ri i=0;i<a.n;++i)for(ri k=0;k<a.m;++k)if(a.a[i][k])
		for(ri j=0;j<b.m;++j)Add(ret.a[i][j],mul(a.a[i][k],b.a[k][j]));
		return ret;
	}
	friend inline Mat operator^(Mat a,int p){
		Mat ret=a;
		for(--p;p;p>>=1,a=a*a)if(p&1)ret=ret*a;
		return ret;
	}
}pw[32],ban[4];
typedef pair<int,int> pii;
const int N=1005;
int n,a[N],f[2][4],cur=0,tmp[4],sum[4];
vector<pii>upd[N];
bool Ban[4];
inline void update(int x,Mat&t){for(ri i=31;~i;--i)if((x>>i)&1)t=pw[i]*t;}
int main(){
	n=read();
	for(ri i=1;i<=n;++i)a[i]=read();
	for(ri a,b,c,tt=read();tt;--tt){
		a=read(),b=read(),c=read();
		upd[a].push_back(pii(b,c));
	}
	for(ri tt=1;tt<4;++tt){
		for(ri i=1;i<4;++i)Ban[i]=(bool)read();
		for(ri s1,s2,s3,s4,sta=0;sta<64;++sta){
			s1=sta&3,s2=(sta>>2)&3,s3=(sta>>4)&3,s4=0;
			for(ri i=0;i<4;++i)tmp[i]=0;
			Ban[3]&&(tmp[s1]=1),Ban[2]&&(tmp[s2]=1),Ban[1]&&(tmp[s3]=1);
			while(tmp[s4])++s4;
			ban[tt].a[s2|(s3<<2)|(s4<<4)][sta]=1;
		}
	}
	pw[0]=ban[1]+ban[2]+ban[3];
	for(ri i=1;i<32;++i)pw[i]=pw[i-1]*pw[i-1];
	f[cur=0][0]=1;
	for(ri pre,tt=1;tt<=n;++tt){
		sort(upd[tt].begin(),upd[tt].end());
		Mat t(0,64,1);
		t.a[63][0]=1;
		pre=0;
		for(ri pos,col,i=0;i<upd[tt].size();++i){
			pos=upd[tt][i].fi,col=upd[tt][i].se;
			update(pos-1-pre,t);
			t=ban[col]*t;
			pre=pos;
		}
		update(a[tt]-pre,t);
		for(ri i=0;i<4;++i)sum[i]=0;
		for(ri i=0;i<64;++i)Add(sum[(i>>4)&3],t.a[i][0]);
		cur^=1;
		for(ri i=0;i<4;++i){
			f[cur][i]=0;
			for(ri j=0;j<4;++j)Add(f[cur][i],mul(f[cur^1][j],sum[i^j]));
		}
	}
	cout<<f[cur][0];
	return 0;
}

你可能感兴趣的:(#,题解)