abc 234

D - Prefix K-th Max

给出一个排列 \(p\) 和一个正整数 \(k\) ,对于所有的 \(i=k,k+1,\cdots,n\) ,需要找到 \(p\) 的前 \(i\) 个数中第 \(k\) 大的.

\(1\leq k\leq n\leq 5\cdot 10^5\)

建立线段树,之后就是单点修改和线段树上二分. 也可以 bit 上上二分.

时间复杂度 : \(O(n\log n)\)

空间复杂度 : \(O(n)\)

code
#include<bits/stdc++.h>
using namespace std;
char in[100005];
int iiter=0,llen=0;
inline char get(){
	if(iiter==llen)llen=fread(in,1,100000,stdin),iiter=0;
	if(llen==0)return EOF;
	return in[iiter++];
}
inline int rd(){
	char ch=get();while(ch<'0'||ch>'9')ch=get();
	int res=0;while(ch>='0'&&ch<='9')res=(res<<3)+(res<<1)+ch-'0',ch=get();
	return res;
}
inline void pr(int res){
	if(res==0){putchar('0');return;}
	static int out[10];int len=0;
	while(res)out[len++]=res%10,res/=10;
	for(int i=len-1;i>=0;i--)putchar(out[i]+'0');
}
const int N=5e5+10;
class seg_tree{
public:
	int ts[N<<2];
	void init(){memset(ts,0,sizeof(ts));}
	void upd(int x,int l,int r,int pos){
		if(l==r){ts[x]++;return;}
		int mid=(l+r)>>1;
		if(pos<=mid)upd(x<<1,l,mid,pos);
		else upd(x<<1|1,mid+1,r,pos);
		ts[x]=ts[x<<1]+ts[x<<1|1];
	}
	int qry(int x,int l,int r,int k){
		if(l==r)return l;
		int mid=(l+r)>>1;
		if(k<=ts[x<<1|1])return qry(x<<1|1,mid+1,r,k);
		else return qry(x<<1,l,mid,k-ts[x<<1|1]);
	}
}T;
int n,k,a[N];
int main(){
	n=rd();k=rd();
	for(int i=0;i<n;i++)a[i]=rd()-1;
	for(int i=0;i<n;i++){
		T.upd(1,0,n-1,a[i]);
		if(i>=k-1)pr(T.qry(1,0,n-1,k)+1),putchar('\n');
	}	
	return 0;
}
/*inline? ll or int? size? min max?*/

E - Arithmetic Number

定义 \(\texttt{luck number}\)\(d_1-d_2=d_3-d_2=\cdots =d_{k}-d_{k-1}\) 的数 .

给出一个数 \(X\) ,求不小于 \(X\) 的最小的 \(\texttt{luck number}\) .

\(1\leq X\leq 10^{17}\)

小于 \(10^6\) 的暴力跑.

其余考虑,如果 \(d_1\)\(d_2\) 确定了,那么后面的都可以确定了,那么枚举 \(d_2\)\(X_2\)\(9\) , 依次 check .

其余的确定 \(d_1=X_1+1\) ,枚举 \(d_2\)\(0\)\(9\) ,依次 check .

这里我写错了一个点,很久没有调出来,就是检查的时候不是直接地判断 \(tmp<X_i\) ,而是之前如果出现了 \(tmp>X_i\) ,那么之后都是当前数必定大于 \(X\) ,不管后面如何.

时间复杂度 : \(O(|X|)\)

空间复杂度 : \(O(|X|)\)

