题目描述 0.5s 512MB
小明是某个游戏中粮仓的管理者,粮食在过冬的时候被老鼠吃掉了好多,因此对不上账了。可是马上领导就要来检查了,小明很慌张。
粮仓里面一共有\(n\)堆粮食,第\(i\)堆粮食按账目上的数量,应该有\(a_i\)斤。
但是现在每一堆粮食都只剩下\(1\)斤了。
于是小明决定祭出自己的终极武器:粮食放大器。
具体地:小明每次可以使用一个从付费商城购买的粮食放大器,然后对区间\([l,r]\)使用,使用后,区间\([l,r]\)里的每一堆粮食,要么\(+1\)斤,要么\(\times 2\),这个小明可以自主决定(也就是,每一堆可以自主的选择\(+1\)或者\(\times 2\))。
由于粮食放大器很贵,所以小明决定尽量少的购买粮食放大器。
请帮助小明算一下,他最少要购买多少个粮食放大器,才能完成目标。
请注意,小明必须把每一堆粮食恰好变成\(a_i\)。
输入格式
第一行输入\(n\)。
第二行输入\(n\)个正整数,表示\(a_1,...,a_n\)。
输出格式
输出答案在单独的一行。
样例输入 #1
1
101
样例输出 #1
9
样例输入 #2
3
3 2 3
样例输出 #2
3
样例输入 #3
10
3 1 3 100 13 2 14 3 5 14
样例输出 #3
17
数据范围
对于10%的数据:\(n\leq 1,a_i\leq 10^6\)。
对于25%的数据:\(n\leq 1\)。
对于60%的数据:\(n\leq 50000\)。
对于70%的数据:\(n\leq 10^5\)。
对于80%的数据:\(n\leq 2\times 10^5\)。
对于100%的数据:\(1\leq n\leq 10^6,1\leq a_i\leq 10^{18}\)。
analysis:
求出每个数字的最大次数和最小次数,让他们尽可能接近,画图可以分析出来,如果前面的数字确定下来是x次,下一个数字如果上下界如果包含x,则当前数字的次数也是x,如果不包含,下界大于x,则取下界,上界小于x,则取上界。
obstacle:当前数字区间和前一个数字区间做比较,就傻了,应该和前一个已经确定的数字x做比较。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,b[2000005], ans;
int cal(ll x){
ll cnt = 0;
while(x > 1){
cnt++;
if(x & 1 == 1) x -= 1;
else x >>= 1;
}
return cnt;
}
struct Node{
ll l, r;
}a[2000005];
int main(){
cin>>n; ll x;
for(int i = 1; i <= n; i++){
cin>>x;
a[i].r = x-1;
a[i].l = cal(x);
// cout<<"pp: "<<i<<" "<<a[i].l<<" "<<a[i].r<<endl;
}
if(n == 1) {
cout<< a[1].l;
return 0;
}
if(a[2].l >= a[1].r){
b[2] = a[2].l, b[1] = a[1].r;
}
else if(a[2].r < a[1].l){
b[2] = a[2].r, b[1] = a[1].l;
}
else if(a[2].r <= a[1].r)
b[2] = a[2].r, b[1] = a[2].r;
else if(a[2].l >= a[1].l)
b[2] = a[2].l, b[1] = a[2].l;
// cout<<"b: "<<b[1]<<" "<<b[2]<<endl;
for(int i = 3; i <= n; i++){
if(a[i].l <= b[i-1] && a[i].r >= b[i-1])
b[i] = b[i-1];
else if(a[i].l > b[i-1])
b[i] = a[i].l;
else if(a[i].r < b[i-1])
b[i] = a[i].r;
}
// cout<<"b: "<<b[3]<<endl;
for(int i = 1; i <= n; i++){
ll y = b[i] - b[i-1];
if(y > 0) ans += y;
}
cout<<ans<<endl;
return 0;
}
稻田
题目描述 2.5s 512MB
小明种了\(n\)颗水稻,它们排成一排。
由于这\(n\)颗水稻的品种不一样,所以它们需要灌溉的水量也不一样,具体地:第\(i\)颗水稻需要被灌溉\([a_i,b_i]\)单位之间的水,多了会淹死,少了会旱死。
小明每次可以给一个区间\([L_i,R_i]\)浇水,这会使得这个区间每颗水稻的水量\(+1\)。
小明决定一共浇水\(m\)次。
问:小明有多少种方法浇水,可以使得每一颗水稻都能得到需要的水。
输入格式
第一行输入\(n,m\)。
第二行输入\(n\)个数字表示\(a_1,...,a_n\)。
第三行输入\(n\)个数字表示\(b_1,...,b_n\)。
输出格式
输出一个数字代表答案\(\mod 998244353\)。
样例输入 #1
2 2
1 1
2 2
样例输出 #1
7
样例解释 #1
每次浇水有三种选择:\([1,1],[2,2],[1,2]\)。所以一共有\(9\)种方案。
只要两次不同时是\([1,1]\)或者\([2,2]\),就是合法的方案。
所以一共有\(7\)种合法方案。
样例输入 #2
3 2
1 1 1
2 2 2
样例输出 #2
17
样例解释 #2
每次浇水一共有\(6\)种选择。
假设第一次是\([1,3]\),那么后续\(6\)种情况都合法。
假设第一次是\([1,2]\),那么后续包含\(3\)的\(3\)种情况都合法。
假设第一次是\([2,3]\),那么后续包含\(1\)的\(3\)种情况都合法。
假设第一次是\([1,1]\),那么后续\([1,3],[2,3]\)都合法。
假设第一次是\([3,3]\),那么后续\([1,3],[1,2]\)都合法。
假设第一次是\([2,2]\),那么后续只有\([1,3]\)合法。
共\(6+3+3+2+2+1=17\)种方案。
样例输入 #3
10 15
1 2 1 1 3 2 1 1 1 5
2 5 3 4 5 8 6 4 2 6
样例输出 #3
216644318
样例输入 #4
100 300
11 16 17 15 19 17 15 15 14 16 12 13 11 14 19 9 13 18 17 16 17 11 13 13 17 17 19 18 13 12 16 16 19 23 13 15 16 13 19 13 11 12 16 23 14 8 13 19 13 13 29 12 15 16 10 9 12 13 11 14 15 19 11 22 23 20 8 20 10 13 17 18 16 21 15 12 22 13 9 13 15 11 19 12 16 22 14 13 14 12 14 15 13 18 19 15 10 23 11 8
281 283 288 283 287 287 285 281 287 283 291 291 290 288 286 282 281 291 286 289 279 281 288 286 290 287 283 284 285 283 287 278 283 276 281 282 280 290 290 279 287 293 286 284 277 286 287 288 284 286 287 291 291 285 291 287 286 282 287 286 286 284 284 282 284 280 282 282 279 282 284 284 292 284 285 284 278 284 288 281 284 282 289 289 292 291 284 287 280 290 280 280 286 284 286 279 285 289 289 286
样例输出 #4
577583272
数据范围
对于5%的数据:\(l_i=0,r_i=m\)。
对于另15%的数据:\(n,m\leq 5\)。
对于另10%的数据:\(n\leq 100,m=2\)。
对于另15%的数据:\(n\leq 50,m=3\)。
对于另25%的数据:\(n,m\leq 50\)。
对于另15%的数据:\(n,m\leq 100\)。
对于100%的数据:\(n\leq 100,m\leq 300,0\leq l_i,r_i\leq m\)。
#include<bits/stdc++.h>
#define maxn 305
#define ll long long
#define mod 998244353
using namespace std;
ll C[maxn][maxn];
void init()
{
C[0][0]=1;
for (int i=1;i<maxn;i++)
for (int j=0;j<=i;j++)
{
if (i==1 || j==0) C[i][j]=1;
else C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
}
int n,m;
int l[maxn],r[maxn];
ll dp1[105][maxn][maxn],dp2[105][maxn][maxn];
void add(ll &x,ll y) {x=(x+y)%mod; return;}
int main()
{
init();
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>l[i];
for (int i=1;i<=n;i++) cin>>r[i];
dp2[0][0][0]=1; //dp2表示考虑完()的结果
for (int i=0;i<=n;i++)
{
//首先转移dp1到dp2,再转移dp2到下一个dp1
for (int j=0;j<=m;j++)
for (int k=l[i];k<=r[i];k++) //左括号个数
if (dp1[i][j][k])
{
for (int t=0;t<=k;t++) //在此处添加t个右括号
{
add(dp2[i][j][k-t],dp1[i][j][k]*C[k][t]);
}
}
for (int j=0;j<=m;j++)
for (int k=0;k<=j;k++) //左括号个数
if (dp2[i][j][k])
{
//在i+1处添加t个左括号,
//需要满足0<=t+k<=j以及l[i+1]<=t+k<=r[i+1]
for (int t=max(0,l[i+1]-k);t<=min(m-j,r[i+1]-k);t++)
{
add(dp1[i+1][j+t][k+t],dp2[i][j][k]*C[m-j][t]);
}
}
}
cout<<dp2[n][m][0]<<endl;
}
把每一个区间想象成一对括号,在每一个位置如果有多个左括号,实际上他们是不一样的左括号,因为顺序是不一样的。