Codeforces 1197簡単な問題解


文書ディレクトリ
  • A題
  • B題
  • C題
  • D題
  • E題
  • F題
  • レースゲート
    A題
    トランスポートゲートは、次いで、a[n−1]−1 a[n−1]−1 a[n−1]−1とn−2 n−2 n−2の最小値をとる.コード:
    #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)<<'
    '
    ; } 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}ai−ai−1の前k−1 k−1 k−1の大きい値を用い,その後a n−a 1 a_n-a_1 an−a 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,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)ができるはずだが、ブロガーは怠け者でO(n m)O(nm)O(nm)を書いた.i fiは、区間左端点がi i i iの場合の最適値を表す.このi i i iを右から左に列挙することを考える.そこで、1つの左端点i i i iについて、2つの状況を考慮する必要があります.
  • ⌈r−l+1 m⌉=1lceilfrac{r−l+1}{m}rceil=1⌈mr−l+1⌉=1,直接O(m)O(m)暴力列挙でよい.
  • ⌈r−l+1 m⌉>1lceilfrac{r−l+1}{m}rceil>1⌈mr−l+1⌉>1であると、i i i i~i+m−1 i+m−1 i+m−1 i+m−m−1が選択され、i+m i+m i+m i+m i+m~n nにおいて⌈r−l+1 m⌉−l+1lceilfrac{ r−l+1}{m}rceil−1{m}rceil−1{m}rceil−1{m}rceil−1{m}rceil−1{m}rce⌈mr−l+1⌉−1群であれば、f i+mf_{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を3回書き間違えた...まず左右の端点を離散化した.一つの区間[l i,r i][l_i,r_i][li,ri]に対してのみ、r j≦l i r_を満たすj\le l_i rj≦liの区間はそれに影響を与えることができ、問題の意味に基づいて、私たちは各区間に対してこのような情報を維持しなければならない.
  • 開始から現在の区間までの総空桁数の最小値は
  • です.
  • 開始から現在の区間までの総空桁数が最小値に等しいシナリオ数を満たす
  • 点p p pに対して、k k kの方式がp p pで終了し、総空位数の最小値がd i s disに等しいとすると、p p pがq q qに移行できる場合、総空位数はd i s+q−p=d i s−p+q dis+q−p=dis−p+q−dis+q−p=dis−p+qとなり、1つのr j r_r_qから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}disrj−rj=minrk≦li{disrk−rk}では,線分ツリー上でd i s p−p dis_を維持する.{p}−p disp−pの最小値とこの最小値の出現回数でよい.
    コード:
    #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題
    コンベヤーは私が長い間考えていた問題を感じていたが、試合中に問題を間違えてできなかったので、問題解を見て突然自分が問題を間違えたことに気づいた.の布袋が1枚しかないことを考えています.せいぜい3 3、3歩しか歩けないことに気づいた.状態f i,0/1,0/1,0/1,0/1 f_を設計することができる.{i,0/1,0/1,0/1}fi,0/1,0/1,0/1,0/1は前のi i i個の格子を表し,最後の3個の格子から出発した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を作ることができ、マトリクスの高速べき乗を最適化することができ、色が与えられた場所ごとに色がない場所と移動式が異なることが分かった.そこで、色ごとに移動式を前処理し、2つの塗布格子間のスペースごとにマトリクスで高速べき乗で移動すればよい.複雑度はO(m∗a 3 l o g 2 a n)O(m*a^3 log_2 a_n)O(m∗a 3 log 2 an)であり,a a a aは行列辺長である.時間の複雑さを最適化できますか?明らかに可能であり、最後に行列全体の値ではなくベクトルを1つ求めるだけであるため、2 k 2^k 2 k個のスペースの行列を前処理し、次いで倍増転送するたびに、O(m∗a 2∗l o g 2 a n)O(m*a^2*log_2 a_n)O(m∗a 2∗log 2 an)の時間的複雑さを達成することができる.
    今は複数の布がある場合を考えています.n p np npの状態をs g sg sg関数値に変えて最後にパケットバックパックのような方法で移行すればよいことが分かった.この問題のs g sg値は3 3 3 3を超えないため、総複雑度はO(m a 2 l o g 2 a)O(ma^2 log_2 a_n)O(ma 2 log 2 an)であり、ここでa=64 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;
    }