ABC306
A
略
B
题意:
给定一个01序列,如果是1,答案加上以它下标为次数,2为底数的值
思路:
需要注意long long最大值为2^63 -1,也就是说如果遇到2^63就歇菜了
考虑用ull,或者__int128存储
其中__int128无法用标准的输入和输出,但是可以正常的运算
C
模拟即可,用pii存答案,下标放在右键排序
void solve(){
int n;cin>>n;
vector<int>a(3*n+1);
vector<int>cnt(n+1);
vector<pii>ans(n+1);
rep(i,1,n)ans[i].se=i;
rep(i,1,3*n){
cin>>a[i];
cnt[a[i]]++;
if(cnt[a[i]]==2)ans[a[i]].fi=i;
}
sort(ans.begin()+1,ans.end());
rep(i,1,n){
cout<<ans[i].se<<' ';
}cout<<endl;
}
D
题意:
给定n个Xi和Yi,分别代表n个菜肴的两种属性
从左至右,每个菜肴可以吃或者不吃
如果吃下去,答案可以获得该菜肴的Y
有毒的菜肴吃后立即获得中毒状态,此时不可以再次吃下有毒的菜肴,吃下没毒的菜肴可以解毒
没毒的菜肴可以随便吃
问最后的答案最大为多少
思路:
经典线性DP
设计状态,f[i]j
考虑转移
当遇到有毒菜肴时,可以 健康->中毒(吃),中毒->中毒(不吃)也可以健康-> 健康(不吃)
当遇到健康菜肴时, 可以 健康->健康(吃或不吃) 中毒->中毒(不吃) 中毒->健康(吃)
int f[maxn][2];
void solve(){
int n;cin>>n;
rep(i,1,n){
int x,y;cin>>x>>y;
if(x==1){
f[i][1]=max(f[i-1][0]+y,f[i][1]);
f[i][1]=max(f[i-1][1],f[i][1]);
f[i][0]=max(f[i-1][0],f[i][0]);
}else{
f[i][0]=max(f[i-1][0]+y,f[i][0]);
f[i][0]=max(f[i-1][1]+y,f[i][0]);
f[i][0]=max(f[i-1][0],f[i][0]);
f[i][1]=max(f[i-1][1],f[i][1]);
}
}
cout<<max(f[n][0],f[n][1])<<endl;
}
E
题意:
给定一个长度为n的初始为全0的序列
有q次操作,每次给出下标和数值,代表将该下标的数组值换成给定的数值
每次操作后,输出序列中前K大的元素之和
思路:
好像是数据结构题
可以用两个multiset解决
一个用来存前K大的数组值,另一个存剩下来的数组值
用一个变量记录答案
每次操作用multiset的find操作查找该位置的数组值是否在存前K大的multiset里面
若是,答案需要先减
先将原先的数组值移除
考虑插入到哪一个multiset中
显然当比其余元素的最大值还大时,要插入到前k大的multiset,并更新答案
否则插入其余元素的multiset
需要维护两个multiset大小分别是k,n-k
不满足条件时按需要移动
multiset<int>a,b;
//a维护序列中前K大的元素
//b维护序列中剩余的元素(即前n-k小的元素)
void solve(){
int n,k,q;cin>>n>>k>>q;
int ans=0;
vector<int>t(n+1);
rep(i,1,k){
a.insert(0);
}
rep(i,1,n-k){
b.insert(0);
}
while(q--){
int x,y;cin>>x>>y;
if(a.find(t[x])!=a.end()){
a.erase(a.find(t[x]));
ans-=t[x];
}else{
b.erase(b.find(t[x]));
}
t[x]=y;
if(b.size()&&y>=*b.rbegin()){
a.insert(y);
ans+=y;
}else b.insert(y);
if(a.size()<k){
a.insert(*b.rbegin());
ans+=*b.rbegin();
b.erase(b.find(*b.rbegin()));
}
if(a.size()>k){
b.insert(*a.begin());
ans-=*a.begin();
a.erase(a.begin());
}
cout<<ans<<endl;
}
}
F
题意:
给定一系列没有相同元素的集合,规定f(A,B)为集合A和集合B中元素在同一序列中排序后,集合A元素的下标之和
求f(A,B)之和(比如共有四个集合a,b,c,d,则需要求f(a,b)+f(a,c)+f(a,d)+f(b,c)+f(b,d)+f(c,d))
思路:
显然一个一个枚举集合并且合并再模拟操作时间复杂度不行
考虑每个集合它对于答案的贡献
显然一个f的答案为 一个集合的每一个元素,比它小的元素数量+1 的和
那么一个集合的f的和,分为 1对它的贡献 + 它自身元素对它的贡献 + 其余元素对它的贡献
每一个f:
1对它的贡献就是m,因为它的元素个数有m个
它自身元素对它的贡献为m(m-1)/2,因为下标求和相当于等差数列求和
因此对于f和:它俩的贡献为(n-i)x(m(m+1)/2)
其余元素对它的贡献为每个数比它小的元素数量之和,可以用树状数组(离散化)维护
从后往前遍历统计答案即可
int tr[maxn];
int n,m;
int lowbit(int x){
return x&-x;
}
void change(int p,int x){
while(p<=n*m){
tr[p]+=x;
p+=lowbit(p);
}
}
int query(int x){
int res=0;
while(x){
res+=tr[x];
x-=lowbit(x);
}
return res;
}
void solve(){
cin>>n>>m;
vector<vector<int>>a(n+1,vector<int>(m+1,0));
map<int,int>mp;
vector<int>k;
rep(i,1,n){
rep(j,1,m){
cin>>a[i][j];
k.pb(a[i][j]);
}
sort(a[i].begin()+1,a[i].end());
}
sort(k.begin(),k.end());
int len=unique(k.begin(),k.end())-k.begin();
int ans=0;
for(int i=0;i<len;i++)mp[k[i]]=i+1;
for(int i=n;i>=1;i--){
int res=(n-i)*m*(m+1)/2;
for(int j=m;j>=1;j--){
change(mp[a[i][j]],1);
ans+=query(mp[a[i][j]]-1);
}
ans+=res;
}
cout<<ans<<endl;
}

浙公网安备 33010602011771号