C:
这类题目还是有比较统一的做法:首先对于区间覆盖这个操作,我们可以利用差分来思考:
如:[l,r]加1,变为:a[l]++,a[r+1]--然后我们在最后从头往后扫描一次就变为整体被覆盖的次数了,这种做法还可以利用到树上路径+1
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 1e6 + 10;
int a[maxn];
int n, m;
signed main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int op;
cin >> op;
if (op == 1)
{
int x, y;
cin >> x >> y;
a[x]++, a[y + 1]--;
}
else if (op == 2)
{
int x;
cin >> x;
a[x]++, a[n + 1]--;
}
else
{
int x;
cin >> x;
a[1]++, a[x + 1]--;
}
}
for (int i = 1; i <= n; i++)
{
a[i] += a[i - 1];
}
int maxx = 0;
for (int i = 1; i <= n; i++)
{
maxx = max(maxx, m-a[i]);
}
cout << maxx << ' ';
int cnt = 0;
for (int i = 1; i <= n; i++)
{
if (m - a[i] == maxx)
{
cnt++;
}
}
cout << cnt;
}
D:
这里是两种算法的叠加,对于第一种我们可以比较快速的想到双指针,对于第二个我们可以利用倍增来处理,复杂度过的去,不过题目有要求最多n个,要转移l,r的长度
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const int maxn = 4e5 + 10;
int a[maxn],b[maxn];
int f[maxn][32];
int n, m;
void st()
{
for (int i = 1; i <2*n; i++)
f[i][0] = b[i];
int k = log2(n);
for (int j = 1; j <= k; j++)
{
for (int i = 1; i <= 2*n- (1ll << j) ; i++)
{
f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}
}
}
int find_max(int l, int r)
{
int k = log2(r - l + 1);
return max(f[l][k], f[r - (1ll << k) + 1][k]);
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
{ cin >> a[i];
a[i + n] = a[i];
}
}
for (int i = 1; i <= n; i++)
{ cin >> b[i];
b[i + n] = b[i];
}
int ans = 1e18;//设为1e9会错
st();
int l = 1, r = 1;
int sum =0;
while (l <=n && r <=2*n){
while (sum <m&&r<=2*n){
sum += a[r];
r++;
}
if (sum >= m&&r-l<=n){//保证长度正确
ans = min(ans, find_max(l, r-1));
}
else if(sum<m){
break;
}
sum -= a[l++];
}
if(ans!=1e18)
cout << ans;
else
{
cout << -1;
}
}
E:
我们需要根据我们已知的数来与之前的数进行匹配,这种体型还是比较常见的,通常都是利用线段树将前面出现的数来标记,再查找,因为没法离散化,我们复杂度为 0(1e6*log2*(1e9))可以卡过去 :多交几次就好
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define int long long
const int maxn = 1e6 + 10;
const int mod = 998244353;
struct node
{
int l, r;
int val;
}tree[maxn*40];
int tot, n, l, r,root;
void pushdown(int rt){
tree[rt].val = tree[tree[rt].l].val + tree[tree[rt].r].val;
tree[rt].val%=mod;
}
void modify(int&rt, int q, int p, int l, int r){
if (!rt)
rt = ++tot;
if (l == r)
{
tree[rt].val += p;
tree[rt].val%=mod;
return;
}
int mid = l + r >> 1;
if (q <= mid)
modify(tree[rt].l, q, p, l, mid);
else
modify(tree[rt].r, q, p, mid + 1, r);
pushdown(rt);
}
int query(int&rt, int ql, int qr, int l, int r){
if (!rt)
return 0;
if (ql == l && qr == r)
return tree[rt].val;
int mid = l + r >> 1;
if (qr <=mid)
{
return query(tree[rt].l, ql, qr, l, mid);
}
else if (ql > mid)
{
return query(tree[rt].r, ql, qr, mid + 1, r);
}
else
{
int ret = query(tree[rt].l, ql, mid, l, mid);
ret += query(tree[rt].r, mid + 1, qr, mid + 1, r);
return ret%mod;
}
}
int a[maxn];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int m;
cin >> n >> m;
int ans = 0;
int len = 1e9 + 10;
for (int i = 1; i <= n; i++){
int k; cin >> k;
int cnt = 0;
for (int j = 1; j <= k; j++)
{
int x; cin >> x; a[++cnt] = x;
if (x >= m)
ans = (ans + query(root, 1, len, 1, len)) % mod;
else
ans = (ans + query(root, m - x, len, 1, len)) % mod;
}
for (int j = 1; j <= k; j++)
{
modify(root, a[j], 1, 1, len);
}
}
cout << ans;
}
F:
我们设dp[i][j]代表前i个人造成伤害为j的方案数,f[i]代表这个人利用i能量最多可以造成多大伤害(因为我们的方案为至少一个人的能量不同)可以这样理解:因为我们的最后组合假如仅看每个人利用的能量来看的话,一个人能量变化导致方案数增加当前仅当变为不同的能量,这个过程就像每个人(这个人有不同的能量消耗组合)任意选取能量消耗,这个组合满足条件
写法上有很多坑:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
const int maxn = 3e3 + 10;
const int mod = 998244353;
int dp[maxn][maxn];
int f[maxn];
int n, m;
int a[maxn], b[maxn];
signed main()
{
cin >> n >> m;
dp[0][0] = 1;//方案数的基本要求
for (int i = 1; i <= n; i++){
int len;
cin >> len;
for (int j = 1; j <= len; j++)cin >> a[j];
for (int j = 1; j <= len; j++)cin >> b[j];
memset(f, 0, sizeof f);
for (int j = 1; j <= len; j++){
for (int k = 3000; k >= b[j]; k--){
if (f[k - b[j]]|| k == b[j])//满背包的做法,也可以将初始变为负inf,f[0]=0来转移
f[k] = max(f[k], f[k - b[j]] + a[j]);
}
}
//利用f[]来转移
for (int j = 0; j <=3000; j++){
if (f[j] == 0&&j!=0)//利用0来将前i-1的法案数转移到i
continue;
if (f[j] > m)f[j] = m;//保证不会溢出
for (int k =0;k<f[j];k++)
dp[i][m] = (dp[i][m] + dp[i - 1][m - k])%mod;//由于dp[i][m]不太一样此时m代表不低于>m
for (int k =m; k >= f[j]; k--){//>=
dp[i][k] = (dp[i][k] + dp[i - 1][k - f[j]])%mod;
}
}
}
cout << dp[n][m];
}