ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

codeforces educational round #111

2021-10-15 20:34:15  阅读:148  来源: 互联网

标签:10 educational int codeforces leq 111 cnt pair id


link

D - Excellent Arrays

我们定义一个长度为 \(n\) 的整数序列 \(a\) 是好的仅当对于任意整数 \(i\in[1,n]\) ,都有 \(a_i\not=i\) 。
设 \(F(a)\) 等于满足 \(1\leq i<j\leq n,a_i+a_j=i+j\) 的 \((i,j)\) 对数。
我们定义一个长度为 \(n\) 的序列 \(a\) 是极好的,仅当:

  • \(a\) 是好的。
  • 对于任意整数 \(i\in[1,n]\) ,\(l\leq a_i\leq r\) 。
  • \(F(a)\) 的值是所有好的、长度为 \(n\) 的序列中最大的。

给定 \(n,l,r\) ,求极好的序列个数对 \(10^9+7\) 取模后的结果。\(T\) 组数据。

\(1\leq t\leq 10^3,\ 2\leq n\leq 2\cdot10^5,\ \sum n\leq 2\cdot 10^5,\ -10^9\leq l\leq 1,n\leq r\leq 10^9\)

最关键的点是设 \(a_i=i+b_i\) .

那么 \(i+b_i+j+b_j=i+j\) ,即 \(b_i=-b_j\) 且 \(b_i\not=0\) .

所以,\(b\) 中所有值取两种最好,即 \(+x\) 和 \(-x\) \((x>0)\),一种个数为 \(\lfloor\frac{n}{2}\rfloor\) ,一种个数为 \(n-\lfloor\frac{n}{2}\rfloor\) ,记为 \(A\) 和 \(B\) .

考虑枚举 \(x\) ,每个数,有三种情况,只能 \(+x\) ,只能 \(-x\) ,既可以 \(+x\) 也可以 \(-x\) .

只需要统计每种情况的个数,设为 \(cnt_1\) ,\(cnt_2\) , \(cnt_3\) ,

如果 \(A=B\) ,方案数为 \(\dbinom{cnt_3}{A-cnt_1}\) .

如果 \(A\not= B\) ,方案数为 \(\dbinom{cnt_3}{A-cnt_1}+\dbinom{cnt_3}{A-cnt_2}\) .

但是,这个 \(x\) 的范围可以达到 \(10^9\) . 这就无法循环 .

不过,很多 \(x\) 的情况是相同的 ,这些情况都是 \(cnt_3=n\) .

来考虑 \(cnt_3=n\) 的 \(x\) 的范围,考虑极值的情况,对于左端点是 \(\min(l-1,r-1)\) , 右端点的情况是 \(\min(r-n,n-1)\) ,因此,范围就是 \(x\in[1,\min(\min(l-1,r-1),\min(r-n,n-1))]\) .

在这个范围内,\(cnt_3=n\) ,对于每个 \(x\in[1,\min(\min(l-1,r-1),\min(r-n,n-1))]\) ,

如果 \(A=B\) ,方案数为 \(\dbinom{n}{A}\) .

如果 \(A\not=B\) ,方案数为 \(\dbinom{n}{A}+\dbinom{n}{B}\) .

剩下的 \(x\) 的取值范围大小不超过 \(n\) .

如果 \(x\) 增加,至少会导致一个数不能 \(+x/-x\) ,在 \(n\) 次操作后,必定只能 \(+x/-x\) 的数其中有一种超过 \(\min(A,B)\) .

所以,可以记录每个数只能 \(+x/-x\) 的 \(x\) 值 ,用 map 或 哈希表存储 .

每次,可以 \(O(1)\) 或 \(O(\log n)\) 地求出当前的 \(cnt_1\) 和 \(cnt_2\) .

用上面那个式子计算贡献即可 .

时间复杂度 : \(\mathrm O(tn)\)

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

code

  #include<bits/stdc++.h>
