找规律可以发现进行足够多次操作后,一定会出现一个数字含有0,mindigit(x)=0;从那以后,数字就不会再变了;
#include
using namespace std;
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define lb(x) (x&(-x))
const int inf=0x3f3f3f3f;
const long long mod=1e9+7;
const int maxn=1e5+5;
int dig(int a){
int num[20];
int k=0;
while(a!=0){
num[k++]=a%10;
a/=10;
}
sort(num,num+k);
return num[0]*num[k-1];
}
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int t; cin>>t;
while(t--){
int a,n;
cin>>a>>n;
int ans=a;
for(int i=1;i<n;i++){
if(a==a+dig(a)){
ans=a;
break;
}
a=a+dig(a);
ans=a;
}
cout<<ans<<endl;
}
}
题意:将n个人分组,每个人所在的组的人数必须大于等于他的经验值e,可以有人不参与分组,问最多能分多少组。
贪心,先将所有人的e进行升序排序,前面的能分组就分组,这样可以使每组尽可能的小。最后多到的不能组队的人就不参与分组。
#include
using namespace std;
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define lb(x) (x&(-x))
const int inf=0x3f3f3f3f;
const long long mod=1e9+7;
const int maxn=2e5+5;
int a[maxn];
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int t; cin>>t;
while(t--){
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1);
int ans=0;
int pos=1;//每组的起始位置
int cnt=0;
while(pos<=n){
cnt=a[pos]-1;
while(a[cnt+pos]>cnt+1){
//当前组的最后一个e大于组的大小时
cnt=a[cnt+pos]-1;
if(cnt+pos>n) break;
}
if(pos+cnt<=n){
ans++;
}
pos=pos+cnt+1;
}
cout<<ans<<endl;
}
}
给三条边x,y,z的大小范围,(A<=x<=B<=y<=C<=z<=D)
问能组成多少个三角形。
很显然 x<=y<=z;所以要满足 x+y>z;
首先可以将z确定下来,然后根据y的范围(B<=y<=C),求出x(z-C<=x<=z-B)的临界范围,y的每次取值,对应了这个范围内的临界点,x>临界点就可以了。又因为x本身有范围(A<=x<=B),大致可以分成三种情况(见代码吧)。
因为数据范围很大,所以只能一次循环,关于y的取值就不能循环遍历了;
其实手写几组数据就能发现规律:
比如说y取了某个值p,x的临界值对应在q,且q,那么x可以有b-q种情况,这时候如果y=p+1,那么x的情况数就+1,这时候先不考虑特例,假设y的所有取值对应的x临界值都∈(a,b),假设这时候x临界值取值范围为(x,y),那么所有情况的总和就满足
(b-y)+(b-y+1)+…+(b-x)
这不就是等差数列求和吗!
相当于将for(y=a;y<=b;y++){
x=…
ans+=(b-x)
}简化了。
少了一层循环。
最后把几种特殊情况处理一下就ok了。
#include
using namespace std;
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define lb(x) (x&(-x))
const int inf=0x3f3f3f3f;
const long long mod=1e9+7;
const int maxn=2e5+5;
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int a,b,c,d;
cin>>a>>b>>c>>d;
int ans=0;
for(int i=c;i<=d;i++){
if(i-c>=b) break;
int l=i-c;
int r=i-b;
if(r<a){
ans+=(r-l+1)*(b-a+1);
continue;
}
if(l>=b){
ans+=0;
continue;
}
if(r>=b){
r=b-1;
}
if(l<a){
ans+=min((a-l),(r-l+1))*(b-a+1);
l=a;
}
ans+=((b-r)+(b-l))*(r-l+1)/2;
}
cout<<ans<<endl;
}
给定n,s;
要构造一个大小为n,和为s的数列a;
问能否构造出一个数组a,其任意子区间的和,都不能与k和s-k(k取1-s的任意值)
很容易想到构造成1,1,1,1,…,s-(n-1) 这样的数组,(直觉,别问为什么)
n个数的所有子区间的和∈**[1,…,n-1 , s-(n-1),…,s]**
要满足题意,只要n-1
n-1~s-n+1之间只要有两个空位就可以了
#include
using namespace std;
#define int long long
#define mem(a) memset(a,0,sizeof(a))
#define lb(x) (x&(-x))
const int inf=0x3f3f3f3f;
const long long mod=1e9+7;
const int maxn=1e5+5;
signed main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int n,s;
cin>>n>>s;
if(n>s/2){
cout<<"NO"<<endl;
return 0;
}
cout<<"YES"<<endl;
for(int i=1;i<n;i++){
cout<<1<<' ';
}
cout<<s-n+1<<endl;
cout<<(s+1)/2<<endl;
}
给定一个数组,和三种操作a,r,m
因为在操作上 m等价于a+r 所以可以 m = min(m,a+r);
求将所有数变成一样大的消费;
因为h存在最优解,令f(h)表示高度全变成h时的消费。
那么f(h)的函数就类似于二次函数
所以就用三分找到这个h值就可以了。
#include
using namespace std;
#define ll long long
#define mem(a) memset(a,0,sizeof(a))
#define lb(x) (x&(-x))
const int inf=0x3f3f3f3f;
const int maxn=1e5+5;
ll arr[maxn];
ll n,a,r,m;
ll f(ll h){
ll add=0,minus=0;
for(int i=1;i<=n;i++){
if(arr[i]<h){
add+=(h-arr[i]);
}
else{
minus+=(arr[i]-h);
}
}
ll num=min(add,minus)*m;
if(add>minus){
num+=(add-minus)*a;
}
else{
num+=(minus-add)*r;
}
return num;
}
void solve(){
if(m>a+r) m=a+r;
ll l=0,r=inf;
while(l<r){
ll midl=l+((r-l)/3);
ll midr=r-((r-l)/3);
if(f(midl)<f(midr)){
r=midr-1;
}
else{
l=midl+1;
}
}
cout<<f(l)<<endl;;
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
cin>>n>>a>>r>>m;
for(int i=1;i<=n;i++){
cin>>arr[i];
}
solve();
return 0;
}