code
#include<bits/stdc++.h>
using namespace std;
string s;
bool chk(int val){
	vector<int>v;
	while(val){
		v.push_back(val%10);
		val/=10;
	}
	if((int)v.size()<=2)return true;
	int dif=v[1]-v[0];
	for(int i=2;i<(int)v.size();i++)if(v[i]-v[i-1]!=dif)return false;
	return true;
}
void work1(){
	int val=0;
	for(int i=0;i<(int)s.size();i++)val=val*10+s[i]-'0';
	for(int i=val;;i++){
		if(chk(i)){
			cout<<i<<endl;
			return;
		}
	}
} 
void work2(){
	vector<int>v;
	for(int i=0;i<(int)s.size();i++)v.push_back(s[i]-'0');
	int dif=v[1]-v[0],tmp=v[1];
	bool ok=true,large=false;
	for(int i=2;i<(int)v.size();i++){
		tmp+=dif;
		if(tmp<0||tmp>9||(tmp<v[i]&&!large))ok=false;
		if(tmp>v[i])large=true;
	}
	if(ok){
		cout<<v[0]<<v[1];
		tmp=v[1];
		for(int i=2;i<(int)v.size();i++){
			tmp+=dif;
			cout<<tmp;
		}
		cout<<endl;
		return;
	}
	for(int d=v[1]+1;d<10;d++){
		dif=d-v[0];tmp=d;ok=true;
		for(int i=2;i<(int)v.size();i++){
			tmp+=dif;
			if(tmp<0||tmp>9)ok=false;
		}
		if(ok){
			tmp=d;
			cout<<v[0]<<d;
			for(int i=2;i<(int)v.size();i++){
				tmp+=dif;
				cout<<tmp;
			}
			cout<<endl;
			return;
		}	
	}
	for(int d=0;d<10;d++){
		dif=d-v[0]-1;tmp=d;ok=true;
		for(int i=2;i<(int)v.size();i++){
			tmp+=dif;
			if(tmp<0||tmp>9)ok=false;
		}
		if(ok){
			tmp=d;
			cout<<v[0]+1<<d;
			for(int i=2;i<(int)v.size();i++){
				tmp+=dif;
				cout<<tmp;
			}
			cout<<endl;
			return;
		}
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>s;	 
	if((int)s.size()<6)work1();
	else work2();
	return 0;
}
/*inline? ll or int? size? min max?*/

F - Reordering

给出字符串 \(S\) . 问 \(S\) 中的子序列可以重排组成的字符串的个数. 模 \(998244353\).

\(1\leq |S|\leq 5000\)

直接 \(dp\) 即可 .

\(dp(i,j)\) 表示,到了字符 i 子序列长度为 \(j\) 的方案数.

code
#include<bits/stdc++.h>
using namespace std;
const int N=5e3+10;
const int mod=998244353;
string s;
int C[N][N];
int cnt[N];
int dp[30][N];
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	C[0][0]=1;
	for(int i=1;i<N;i++){
		C[i][0]=1;
		for(int j=1;j<N;j++){
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
		}
	}
	cin>>s;
	for(int i=0;i<(int)s.size();i++)cnt[s[i]-'a']++;
	dp[0][0]=1;
	for(int i=0;i<26;i++)for(int j=0;j<=(int)s.size();j++){
		if(dp[i][j]==0)continue;
		for(int k=0;k<=cnt[i];k++){
			dp[i+1][j+k]=(dp[i+1][j+k]+1ll*dp[i][j]*C[j+k][k]%mod)%mod;
		}
	}
	int ans=0;
	for(int i=1;i<=(int)s.size();i++)ans=(ans+dp[26][i])%mod;
	cout<<ans<<endl;
	return 0;
}
/*inline? ll or int? size? min max?*/

G - Divide a Sequence

给出一个序列 \(A\) .

将序列分成几段,将当前分段的价值记为每一段的最大值减最小值的乘积 .

求所有分段价值的和 .

\(1\leq n\leq 3\cdot 10^5,1\leq A_i\leq 10^9\)

\(dp(i)\) 表示以 \(A_i\) 结尾的价值和 .

\(dp(i)=\sum dp(j)\max\{A_{j+1},A_{j+2},\cdots,A_i\}-\sum dp(j)\min\{A_{j+1},A_{j+2},\cdots,A_i\}\)

\(dp\) 可以用线段树优化,第 \(j\) 个节点记录 $dp(j)\max/\min{A_{j+1},A_{j+2},\cdots,A_i} $ .

但是最大值/最小值可能会变,套路地用 \(\mathrm{stack}\) 维护,然后乘上之前最大值/最小值的逆元之后乘上当前最大值/最小值.

因为每个元素只会进栈出栈依次,分析得到总的修改次数是 \(O(n)\) 级别的 .

时间复杂度 : \(O(n\log n)\)

空间复杂度 : \(O(n)\)