using namespace std;
inline int read(){
	char ch=getchar();
	while((ch<'0'||ch>'9')&&(ch!='-'))ch=getchar();
	bool f=false;
	if(ch=='-'){
		f=true;
		ch=getchar();
	}
	int res=0;
	while(ch>='0'&&ch<='9'){
		res=(res<<3)+(res<<1)+ch-'0';
		ch=getchar();
	}
	if(f)res=-res;
	return res;
}
inline void print(int res){
	if(res==0){
		putchar('0');
		return;
	}
	int a[10],len=0;
	while(res>0){
		a[len++]=res%10;
		res/=10;
	}
	for(int i=len-1;i>=0;i--)
		putchar(a[i]+'0');
}
const int mod=1e9+7;
int t;
int n,l,r;
int p[200010],inv[200010];
unordered_map<int,int>cnt1,cnt2;
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;
}
inline int C(int a,int b){
	return 1ll*p[a]*inv[b]%mod*inv[a-b]%mod;
}
void solve(){
	cnt1.clear();cnt2.clear();
	n=read();l=read();r=read();
	int mx=r;
	for(int i=1;i<=n;i++){
		cnt1[i-l+1]++;
		cnt2[r-i+1]++;
		mx=max(i-l+1,r-i+1);
	}
	int a=n/2,b=n-a,sum1=0,sum2=0;
	int mn=min(min(1-l,r-1),min(r-n,n-l));
	int ans=1ll*mn*C(n,a)%mod;
	for(int i=mn+1;i<mx;i++){
		if(cnt1.find(i)!=cnt1.end())sum1++;
		if(cnt2.find(i)!=cnt2.end())sum2++;
		if(sum1>a||sum2>b)break;
		int tmp=1ll*C(n-sum1-sum2,a-sum1);
		ans=(ans+tmp)%mod;
	}
	if(a!=b){
		sum1=0;sum2=0;
		ans=(ans+1ll*mn*C(n,b)%mod)%mod;
		for(int i=mn+1;i<mx;i++){
			if(cnt1.find(i)!=cnt1.end())sum1++;
			if(cnt2.find(i)!=cnt2.end())sum2++;
			if(sum1>b||sum2>a)break;
			int tmp=1ll*C(n-sum1-sum2,a-sum2);
			ans=(ans+tmp)%mod;
		} 
	}
	print(ans);
	putchar('\n');
}
int main(){
	p[0]=1;
	for(int i=1;i<200010;i++)p[i]=1ll*p[i-1]*i%mod;
	for(int i=0;i<200010;i++)inv[i]=ksm(p[i],mod-2);
	t=read();
	while(t--){
		solve();
	}
	return 0;
}
/*inline? ll or int? size? min max?*/r4eerrree3ee
E - Stringforce

设 \(s\) 是一个由前 \(k\) 个小写字母构成的字符串,\(v\) 是前 \(k\) 个小写字母中的某一个。定义 \(\mathrm{MaxLen}(s,v)\) 表示 \(s\) 所有仅由字母 \(v\) 构成的连续子串的最长长度。

定义 \(s\) 的价值为所有 \(\mathrm{MaxLen}(S,v)\) 的最小值,其中 \(v\) 取遍前 \(k\) 个小写字母。

现在给定一个长度为 \(n\) 的字符串 \(s\),\(s\) 中字母要么是前 \(k\) 个小写字母中的某一个,要么是问号。你需要将 \(s\) 中的每一个问号替换成前 \(k\) 个小写字母中的一个,并最大化 \(s\) 的价值。方便起见,你只需要输出这个最大的价值即可。

\(1\leq n\leq 2\cdot 10^5,1\leq k\leq 17\)

看到为所有 \(\mathrm {MaxLen}(S,v)\) 的最小值,输出这个最大值,想到要二分答案 .

现在的问题就是如何判断,二分答案 \(x\) .

因为这个 \(k\) 非常的小,想到一个最原始的状压 \(dp\) ,\(f(i,s)\) 表示到了第 \(i\) 位,是否能满足 \(s\) 中的集合 \(\mathrm {Maxlen}(s,v)\geq k\) . 发现这个 \(dp\) 是 \(\mathrm O(n2^k)\) 的,非常不好 .

换一种表达方式,\(f(s)\) 表示,满足 \(s\) 中集合 \(\mathrm{Maxlen}(s,v)\geq k\) 最少需要的前缀 .

每次预处理 \(len(i,j)\) 表示第 \(i\) 个字符,从第 \(j\) 位往后最少需要多少位使得连续字符的长度至少为 \(k\) .

对于当前状态 \(f(s)\) ,枚举新加入的一个字符 \(i\),

\(f(s|2^i)=\min(f(s|2^i),len(i,f(s)))\) .

预处理是 \(\mathrm O(kn)\) 的,\(dp\) 是 \(\mathrm O(k2^k)\) 的,加上二分的 \(\log\) .

时间复杂度 : \(\mathrm O((kn+k2^k)\log n)\)

空间复杂度 : \(O(kn+k2^k)\)

