Educational Codeforces Round 80 (Rated for Div. 2)

题目链接:Educational Codeforces Round 80 (Rated for Div. 2)


A:很明显是一个双沟函数,但是有一些取整的东西,导致容易有小偏差。但是我们完全不用O(1) 去做,我们知道最小值肯定在sqrt(d)附近,于是小范围暴力一下即可。

AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
#define int long long
using namespace std;
int T,n,d;
inline void solve(){
	cin>>n>>d;	int l=max((int)sqrt(d)-1000,0LL),r=2*sqrt(d)+1000,res=1e14;
	for(int i=l;i<=r;i++){
		res=min(res,(int)(i+ceil(d*1.0/(i+1))));
	}
	if(res<=n)	puts("YES");
	else	puts("NO");
}
signed main(){
	cin>>T;
	while(T--)	solve();
	return 0;
}

B:假设b的位数为x,那么我们可得到 a * pow(10,x) + b = a * b + a + b 所以 b为9,99,999.。。。

AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
#define int long long
using namespace std;
int a,b,T;
inline void solve(){
	cin>>a>>b;	int t=9,res=0;
	while(t<=b){
		res+=a;	t=t*10+9;
	}
	cout<<res<<endl;
}
signed main(){
	cin>>T;
	while(T--)	solve();
	return 0;
}

C:我们可以发现,当最后一位满足条件的时候,那么整个序列都是满足条件的。所以我们预处理出,当前可以选i个数字,长度为j的非严格LIS的数量。

AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
#define int long long
using namespace std;
const int p=1e9+7;
int n,m,dp[20][1010],res;
void init(){
	for(int i=1;i<=n;i++)	dp[0][i]=1;
	for(int i=1;i<=m;i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=j;k++)	dp[i][j]=(dp[i][j]+dp[i-1][k])%p;
}
signed main(){
	cin>>n>>m;	init();
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j++){
			res=(res+dp[m-1][i]*dp[m-1][n-j+1]%p)%p;
		}
	}
	cout<<res;
	return 0;
}

D:最小值最大,首先考虑二分答案。比如当前二分的答案是mid,那么其实数字本身的大小不重要了,我们只关注是否大于等于mid,所以把大于等于的置为1,小于的置为0,那么就可以状态压缩了。只要有两个状态或运算为 1<

AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
//#define int long long
using namespace std;
const int N=3e5+10;
int n,m,a[N][9],st[1<<8],l,r=1e9,res1=1,res2=1;
inline int check(int mid){
	memset(st,0,sizeof st);
	for(int i=1;i<=n;i++){
		int s=0;
		for(int j=0;j<m;j++)	if(a[i][j]>=mid)	s|=(1<<j);
		st[s]=i;
	}
	for(int i=(1<<m);i>=0;i--){
		for(int j=0;j<m;j++)	if(st[i|(1<<j)])	st[i]=st[i|(1<<j)];
	}
	for(int i=0;i<(1<<m);i++){
		if(st[i]&&st[((1<<m)-1)^i]){
			res1=st[i],res2=st[((1<<m)-1)^i];	return 1;
		}
	}
	return 0;
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)	for(int j=0;j<m;j++)	scanf("%d",&a[i][j]);
	while(l<r){
		int mid=l+r+1>>1;
		if(check(mid))	l=mid;
		else	r=mid-1;
	}
	cout<<res1<<' '<<res2;
	return 0;
}

E:我们考虑最前面的位置,当且仅当当前这个人往前面走的时候才会往前,否则无论如何位置要么不变,要么往后移动。所以我们考虑最后的位置即可。最后的位置其实就是看前面有多少人,那么我们可以用树状数组维护前面多少人即可,因为都是往前面移动,所以我们最开始的位置从m+1开始即可。

AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
//#define int long long
using namespace std;
const int N=3e5+10;
int d[N<<1],n,m,l[N],r[N],pos[N],len;
inline void add(int x,int v){for(x;x<=len;x+=(x&(-x))) d[x]+=v;}
inline int ask(int x){int s=0; for(;x;x-=(x&(-x))) s+=d[x]; return s;}
signed main(){
	cin>>n>>m;	len=n+m;
	iota(l+1,l+1+n,1);	iota(r+1,r+1+n,1);
	for(int i=1;i<=n;i++)	pos[i]=m+i,add(pos[i],1);
	for(int i=1,x;i<=m;i++){
		scanf("%d",&x);	l[x]=1;
		r[x]=max(r[x],ask(pos[x]));
		add(pos[x],-1);	pos[x]=m+1-i; add(pos[x],1);
	}
	for(int i=1;i<=n;i++)	r[i]=max(r[i],ask(pos[i]));
	for(int i=1;i<=n;i++)	printf("%d %d\n",l[i],r[i]);
	return 0;
}

你可能感兴趣的:(Codeforces)