D. The Strongest Build
题意:
给你
n
n
n个位置(
n
<
=
10
n<=10
n<=10),每个位置有
c
i
ci
ci个备选的数(
c
i
<
=
200000
ci<=200000
ci<=200000),然后有
m
m
m个组合(
m
<
=
200000
m<=200000
m<=200000),求除了这些组合之外的 和最大的组合,输出最大和。
思路 一:
根据
m
m
m个限制,建一颗字典树,然后遍历字典树,在当前点,保证取当前点到根的数,下面的位置贪心取,遍历完字典树之后的最大值即是答案。用线段树来维护下面位置的贪心取法。
代码:
代码较复杂,找错误找了一天,而且字典树也写烂了…哭了,不过AC了就证明比赛中也能实现。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m;
int num[12];
struct node{
int id,w;
}z[11][200004];
bool cmp(node a,node b){
return a.w>b.w;
}
int tot=0;
int pre[12];
int id[11][200004];
struct {
map<int,int>mp;
vector<int>v;
}trie[2000005];
int tr[11][800006];
int ans=0;
stack<int>sta,ans1;
int ff=-1;
void build(int id,int p,int l,int r){
tr[id][p]=0;
if(l==r)return ;
int mid=l+r>>1;
build(id,2*p,l,mid);
build(id,2*p+1,mid+1,r);
}
void update(int id,int p,int l,int r,int x,int y,int w){
if(l==r){
tr[id][p]+=w;
return ;
}
int mid=l+r>>1;
if(x<=mid)update(id,2*p,l,mid,x,y,w);
else update(id,2*p+1,mid+1,r,x,y,w);
tr[id][p]=tr[id][2*p]+tr[id][2*p+1];
}
int query(int id,int p,int l,int r){
if(l==r){
if(tr[id][p]==0)return l;
else return -1;
}
int mid=l+r>>1;
if(tr[id][2*p]!=mid-l+1)return query(id,2*p,l,mid);
else return query(id,2*p+1,mid+1,r);
}
void dfs(int x,int y,int w,int id){
if(x!=0)update(x,1,1,num[x],y,y,1);
if(x!=0&&x!=n)sta.push(y);
if(x==n)return;
for(int i=0;i<trie[id].v.size();i++){
int too=trie[id].v[i];
int to=trie[id].mp[too];
dfs(x+1,too,w+z[x][y].w,to);
}
int fla=query(x+1,1,1,num[x+1]);
if(fla!=-1){
if(w+z[x+1][fla].w+pre[x+2]+z[x][y].w>ans){
ans=w+z[x+1][fla].w+pre[x+2]+z[x][y].w;
ans1=sta;
ff=fla;
}
}
if(x!=0)sta.pop();
for(int i=0;i<trie[id].v.size();i++){
int too=trie[id].v[i];
int to=trie[id].mp[too];
update(x+1,1,1,num[x+1],too,too,-1);
}
}
int main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin>>n;
z[0][0].w=0;
for(int i=1;i<=n;i++){
cin>>num[i];
for(int j=1;j<=num[i];j++){
cin>>z[i][j].w;
z[i][j].id=j;
}
sort(z[i]+1,z[i]+1+num[i],cmp);
for(int j=1;j<=num[i];j++){
id[i][z[i][j].id]=j;
}
}
pre[n+1]=0;
for(int i=n;i>=1;i--)pre[i]=pre[i+1]+z[i][1].w;
cin>>m;
for(int i=1;i<=m;i++){
int a=0,prre=0;
for(int j=1;j<=n;j++){
cin>>a;
if(!trie[prre].mp.count(id[j][a])){
trie[prre].v.push_back(id[j][a]);
tot++;
trie[prre].mp[id[j][a]]=tot;
}
prre=trie[prre].mp[id[j][a]];
}
}
for(int i=1;i<=n;i++){
build(i,1,1,num[i]);
}
dfs(0,0,0,0);
stack<int>q;
int siz=0;
while(!ans1.empty()){
int a=ans1.top();ans1.pop();
siz++;
q.push(a);
}
int cnt=0;
while(!q.empty()){
int a=q.top();q.pop();
cnt++;
cout<<z[cnt][a].id<<" ";
}
if(ff!=-1&&siz<n){
siz++;
cout<<z[siz][ff].id<<" ";
}
for(int i=siz+1;i<=n;i++)cout<<z[i][1].id<<" ";
return 0;
}
思路 二:
题目可以看成,求枚举前K大,并且没有被ban掉,用map+priority_queue 和 vector 维护一个轮廓线,写个类似bfs即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int n,m;
int num[12];
struct node{
int id,w;
}z[11][200004];
struct nod{
vector<int>v;
int sum;
bool operator <(const nod & ths)const{
return sum<ths.sum;
}
};
priority_queue<nod>q;
map<vector<int>,int>mp1;
map<vector<int>,int>mp2;
vector<int>vv;
bool cmp(node a,node b){
return a.w>b.w;
}
int id[11][200005];
void bfs(){
vv.clear();
int sum=0;
for(int i=1;i<=n;i++)vv.push_back(1),sum+=z[i][1].w;
q.push({vv,sum});
while(!q.empty()){
nod x=q.top();q.pop();
if(!mp1.count(x.v)){
for(int i=0;i<x.v.size();i++){
cout<<z[i+1][x.v[i]].id<<" ";
}
return ;
}
for(int i=0;i<x.v.size();i++){
if(x.v[i]==num[i+1])continue;
int sum=x.sum;
sum-=z[i+1][x.v[i]].w;
x.v[i]++;
sum+=z[i+1][x.v[i]].w;
if(!mp2.count(x.v)){
mp2[x.v]=1;
q.push({x.v,sum});
}
x.v[i]--;
}
}
}
int main(){
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>num[i];
for(int j=1;j<=num[i];j++){
cin>>z[i][j].w;
z[i][j].id=j;
}
sort(z[i]+1,z[i]+1+num[i],cmp);
for(int j=1;j<=num[i];j++){
id[i][z[i][j].id]=j;
}
}
cin>>m;
for(int i=1;i<=m;i++){
vv.clear();
for(int j=1;j<=n;j++){
int a;cin>>a;
vv.push_back(id[j][a]);
}
mp1[vv]=1;
}
bfs();
return 0;
}