code

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9+10;
int n,k;
string s;
int a[200010][18],b[200010][18];
int f[1<<18];
bool check(int len){
	for(int i=0;i<=n;i++)for(int j=0;j<k;j++)b[i][j]=n;
	for(int j=0;j<k;j++){
		for(int i=n-1;i>=0;i--){
			b[i][j]=min(b[i][j],b[i+1][j]);
			if(a[i][j]>=len)b[i][j]=min(b[i][j],i+len-1);
		}
	}
	for(int mask=0;mask<(1<<k);mask++)f[mask]=inf;
	f[0]=0;
	for(int mask=0;mask<(1<<k);mask++){
		if(f[mask]==inf)continue;
		for(int i=0;i<k;i++){
			if(((mask&(1<<i))>0)||(b[f[mask]][i]>=n))continue;
			f[mask|(1<<i)]=min(f[mask|(1<<i)],b[f[mask]][i]+1);
		}
	}
	return f[(1<<k)-1]<=n;
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>k>>s;
	for(int c=0;c<k;c++){
		int j=-1;
		for(int i=0;i<n;i++){
			j=max(j,i-1);
			while(j+1<n&&(s[j+1]=='?'||s[j+1]==c+'a'))j++;
			if(j>=i)a[i][c]=j-i+1;
		}
	}
	int low=0,high=n+1,ans=0;
	while(low<high){
		int mid=(low+high)>>1;
		if(check(mid)){
			ans=max(ans,mid);
			low=mid+1;
		}
		else{
			high=mid;
		}
	} 
	cout<<ans<<endl;
	return 0;
}
/*inline? ll or int? size? min max?*/
F - Jumping Around

有一条坐标轴,上面有 \(n\) 个石头,第 \(i\) 个石头在 \(a_i\) 位置. 每个石头的位置唯一,满足 \(a_1<a_2<\cdots <a_n\) .

有一个机械青蛙在第 \(s\) 个石头上,有一个跳跃半径 \(d\) ,有一个跳跃距离 \(k\) ,青蛙可以跳到距离在 \([d-k,d+k]\) 之内任意方向的石头.

有 \(q\) 组询问,每个询问形如 \(i,k\) ,表示询问是否可以在跳跃距离为 \(k\) 的情况下从第 \(s\) 个石头跳到第 \(i\) 个石头.

\(1\leq n,q\leq 2\cdot 10^5,1\leq s\leq n,1\leq d\leq 10^6,1\leq a_i\leq 10^6,\ a_1<a_2<\cdots <a_n,1\leq i\leq n,1\leq k\leq 10^6\)

发现对于特定的 \(i\) ,\(k\) 具有单调性,可以考虑求出最小的 \(k\) 使得 \(s,i\) 之间可以互相到达.

对于 \(i,j\) 之间,存在一条 \(|a_i-d-a_j|\) 的边和一条 \(|a_i+d-a_j|\) 的边.

那么,就是在一个 \(n\) 个点,\(n^2\) 条边的图上求 \(s\) 到 \(i\) 的最小瓶颈路径,从 \(s\) 到 \(i\) 的所有最小瓶颈路径都在该图的最小生成树上. 那么,就是要求该图的最小生成树.

边的数量非常大,达到了 \(n^2\) 级别,就不能考虑用 kruscal 做了,要用 prim .

如何计算已经在树上的点和非树点的最小连边.

首先 ,在最小生成树上的点都可以跳到位置 \(a_i-d\) 和位置 \(a_i+d\) ,那么,加入在离 \(a_i-d\) 和 \(a_i+d\) 的,且不在树上的点最优. 可以用两个 set 维护,一个 set \(S_2\) 保存每个在树上的点的 \(a_i-d\) 和 \(a_i+d\) 位置和编号;另一个 set \(S_1\) 维护每个在非树的点和树上的点的最小距离 \(dis(i)\),编号 \(i_1\) 和所连树点的编号 \(i_2\) .

考虑有几种操作,

  1. 在 \(S_1\) 中删除 \(i\) ,找到左边,右边分别距离 \(i\) 最近的两个非树点 \(x,y\) ,找到距离 \(x\) 最近的右侧树点,跟新 \(dis(x),S_1\) ;找到距离 \(y\) 最近的左侧树点,跟新 \(dis(x),S_1\) . 这是 erase 操作.
  2. 在 \(S_2\) 中加入点 \(i\) ,有两个位置 \(a_i-d,a_i+d\) ,是等价的. 找到 \(p\) 左边和右边最近的点 \(x,y\) ,跟新 \(dis(x),S_1\) ,\(dis(y),S_2\) . 这是 insert 操作.

首先是查询 \(S_1\) 中最小的元素 \(dis(i),i_1,i_2\),现在 \(S_2\) 中直接插入 \(a_{i_1}-d,i_1\) 和 \(a_{i_1}+d,i_1\) ,再对 \(i_1\) 进行删除操作(顺序不能反),对 \(a_{i_1}-d\) 和 \(a_{i_1}+d\) 进行 insert 操作 .

