NOIP2022 做题笔记

由于本人NOIP2023做的太烂了,被教练拉去做NOIP2022了qwq
first hour:这t1看上去还行,先写了
second hour:t2看上去有些难度,让我想一想
third hour:快想出来了,先写一写吧
fourth hour:写写写写写.....
最后100pts遗憾离场......
赛后有了深刻的认识,很多题是不能一步到位的,只能拼暴力。

P8865 [NOIP2022] 种花

预处理+前缀和即可。
本人代码自带大常数,写的非常史。


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mod 998244353
#define mxn 1010
ll n,m,c,f,T,id,ans1,ans2,cnt1,cnt2;
ll nexti[mxn][mxn],nextj[mxn][mxn];
char s[mxn][mxn];
void solve(){
    cin>>n>>m>>c>>f;
    memset(nexti,0,sizeof(nexti));
    memset(nextj,0,sizeof(nextj));
    ans1=ans2=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            nexti[i][j]=n+1,nextj[i][j]=m+1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>s[i][j]; 
            if(s[i][j]=='1'){
                nexti[i][j]=i,nextj[i][j]=j;
                for(int k=i-1;k;k--){
                    if(s[k][j]=='0')nexti[k][j]=i;
                    else break;
                }
                for(int k=j-1;k;k--){
                    if(s[i][k]=='0')nextj[i][k]=j;
                    else break;
                }
            }
        }
    }   
    for(int i=1;i<m;i++){
        for(int j=1;j<=n;){
            if(s[j][i]=='1'){
                j++;continue;
            }
            cnt1=0,cnt2=0;
            for(int k=j+2;k<nexti[j][i];k++)
                cnt1+=nextj[k][i]-i-1,cnt2+=(nextj[k][i]-i-1)*(nexti[j][i]-k-1);
            for(int k=j;k<nexti[j][i]-2;k++){
                ans1=(ans1+(nextj[k][i]-i-1)*cnt1%mod)%mod;
                ans2=(ans2+(nextj[k][i]-i-1)*cnt2%mod)%mod;
                cnt1-=nextj[k+2][i]-i-1;
                cnt2-=(nextj[k+2][i]-i-1)*(nexti[j][i]-k-3);
            }
            j=nexti[j][i]+1;
        }
    }
    cout<<ans1*c<<' '<<ans2*f<<'\n';
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>T>>id;while(T--)solve();
    return 0;
}

P8866 [NOIP2022] 喵了个喵

较难的思维好题。
\(k=2\times n-2\) 的情况是送的。
\(k=2\times n-1\) 的情况考虑离线,对于多出来的数分类讨论。
具体可以看这篇题解,讲的非常牛逼。
另外就是这题细节及其多,要维护的东西及其多,再加上万恶的spj,更加深其史的程度。


