noip模擬31 solutions
我就覺得這些考試題是越考越難,我是也越考越完蛋,已經完完全全的接近爆零了
只有20pts,說真的這還是我第一次掛掉30pts,本來我還有50pts嘞
所以這次考試我直接炸裂,改題的時候也不是非常的順利,一直不知道該咋辦
但是我得到了一個經驗,線段樹真的是個好東西,得好好的利用
T1 game
這個題真的快要氣死我了,我在考場上一秒切掉最優解
一個小時沒想出來字典序,真是無奈。。。。。
最後看題解的時候發現這個直接線段樹維護就好了
維護啥呢?維護這個區間內所有的a,b一共可以造成的貢獻是多少
每次二分這個不影響貢獻的邊界值,可能不太好理解
就是你發現在兩個序列中,只要b大於a就好了,所以我們在權值線段樹中在相應的位置的相應的序列中插入1
這樣我們每次pushup的時候就可以維護出每個區間的貢獻
我們按順序便利a陣列,首先刪掉當前的a,去線段樹中尋找不影響總貢獻的最大值
這個肯定是單調的,因為你區間內的數越多,獲得貢獻的可能性越大
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
int n,a[N],b[N];
struct XDS{
#define ls x<<1
#define rs x<<1|1
int sum[N*4],dw[N*4],up[N*4];
void pushup(int x){
int tmp=min(dw[ls],up[rs]);
sum[x]=sum[ls]+sum[rs]+tmp;
dw[x]=dw[ls]+dw[rs]-tmp;
up[x]=up[ls]+up[rs]-tmp;
return ;
}
void ins(int x,int l,int r,int pos,int v1,int v2){
//cout<<x<<" "<<l<<" "<<r<<" "<<pos<<endl;
if(l==r){
dw[x]+=v1;
up[x]+=v2;
return ;
}
int mid=l+r>>1;
if(pos<=mid)ins(ls,l,mid,pos,v1,v2);
else ins(rs,mid+1,r,pos,v1,v2);
pushup(x);return ;
}
#undef ls
#undef rs
}xds;
int mx,sum;
multiset<int> st;
signed main(){
scanf("%d",&n);
for(re i=1;i<=n;i++)scanf("%d",&a[i]),mx=max(mx,a[i]);
for(re i=1;i<=n;i++)scanf("%d",&b[i]),mx=max(mx,b[i]);
//cout<<mx<<endl;
for(re i=1;i<=n;i++)xds.ins(1,1,mx,a[i],1,0);
for(re i=1;i<=n;i++)xds.ins(1,1,mx,b[i],0,1),st.insert(b[i]);
int sum=xds.sum[1];
for(re i=1;i<=n;i++){
//cout<<i<<endl;
xds.ins(1,1,mx,a[i],-1,0);
int l=a[i]+1,r=*st.rbegin(),mid;
//cout<<l<<" "<<r<<" ";
while(l<r){
//cout<<l<<" "<<r<<endl;
mid=l+r+1>>1;
xds.ins(1,1,mx,mid,0,-1);
if(1+xds.sum[1]==sum)l=mid;
else r=mid-1;
xds.ins(1,1,mx,mid,0,1);
}
//cout<<i<<" sb"<<" ";
xds.ins(1,1,mx,l,0,-1);
if(l<=r&&1+xds.sum[1]==sum){
printf("%d ",l);
sum--;
st.erase(st.find(l));
}
else{
xds.ins(1,1,mx,l,0,1);
l=1;r=a[i];
while(l<r){
//cout<<l<<" "<<r<<endl;
mid=l+r+1>>1;
xds.ins(1,1,mx,mid,0,-1);
if(xds.sum[1]==sum)l=mid;
else r=mid-1;
xds.ins(1,1,mx,mid,0,1);
}
xds.ins(1,1,mx,l,0,-1);
printf("%d ",l);
st.erase(st.find(l));
}
//cout<<endl;
}
}
T2 time
這個考場上其實就差一步就好了,逆序對沒用對
所以這個題就爆掉了30pts
那這個題的正解也是線段樹(其實我是看著std的雙端佇列不爽才寫線段樹的)
最小值一定在兩端,所以每次只需要將這個最小值向左右兩邊移動就好了
你發現這個最小值的移動並不會對比他大的值造成任何影響
我們只需要用線段樹維護左邊的最小值以及距離,右邊一樣
因為這裡會出現相同的數字,所以左右兩邊要分別維護
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
const int inf=0x3f3f3f3f;
int n,a[N],ans;
struct XDS{
#define ls x<<1
#define rs x<<1|1
int lmn[N*4],lrk[N*4],rmn[N*4],rrk[N*4];
int siz[N*4];
void pushup(int x){
if(a[lmn[ls]]<=a[lmn[rs]]){
lmn[x]=lmn[ls];
lrk[x]=lrk[ls];
}
else{
lmn[x]=lmn[rs];
lrk[x]=lrk[rs]+siz[ls];
}
if(a[rmn[rs]]<=a[rmn[ls]]){
rmn[x]=rmn[rs];
rrk[x]=rrk[rs];
}
else{
rmn[x]=rmn[ls];
rrk[x]=rrk[ls]+siz[rs];
}
siz[x]=siz[ls]+siz[rs];
return ;
}
void build(int x,int l,int r){
if(l==r){
lmn[x]=rmn[x]=l;
lrk[x]=rrk[x]=1;
siz[x]=1;
return ;
}
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(x);return ;
}
void del(int x,int l,int r,int pos){
//cout<<x<<" "<<l<<" "<<r<<" "<<pos<<endl;
if(l==r){
lmn[x]=rmn[x]=0;
lrk[x]=rrk[x]=inf;
siz[x]=0;
return ;
}
int mid=l+r>>1;
if(pos<=mid)del(ls,l,mid,pos);
else del(rs,mid+1,r,pos);
pushup(x);return ;
}
#undef ls
#undef rs
}xds;
signed main(){
scanf("%d",&n);
a[0]=inf;
for(re i=1;i<=n;i++)
scanf("%d",&a[i]);
xds.build(1,1,n);
for(re i=1;i<=n;i++){
//cout<<i<<" "<<xds.lmn[1]<<" "<<xds.rmn[1]<<endl;
if(xds.lrk[1]<=xds.rrk[1]){
ans+=xds.lrk[1]-1;
xds.del(1,1,n,xds.lmn[1]);
}
else{
ans+=xds.rrk[1]-1;
xds.del(1,1,n,xds.rmn[1]);
}
//cout<<"sB"<<endl;
}
printf("%d",ans);
}
T3 cover
這個最簡單了,看到包含和不相交,一眼就是樹形dp
可是我不會打,考完了直接一個\(\mathcal{O(n^2)}\)的暴力轉移拿到45
這個差分的確是個好東西;
直接把每一個權值插入到對應的節點的multiset中
我們每次讓輕鏈合併到重鏈,因為一顆樹上的輕鏈包含的節點總個數就是n
所以合併的總複雜度是\(\mathcal{O(nlogn)}\)
這個差分其實就是對一個函式進行做差,
我們將這個二維的轉移陣列,每個節點當作一個函式,那麼他的差分一定是單調不增的
為啥,因為最大的放在前面一定最優
AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=3e5+5;
int n,m;
struct node{
int l,r;
ll a;
node(){}
bool operator < (node x)const{
if(l!=x.l)return l<x.l;
return r>x.r;
}
}sca[N];
int to[N],nxt[N],head[N],rp;
int du[N];
void add_edg(int x,int y){
to[++rp]=y;
nxt[rp]=head[x];
head[x]=rp;
}
int sta[N],tot;
bool check(int x,int y){
return sca[x].l<=sca[y].l&&sca[y].r<=sca[x].r;
}
multiset<ll> f[N];
void merge(multiset<ll> &x,multiset<ll> &y){
if(x.size()<y.size())swap(x,y);
vector<ll> tmp;tmp.clear();
multiset<ll>::iterator it=y.begin();
while(it!=y.end()){
//cout<<"sb"<<endl;
tmp.push_back(*it+*x.begin());
x.erase(x.begin());
it=next(it);
}
for(re i=0;i<tmp.size();i++)x.insert(tmp[i]);
}
void dfs(int x){
for(re i=head[x];i;i=nxt[i]){
int y=to[i];dfs(y);
merge(f[x],f[y]);
//cout<<x<<" "<<y<<" "<<"ok"<<endl;
}
if(x==m+1)return ;
f[x].insert(-sca[x].a);
}
signed main(){
scanf("%d%d",&n,&m);
for(re i=1;i<=m;i++)scanf("%d%d%lld",&sca[i].l,&sca[i].r,&sca[i].a);
sort(sca+1,sca+m+1);
sta[tot=1]=m+1;
for(re i=1;i<=m;i++){
while(tot>1&&!check(sta[tot],i)){
add_edg(sta[tot-1],sta[tot]);
du[sta[tot]]++;tot--;
}
sta[++tot]=i;
}
tot--;
while(tot){
add_edg(sta[tot],sta[tot+1]);
du[sta[tot+1]]++;tot--;
}
dfs(m+1);
//cout<<"sb"<<endl;
ll ans=0;
//cout<<"sb"<<" "<<(*f[m+1].begin())<<endl;
for(re i=1;i<=m;i++){
//cout<<i<<" ";
if(f[m+1].size()){
ans+=-(*f[m+1].begin());
f[m+1].erase(f[m+1].begin());
}
printf("%lld ",ans);
}
}