此时,可以在 \(O(nlogn)\) 时间内建出一棵最小生成树.

之后只需要在最小生成树上求一个简单的 \(dp\) ,用 \(dp(i)\) 表示从根节点 \(s\) 到节点 \(i\) 路径上的边权最大值.

之后询问的时候就可以 \(O(1)\) 地查询 .

时间复杂度 : \(O(nlogn+q)\)

空间复杂度 : \(O(nlogn)\) ? set的内存怎么算

code

#include<bits/stdc++.h>
using namespace std;
const int inf=1e9;
int n,q,s,d;
int a[200010];
int dis[200010];
set<pair<int,int> >s1,s2; //pos i
bool ok[200010];
priority_queue<pair<int,pair<int,int> > >Q; //dis u v
void ins(int pos,int id){
	set<pair<int,int> >::iterator itr=s1.lower_bound(make_pair(pos,-1));
	set<pair<int,int> >::iterator itl=s1.upper_bound(make_pair(pos,inf));
	if(itl!=s1.begin()){
		itl--;
		int np=itl->first,nid=itl->second;
		if(abs(np-pos)<dis[nid]){
			dis[nid]=abs(np-pos);
			Q.push(make_pair(-dis[nid],make_pair(nid,id)));
		}
	}
	if(itr!=s1.end()){
		int np=itr->first,nid=itr->second;
		if(abs(np-pos)<dis[nid]){
			dis[nid]=abs(np-pos);
			Q.push(make_pair(-dis[nid],make_pair(nid,id)));
		}
	}
}
void era(int u){
	s1.erase(make_pair(a[u],u));
	set<pair<int,int> >::iterator itr=s1.lower_bound(make_pair(a[u],-1));
	set<pair<int,int> >::iterator itl=s1.upper_bound(make_pair(a[u],inf));
	if(itl!=s1.begin()){
		itl--;
		int id=itl->second; // dot : id   pos : a[id] find right 
		set<pair<int,int> >::iterator it=s2.lower_bound(make_pair(a[id],-1));
		if(it!=s2.end()){
			int id2=it->second;
			if(abs(it->first-a[id])<dis[id]){
				dis[id]=abs(it->first-a[id]);
				Q.push(make_pair(-dis[id],make_pair(id,id2)));
			}
		}
	}
	if(itr!=s1.end()){
		int id=itr->second;
		set<pair<int,int> >::iterator it=s2.upper_bound(make_pair(a[id],inf));
		if(it!=s2.begin()){
			it--;	
			int id2=it->second;
			if(abs(a[id]-it->first)<dis[id]){
				dis[id]=abs(a[id]-it->first);
				Q.push(make_pair(-dis[id],make_pair(id,id2)));
			}
		}
	}
}
vector<pair<int,int> >g[200010];
inline void add_edge(int u,int v,int cost){
	g[u].push_back(make_pair(v,cost));
	g[v].push_back(make_pair(u,cost));
}
int dp[200010];
void dfs(int x,int fa){
	for(int i=0;i<(int)g[x].size();i++){
		int to=g[x][i].first,cost=g[x][i].second;
		if(to==fa)continue;
		dp[to]=max(dp[to],max(dp[x],cost));
		dfs(to,x);
	}
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>q>>s>>d;
	s--;
	for(int i=0;i<n;i++)cin>>a[i];
	for(int i=0;i<n;i++)if(i!=s)s1.insert(make_pair(a[i],i));
	for(int i=0;i<n;i++)dis[i]=inf;
	s2.insert(make_pair(a[s]-d,s));
	s2.insert(make_pair(a[s]+d,s));
	ins(a[s]-d,s);
	ins(a[s]+d,s);
	ok[s]=true;
	while(!Q.empty()){
		int dis=-Q.top().first,u=Q.top().second.first,v=Q.top().second.second;
		Q.pop();
		if(ok[u])continue;
		ok[u]=true;
		add_edge(u,v,dis);
		s2.insert(make_pair(a[u]-d,u));
		s2.insert(make_pair(a[u]+d,u));
		era(u);
		ins(a[u]-d,u);
		ins(a[u]+d,u);
	}
	dfs(s,-1);
	for(int i=0;i<q;i++){
		int x,k;
		cin>>x>>k;
		x--;
		if(dp[x]>k)cout<<"No\n";
		else cout<<"Yes\n";
	}
	return 0;
}

标签:10,educational,int,codeforces,leq,111,cnt,pair,id
来源: https://www.cnblogs.com/suadwm/p/15412532.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有