#include<bits/stdc++.h>
using namespace std;
#define mxn 1010
#define mxm 2000010
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
int T,n,m,k,crd[mxm],empt;
int id[mxn];
vector<pii> ans;
deque<int> sta[mxn];
queue<int> q;
void init(){
    while(!q.empty())q.pop();
    for(int i=1;i<=n;i++)sta[i].clear();
    for(int i=1;i<n;i++)q.push(i),q.push(i);
    memset(id,0,sizeof(id));
    ans.clear(),empt=n;
}
bool _push(int x){
    if(!id[x]){
        if(q.empty())return 0;
        int u=q.front();q.pop();
        sta[u].pb(x),id[x]=u;
        ans.pb(mp(u,0));
    }
    else{
        int u=id[x];
        q.push(u),id[x]=0;
        if(sta[u].back()==x){
            sta[u].pop_back();
            ans.pb(mp(u,0));
        }
        else{
            sta[u].pop_front();
            ans.pb(mp(empt,0)),ans.pb(mp(u,empt));
        }
    }
    return 1;
}
void solve(){
    cin>>n>>m>>k;
    init();
    for(int i=1;i<=m;i++)cin>>crd[i];
    for(int i=1;i<=m;i++){
        if(_push(crd[i]))continue;//先不断插入
        //位置不够了
        int x=crd[i],pos=i+1;
        while(pos<=m&&crd[pos]!=x&&sta[id[crd[pos]]].back()==crd[pos])pos++;
        if(crd[pos]==x){//如果就是这个x
            ans.pb(mp(empt,0));//放到辅助栈
            for(int j=i+1;j<pos;j++)_push(crd[j]);//先自由插入
            ans.pb(mp(empt,0));//再消除
            i=pos;continue;
        }
        int cnt=0,a=crd[pos],b=sta[id[crd[pos]]].back();
        int p=id[crd[pos]];
        for(int j=i+1;j<pos;j++)if(crd[j]==b)cnt++;
        if(cnt%2==1){//是奇数
            ans.pb(mp(empt,0)),sta[empt].pb(x);
            //把x插到辅助栈里
            for(int j=i+1;j<pos;j++){
                if(crd[j]==b)ans.pb(mp(p,0));//是b就直接插到所在栈
                else _push(crd[j]);//不是就自由插入
            }
            q.push(empt),id[x]=empt,id[a]=id[b]=0;
            sta[p].clear();//更新
            ans.pb(mp(p,0)),empt=p;//把剩下的插到p里,更换辅助栈
        }
        else{//是偶数
            ans.pb(mp(p,0));//把x插到所在栈上
            for(int j=i+1;j<pos;j++){
                if(crd[j]==b)ans.pb(mp(empt,0));//其他的插到辅助栈
                else _push(crd[j]);
            }
            id[a]=0,id[x]=p;
            ans.pb(mp(empt,0)),ans.pb(mp(p,empt));
            sta[p].pop_front(),sta[p].pb(x);//更新
        }
        i=pos;
    }        
    cout<<ans.size()<<'\n';
    for(int i=0;i<ans.size();i++){
        if(!ans[i].second)
            cout<<"1 "<<ans[i].first<<'\n';
        else cout<<"2 "<<ans[i].first<<' '<<ans[i].second<<'\n';
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>T;while(T--)solve();
    return 0;
}

P8867 [NOIP2022] 建造军营

tarjan缩点+树形dp。
dp的初始化形如这样:

\[dp_{u,0}=2^{E_u} \qquad\qquad\,\\ dp_{u,1}=2^{E_u+V_u}-2^{E_u} \]

其中 \(E_u\) 为该边双连通分量的边数,\(V_u\) 为点数,\(dp_{u,0/1}\) 为对于每个连通分量,选或不选城市建造军营。
dp式形如这样:

\[dp_{u,1}=dp_{u,0}\times dp_{v,1}+dp_{u,1}\times(dp_{v,0}\times 2+dp_{v,1}\\ \, dp_{u,0}=dp_{u,0}\times dp_{v,0}\qquad\qquad\qquad\qquad\qquad\qquad \]

最后统计答案的时候,若 \(u\) 为根,则 \(ans=ans+dp_{u,1}\);若 \(u\) 不为根,则 \(ans=ans+dp_{u,1}\times 2^{s_{root}-s_u-1}\),其中 \(s_u\) 为以 \(u\) 为根的子树中边(缩点前的边)的数量。


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define mxn 500010
#define mxm 2000010
#define mod 1000000007
#define pb push_back
#define mp make_pair
#define pll pair<ll,ll>
ll n,m,cnt,ts;
ll dfn[mxn],low[mxn],sta[mxn],len,dcc[mxn];
ll vsum[mxn],esum[mxn];
ll dp[mxn][2],ans,mi2[mxm],s[mxn];
bool ins[mxn],vis[mxm];
vector<pll> t[mxn];
vector<int> g[mxn];
map<pll,bool> evis;
void init(){
    mi2[0]=1;
    for(int i=1;i<=m*2;i++)
        mi2[i]=(mi2[i-1]<<1)%mod;
}
void tarjan(int u,int f){
    dfn[u]=low[u]=++ts;
    for(int i=0;i<t[u].size();i++){
        int v=t[u][i].first,num=t[u][i].second;
        if(!dfn[v]){
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u])
                vis[num]=1,vis[u>v?num-1:num+1]=1;
        }
        else if(dfn[v]<dfn[u]&&v!=f)
            low[u]=min(low[u],dfn[v]);
    }
}
void dfs1(int u,int id){
    dcc[u]=id,vsum[id]++;
    for(int i=0;i<t[u].size();i++){
        int v=t[u][i].first,num=t[u][i].second;
        if(vis[num]&&dcc[v])
            g[id].pb(dcc[v]),g[dcc[v]].pb(id);
        else if(!vis[num]&&!dcc[v])dfs1(v,id);
    }
}
void dfs2(int u,int f){
    s[u]=esum[u];
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(v==f)continue;
        dfs2(v,u);
        s[u]+=s[v]+1;
    }
}
void dfs3(int u,int f){
    dp[u][0]=mi2[esum[u]];
    dp[u][1]=(mi2[vsum[u]+esum[u]]-mi2[esum[u]]+mod)%mod;
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(v==f)continue;
        dfs3(v,u);
        ll tot=dp[u][1];
        dp[u][1]=dp[u][0]*dp[v][1]%mod;
        dp[u][1]=(dp[u][1]+tot*((2*dp[v][0]%mod+dp[v][1])%mod)%mod)%mod;
        dp[u][0]=(dp[u][0]*(dp[v][0]*2%mod)%mod)%mod;
    }
    if(u==1)ans=(ans+dp[u][1])%mod;
    else ans=(ans+dp[u][1]*mi2[s[1]-s[u]-1]%mod)%mod;
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>m;
    init();
    for(int i=1;i<=m;i++){
        int u,v;cin>>u>>v;
        if(u==v)continue;
        if(u>v)swap(u,v);
        t[u].pb(mp(v,++cnt)),t[v].pb(mp(u,++cnt));
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])tarjan(i,0);
    cnt=0;
    for(int i=1;i<=n;i++)
        if(!dcc[i])dfs1(i,++cnt);
    for(int i=1;i<=n;i++)
        for(int j=0;j<t[i].size();j++){
            int v=t[i][j].first;
            if(dcc[i]==dcc[v])esum[dcc[i]]++;
        }
    for(int i=1;i<=cnt;i++)esum[i]/=2;
    dfs2(1,0),dfs3(1,0);
    cout<<ans<<'\n';
    return 0;
}