code
#include<bits/stdc++.h>
using namespace std;
char in[100005];
int iiter=0,llen=0;
inline char get(){
	if(iiter==llen)llen=fread(in,1,100000,stdin),iiter=0;
	if(llen==0)return EOF;
	return in[iiter++];
}
inline int rd(){
	char ch=get();while(ch<'0'||ch>'9')ch=get();
	int res=0;while(ch>='0'&&ch<='9')res=(res<<3)+(res<<1)+ch-'0',ch=get();
	return res;
}
inline void pr(int res){
	if(res==0){putchar('0');return;}
	static int out[10];int len=0;
	while(res)out[len++]=res%10,res/=10;
	for(int i=len-1;i>=0;i--)putchar(out[i]+'0');
}
const int N=3e5+10;
const int mod=998244353;
class segtree{
public:
	int ts[N<<2],tag[N<<2];
	void init(){
		memset(ts,0,sizeof(ts));
		memset(tag,1,sizeof(tag));
	}
	inline void pd(int x){
		if(tag[x]==1)return;
		ts[x<<1]=1ll*ts[x<<1]*tag[x]%mod;
		ts[x<<1|1]=1ll*ts[x<<1|1]*tag[x]%mod;
		tag[x<<1]=(tag[x<<1]+tag[x])%mod;
		tag[x<<1|1]=(tag[x<<1|1]+tag[x])%mod;
		tag[x]=1;
	}
	void upd(int x,int l,int r,int pos,int val){
		if(l==r){
			ts[x]=val;
			return;
		}
		pd(x);
		int mid=(l+r)>>1;
		if(pos<=mid)upd(x<<1,l,mid,pos,val);
		else upd(x<<1|1,mid+1,r,pos,val);
		ts[x]=(ts[x<<1]+ts[x<<1|1])%mod;
	}
	void upd(int x,int l,int r,int cl,int cr,int val){
		if(l==cl&&r==cr){
			if(l!=r)pd(x);
			tag[x]=(tag[x]+val)%mod;
			ts[x]=1ll*ts[x]*val%mod;
			return;
		}
		pd(x);
		int mid=(l+r)>>1;
		if(cr<=mid)upd(x<<1,l,mid,cl,cr,val);
		else if(mid+1<=cl)upd(x<<1|1,mid+1,r,cl,cr,val);
		else upd(x<<1,l,mid,cl,mid,val),upd(x<<1|1,mid+1,r,mid+1,cr,val);
		ts[x]=(ts[x<<1]+ts[x<<1|1])%mod;
	}
}T1,T2;
inline int ksm(int x,int k){
	if(k==0)return 1;
	int res=ksm(x,k>>1);
	res=1ll*res*res%mod;
	if(k&1)res=1ll*res*x%mod;
	return res;
}
int n;
int a[N];
int dp[N];
stack<pair<int,int> >s1,s2;//max&min
int main(){
	n=rd();
	for(int i=0;i<n;i++)a[i]=rd();
	T1.init();T2.init();
	cerr<<n<<endl;
	dp[0]=1;
	T1.upd(1,0,n-1,0,dp[0]);
	T2.upd(1,0,n-1,0,dp[0]);
	for(int i=1;i<=n;i++){
		while(!s1.empty()&&s1.top().first<=a[i-1]){
			pair<int,int>p=s1.top();s1.pop();
			T1.upd(1,0,n-1,(s1.empty()?0:s1.top().second+1),p.second,ksm(p.first,mod-2));
		}
		T1.upd(1,0,n-1,(s1.empty()?0:s1.top().second+1),i-1,a[i-1]);
		s1.push(make_pair(a[i-1],i-1));
		while(!s2.empty()&&s2.top().first>=a[i-1]){
			pair<int,int>p=s2.top();s2.pop();
			T2.upd(1,0,n-1,(s2.empty()?0:s2.top().second+1),p.second,ksm(p.first,mod-2));
		}
		T2.upd(1,0,n-1,(s2.empty()?0:s2.top().second+1),i-1,a[i-1]);
		s2.push(make_pair(a[i-1],i-1));
		dp[i]=(T1.ts[1]-T2.ts[1]+mod)%mod;
		if(i<n)T1.upd(1,0,n-1,i,dp[i]);
		if(i<n)T2.upd(1,0,n-1,i,dp[i]);
	}
	pr(dp[n]);putchar('\n');
	return 0;
}
/*inline? ll or int? size? min max?*/

Ex - Enumerate Pairs

鸽鸽鸽 🕊

posted @ 2022-01-09 17:49  xyangh  阅读(46)  评论(0)    收藏  举报