A.
解
注意,每种颜色仅出现一次。
因此树状数组维护所有操作的左右端点, \(答案=目前已完成的操作数-[1,当前操作左端点-1]中的右端点个数-[当前操作右端点+1,n]中的左端点个数\) 。
Code
#include
using namespace std;
const int maxn=100003;
int n,Q,tot;
struct BIT{
int t[maxn];
BIT(){memset(t,0,sizeof(t));}
void add(int pos,int k){while(pos<=n)t[pos]+=k,pos+=pos&-pos;}
int query(int pos){int ret=0;while(pos)ret+=t[pos],pos-=pos&-pos;return ret;}
int query(int l,int r){return l<=r?query(r)-query(l-1):0;}
}treel,treer;
int main(){
scanf("%d%d",&n,&Q);
while(Q--){
int mo,l,r;
scanf("%d%d%d",&mo,&l,&r);
if(mo==1){
treel.add(l,1);
treer.add(r,1);
tot++;
}
else{
printf("%d\n",tot-treer.query(1,l-1)-treel.query(r+1,n));
}
}
return 0;
}
B.
解
直接背包,每次转移维护前 \(k\) 大的值。
Code
#include
using namespace std;
const int maxk=53,maxv=5003,maxn=203;
int k,v,n,V[maxn],A[maxn];
struct T{
int a[maxk];
T(){memset(a,0,sizeof(a));}
T merge(const T &t,int val){
T ret;
int i=1,j=1;
for(;i<=*a&&j<=*t.a&&*ret.at.a[j]+val){
ret.a[++*ret.a]=a[i];
i++;
}
else{
ret.a[++*ret.a]=t.a[j]+val;
j++;
}
}
while(i<=*a&&*ret.a=V[i];j--){
dp[j]=dp[j].merge(dp[j-V[i]],A[i]);
}
}
int ans=0;
for(int i=1;i<=k;i++)ans+=dp[v].a[i];
printf("%d\n",ans);
return 0;
}
C.
解
如果你只设一个dp数组,这道题会变得比较困难
设 \(dp1[i][j]\) 表示拿到前 \(i\) 种物品施 \(j\) 次魔法所需最小成本, \(dp2[i][j]\) 表示拿到第 \(i\) 种物品施 \(j\) 次魔法所需最小成本, \(dp[i][j]\) 表示花 \(i\) 个金币施 \(j\) 次魔法所能获得的最大利润。
转移见下。
Code
#include
using namespace std;
const int maxn=1003,INF=1050000000;
int n,m,V,K,a[maxn],b[maxn],dp1[maxn][maxn],dp2[maxn][maxn],dp[maxn][maxn];
vector > h[maxn];
int main(){
scanf("%d%d%d%d",&n,&m,&V,&K);
for(int i=1;i<=n;i++)scanf("%d%d",a+i,b+i);
for(int i=1;i<=m;i++){
int x,k;
scanf("%d%d",&x,&k);
h[x].push_back(vector(k));
for(int j=0;j