P8868 [NOIP2022] 比赛

极好的线段树高难度题。
考虑把询问挂线段树上,每次区间合并的时候,对于 \(a\)\(b\) 的最大值在左边还是右边分类讨论,然后大力合并。
合并时对于答案也要开两个线段树查,所以代码巨大难写,巨大难调。


#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
#define mxn 250010
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define X first
#define Y second
#define il inline
int T,n,q;
pii askk[mxn];
ll ans[mxn],sum1[mxn<<2],a[mxn],b[mxn],maxa[mxn],maxb[mxn];
vector<int> ask[mxn<<2],lft[mxn],rgt[mxn],total;
struct Seg{
    ll lazy[mxn<<2],sum2[mxn<<2],bsum[mxn<<2];
    il void push_up(int rot){
        sum2[rot]=sum2[rot<<1]+sum2[rot<<1|1];
        bsum[rot]=bsum[rot<<1]+bsum[rot<<1|1];
    }
    il void push_down(int rot){
        if(!lazy[rot])return;  
        sum2[rot<<1]+=lazy[rot]*bsum[rot<<1];
        sum2[rot<<1|1]+=lazy[rot]*bsum[rot<<1|1];
        lazy[rot<<1]+=lazy[rot],lazy[rot<<1|1]+=lazy[rot];
        lazy[rot]=0; 
    }
    il void build(int rot,int l,int r,int id){
        sum2[rot]=lazy[rot]=bsum[rot]=0;
        if(l==r){
            if(!id)bsum[rot]=1;
            else bsum[rot]=maxb[l];
            return;
        }
        int mid=(l+r)>>1;
        build(rot<<1,l,mid,id),build(rot<<1|1,mid+1,r,id);
        push_up(rot);
    }
    il void add(int rot,int l,int r,int x,int y,ll k){
        if(r<x||l>y)return;
        if(l>=x&&r<=y){
            sum2[rot]+=k*bsum[rot],lazy[rot]+=k;
            return;
        }
        push_down(rot);
        int mid=(l+r)>>1;
        add(rot<<1,l,mid,x,y,k),add(rot<<1|1,mid+1,r,x,y,k);
        push_up(rot);
    } 
    il ll query(int rot,int l,int r,int x,int y){
        if(l>=x&&r<=y)return sum2[rot];
        push_down(rot);
        int mid=(l+r)>>1;
        ll ret=0;
        if(x<=mid)ret+=query(rot<<1,l,mid,x,y);
        if(y>mid)ret+=query(rot<<1|1,mid+1,r,x,y);
        return ret;
    }
}s1,s2;
namespace solver{
    il void add(int rot,int l,int r,int x,int y,int id){
        if(l>=x&&r<=y){
            ask[rot].pb(id);
            return;
        }
        int mid=(l+r)>>1;
        if(x>mid)add(rot<<1|1,mid+1,r,x,y,id);
        else if(y<=mid)add(rot<<1,l,mid,x,y,id);
        else{
            ask[rot].pb(id);
            add(rot<<1,l,mid,x,y,id);
            add(rot<<1|1,mid+1,r,x,y,id);
        } 
    }
    il void clear(int l,int r){
        total.clear();
        for(int i=l;i<=r;i++)
            lft[i].clear(),rgt[i].clear();
    }
    il void init_max(int l,int r){
        int mid=(l+r)>>1;
        maxa[mid]=a[mid],maxb[mid]=b[mid];
        maxa[mid+1]=a[mid+1],maxb[mid+1]=b[mid+1];
        for(int i=mid-1;i>=l;i--)
            maxa[i]=max(maxa[i+1],a[i]),maxb[i]=max(maxb[i+1],b[i]);
        for(int i=mid+2;i<=r;i++)
            maxa[i]=max(maxa[i-1],a[i]),maxb[i]=max(maxb[i-1],b[i]);
    }
    il void solve(int rot,int l,int r){
        if(l==r){
            sum1[rot]=a[l]*b[l];
            for(int id:ask[rot])ans[id]+=sum1[rot];
            return;
        }
        int mid=(l+r)>>1;
        solve(rot<<1,l,mid);
        solve(rot<<1|1,mid+1,r);
        sum1[rot]=sum1[rot<<1]+sum1[rot<<1|1];
        clear(l,r);
        for(int id:ask[rot])
            if(askk[id].X<=l&&askk[id].Y>=r)total.pb(id);
            else lft[max(l,askk[id].X)].pb(id),rgt[min(r,askk[id].Y)].pb(id);
        init_max(l,r);
        s1.build(1,l,mid,0),s2.build(1,l,mid,1);
        for(int i=mid+1,j=mid+1,k=mid+1;i<=r;i++){
            while(maxa[j-1]<maxa[i]&&j>l)j--;
            while(maxb[k-1]<maxb[i]&&k>j)k--;
            if(k<=mid)s1.add(1,l,mid,k,mid,maxa[i]*maxb[i]);
            if(j<k)s2.add(1,l,mid,j,k-1,maxa[i]);
            for(int id:rgt[i]){
                int left=max(l,askk[id].X);
                ans[id]+=s1.query(1,l,mid,left,mid)+s2.query(1,l,mid,left,mid);
            }
        }
        sum1[rot]+=s1.query(1,l,mid,l,mid)+s2.query(1,l,mid,l,mid);
        s1.build(1,mid+1,r,0),s2.build(1,mid+1,r,1);
        for(int i=mid,j=mid,k=mid;i>=l;i--){
            while(maxa[j+1]<maxa[i]&&j<r)j++;
            while(maxb[k+1]<maxb[i]&&k<j)k++;
            if(k>mid)s1.add(1,mid+1,r,mid+1,k,maxa[i]*maxb[i]);     
            if(j>k)s2.add(1,mid+1,r,k+1,j,maxa[i]);
            for(int id:lft[i]){
                int right=min(r,askk[id].Y);
                ans[id]+=s1.query(1,mid+1,r,mid+1,right)+s2.query(1,mid+1,r,mid+1,right);
            }
        }
        sum1[rot]+=s1.query(1,mid+1,r,mid+1,r)+s2.query(1,mid+1,r,mid+1,r); 
        for(int id:total)ans[id]+=sum1[rot];    
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0); 
    cin>>T>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)cin>>b[i];
    cin>>q;
    for(int i=1;i<=q;i++){
        cin>>askk[i].X>>askk[i].Y;
        solver::add(1,1,n,askk[i].X,askk[i].Y,i);
    }
    solver::solve(1,1,n);
    for(int i=1;i<=q;i++)cout<<ans[i]<<'\n';
    return 0;
}
posted @ 2024-11-08 09:27  nagato__yuki  阅读(41)  评论(0)    收藏  举报