题目链接:Educational Codeforces Round 80 (Rated for Div. 2)
A:很明显是一个双沟函数,但是有一些取整的东西,导致容易有小偏差。但是我们完全不用O(1) 去做,我们知道最小值肯定在sqrt(d)附近,于是小范围暴力一下即可。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
#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<bits/stdc++.h>
#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<bits/stdc++.h>
#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<<m-1 ,那么就是合法的。设 1<<m -1 为s,那么我们可以对每一个状态x找 s^x是否存在,但是有一个问题,就是可能某一个状态是包含 x ^ s而不单单是 s ^ x ,所以我们把包含每一个状态且存在的,都置为1即可。
AC代码:
#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include<bits/stdc++.h>
//#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<bits/stdc++.h>
//#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;
}