十二省聯考題解 - JLOI2019 題解

十二省聯考題解 - JLOI2019 題解

兩個T3的難度較大node

平均代碼量遠大於去年省選ios

套路題考查居多算法

A

難度等級 1數據結構

$n^2$暴力能夠拿到$60$分的優秀成績函數

而後能夠想到把區間異或轉化爲前綴兩點異或優化

能夠想到使用二分答案的方法+可持久化Trie解決,可是時間複雜度爲$n\log^2 (4294967295) $ui

這是惟一一經過不了的$poly(\log)$作法,常數不夠優秀的話會獲得$60$分,卡一卡說不定能夠$80$,理論上能卡到$100$spa

而後能夠經過將二分放在可持久化Trie上,將複雜度降爲$ n \log(4294967295)$,能夠經過此題code

而後還有一種維護相似於超級鋼琴的作法,我並不瞭解排序

對於我改題的時候,寫的是一個可持久化Trie+堆的作法,咱們維護一個大根堆,將以$i$爲結尾的區間中最大的推入堆

而後每次找到堆頂,查找對應右端點下一個比這個小一點的區間是多大,而後再推入堆便可

複雜度很顯然,爲:$(n+k)\log (4294967295)$

// luogu-judger-enable-o2
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 500005
#define ll long long
int n,K,now[N];unsigned a[N];long long ans;
struct Trie
{
    int ch[N*33][2],siz[N*33],cnt,rot[N];
    inline void copy(int x,int y){siz[x]=siz[y];ch[x][0]=ch[y][0];ch[x][1]=ch[y][1];}
    inline void insert(unsigned x,int id)
    {
        int rt=++cnt,lst=rot[id-1],t;rot[id]=rt;copy(rt,lst);
        for(int i=31;~i;i--)
        {
            t=(x>>i)&1;
            ch[rt][t]=++cnt;rt=ch[rt][t];lst=ch[lst][t];
            copy(rt,lst);siz[rt]++;
        }
    }
    inline int query(unsigned x,int id,int K)
    {
        int rt=rot[id],t;unsigned ret=0;
        for(int i=31;~i;i--)
        {
            if(!rt)return ret;t=(x>>i)&1;
            if(K>siz[ch[rt][!t]])K-=siz[ch[rt][!t]],rt=ch[rt][t];
            else rt=ch[rt][!t],ret|=1u<<i;
        }
        return ret;
    }
}tr;
priority_queue<pair<unsigned ,int > >q;
int main()
{
    // freopen("xor.in","r",stdin);
    // freopen("xor.out","w",stdout);
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++)scanf("%u",&a[i]),a[i]^=a[i-1],tr.insert(a[i-1],i);
    for(int i=1;i<=n;i++)q.push(make_pair(tr.query(a[i],i,++now[i]),i));int t;
    while(K--)ans+=q.top().first,t=q.top().second,q.pop(),q.push(make_pair(tr.query(a[t],t,++now[t]),t));
    printf("%lld\n",ans);
}

長度較短,是一個相對簡單的可持久化數據結構套路

B

難度等級 3

能夠轉化爲一個經典的字符串問題,能夠容易的想到,使用字符串hash拿到$40$分的成績

而後能夠想到使用Sa,找到知足某個位置以後的字符串是這個B串的rank區間,而後使用線段樹優化建圖來完成,能拿到$80$分的優秀成績,而後我就不太會了,而且代碼量較高,並不能在很短的時間內完成

同時,能夠想到使用Sam解決,在後綴樹上主席樹合併優化建圖,而後再倍增定位節點一樣能夠拿到$80$分的優秀成績,可是一樣,代碼複雜度較高

在此基礎上,咱們發現,線段樹合併這步沒有意義,直接在後綴樹上拆點連邊,後綴樹優化建圖,就能夠拿到$80$分的優秀成績

而後在此基礎上,將每一個節點的長度和編號按照長度從小到大,先b後a的順序排序,而後前綴優化建圖,便可拿到$100$分的優秀成績

代碼複雜度相對不高,總體考查了選手對於字符串經典模型的轉化,前綴優化建圖,後綴樹的基本應用等知識,對字符串方面薄弱的選手是一個沉重的打擊

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define ll long long
int n,na,nb,tot,cnt_SAM,oth,p[400005];char s[200005];
namespace Graph
{
    const int N = 1000005;
    struct node{int to,next;}e[N<<2];int head[N],cnt,in[N],q[N],a[N];ll f[N];
    void add(int x,int y){e[cnt]=(node){y,head[x]};head[x]=cnt++;in[y]++;}
    void init(){cnt=0;memset(head,-1,sizeof(int)*(tot+3));memset(in,0,sizeof(int)*(tot+3));memset(a,0,sizeof(int)*(tot+3));}
    ll tsort()
    {
        int l=0,r=0;for(int i=1;i<=tot;i++)if(!in[i])q[r++]=i;
        while(l<r)
        {
            int x=q[l++];
            for(int i=head[x];i!=-1;i=e[i].next)
            {
                int to1=e[i].to;
                if(!(--in[to1]))q[r++]=to1;
            }
        }
        if(r!=tot)return -1;ll ans=0;
        for(int j=r-1;~j;j--)
        {
            int x=q[j];f[x]=a[x];
            for(int i=head[x];i!=-1;i=e[i].next)f[x]=max(f[x],f[e[i].to]+a[x]);
            ans=max(ans,f[x]);
        }return ans;
    }
}
inline bool cmp(const pair<int ,int > &a,const pair<int ,int >&b){return a.first==b.first?a.second>b.second:a.first<b.first;}
// SAM
namespace Sam
{
    const int N = 400005;
    int trs[N][26],len[N],fa[N],pos[N],lst,cnt;vector<pair<int ,int > >s[N];
    inline void init(){lst=cnt=1;memset(trs[1],0,sizeof(trs[1]));fa[1]=len[1]=0;}
    inline int new_node(){cnt++;fa[cnt]=len[cnt]=0;memset(trs[cnt],0,sizeof(trs[cnt]));s[cnt].clear();return cnt;}
    inline void insert(int x,int id)
    {
        int np=new_node(),nq,p=lst,q;len[np]=len[p]+1;pos[id]=np;lst=np;
        for(;p&&!trs[p][x];p=fa[p])trs[p][x]=np;
        if(!p)fa[np]=1;else if(len[q=trs[p][x]]==len[p]+1)fa[np]=q;
        else
        {
            len[nq=new_node()]=len[p]+1;fa[nq]=fa[q];fa[q]=fa[np]=nq;
            memcpy(trs[nq],trs[q],sizeof(trs[nq]));
            for(;p&&trs[p][x]==q;p=fa[p])trs[p][x]=nq;
        }
    }
    struct node{int to,next;}e[N];int head[N],edge_cnt;
    inline void add(int x,int y){e[edge_cnt]=(node){y,head[x]};head[x]=edge_cnt++;}
    int f[N][19];
    void dfs(int x,int from)
    {
        f[x][0]=from;for(int i=1;i<19;i++)f[x][i]=f[f[x][i-1]][i-1];
        for(int i=head[x];i!=-1;i=e[i].next)dfs(e[i].to,x);
    }
    inline void build(){cnt_SAM=cnt;memset(head,-1,sizeof(int)*(cnt+3));edge_cnt=0;for(int i=2;i<=cnt;i++)add(fa[i],i);dfs(1,0);}
    inline void query(int l,int p,int id)
    {
        int x=pos[p];
        for(int i=18;~i;i--)if(len[f[x][i]]>=l)x=f[x][i];
        s[x].push_back(make_pair(l,id));
    }
    inline void build_Graph()
    {
        oth=0;
        for(int i=2;i<=cnt;i++)
        {
            sort(s[i].begin(),s[i].end(),cmp);int lim=s[i].size(),lst=i;
            for(int j=0;j<lim;j++)
            {
                int x=s[i][j].second+cnt;p[s[i][j].second]=x;
                if(j==0)Graph::add(i,x);else Graph::add(s[i][j-1].second+cnt,x);
                lst=x;
                if(s[i][j].second<=na)
                {
                    int u=na+nb+cnt+(++oth);
                    p[s[i][j].second]=u;Graph::add(x,u);
                    Graph::a[u]=s[i][j].first;
                }
            }
            for(int j=head[i];j!=-1;j=e[j].next)Graph::add(lst,e[j].to);
        }
    }
}
int T,Case;
void solve()
{
    scanf("%s",s+1);n=strlen(s+1);Sam::init();
    for(int i=n;i;i--)Sam::insert(s[i]-'a',i);Sam::build();scanf("%d",&na);
    for(int i=1,l,r;i<=na;i++)scanf("%d%d",&l,&r),Sam::query(r-l+1,l,i);scanf("%d",&nb);
    for(int i=1,l,r;i<=nb;i++)scanf("%d%d",&l,&r),Sam::query(r-l+1,l,i+na);
    tot=(cnt_SAM)+(na<<1)+nb;Graph::init();Sam::build_Graph();
    int m;scanf("%d",&m);for(int x,y;m--;Graph::add(p[x],p[y+na]))scanf("%d%d",&x,&y);
    printf("%lld\n",Graph::tsort());
}
int main(){scanf("%d",&T);while(T--)Case++,solve();}

能夠顯然的發現,我就是那個被打擊的

C

難度等級 5

這個題不太能作吧?

1_998244353直接費馬小定理+快速冪就能夠了

1?暴力找一個模數便可,模數爲$1145141$

1?+經過找到輸入數據中,輸入最接近的兩個數,找到對應答案裏的位置,而後經過觀察輸出數據找到模數的大體區間,而後驗證便可,模數爲$5211600617818708273$

1wa_998244353,觀察能夠發現,這個是在乘法的過程當中爆$int$了,而後第一個能夠直接暴力每次乘以$19$,第二個的話,一種是能夠分塊打表,也能夠經過找循環節搞定

2p的話,就是若是是質數,該位爲$p$不然爲$.$,而後8是直接線篩,9是線篩+根號求,10是$Miller-Rabin$

2u的話,就是莫比烏斯函數,前兩個相似上面的前兩個,最後一個打表+線篩便可

2g的話,就是原根,我不會

沒寫

D

難度等級 4

能夠說是一個很是好的高維DP問題,數據範圍具備迷惑性,讓人以爲暴力能過,而後本人嘗試卡常,而後失敗了,最後只拿到了$50$分

咱們先給出一個DP方程:$f[i][j][k][0/1]$表示前$i$個學校,藍陣營有$j$我的,鴨派系有$k$我的的方案數,而後每次直接轉移便可,特判很少,屬於能夠接受的範圍

而後咱們發現$k$比較小,因此咱們考慮提出一個複雜度閾值有關$k$的作法

那麼咱們先從$k=0$入手

對於$k=0$的狀況,咱們發現,不論這個城市選擇哪個陣營,如何選擇,和學校選擇哪一個派系沒有任何關係

由於對於每一個陣營,都有兩個派系能夠選,而且由於$k=0$因此具體選哪一個陣營沒啥意義

因此答案即爲$選擇派系的分組數\times 選擇陣營的分組數$,這兩個東西分別DP一下便可

而後咱們考慮$k \neq 0$的狀況,能夠發現,必定最多隻有$k$個城市和$k$個學校不會被訪問

這樣咱們發現能夠單獨對這$k$個城市DP,而後剩下的的東西作一遍$k=0$的狀況便可

可是咱們發現,這樣若是這$k$個城市的學校數=n的話,那複雜度就依然爲$nm^2$就沒有發生任何優化

那麼咱們考慮將這個問題和$k=0$結合起來

咱們發現,對於這樣的對於存在約束的學校,只須要正常DP一下,而後對於沒有約束的學校,你只需欽定它的陣營,並不須要欽定它的派系,派系只須要最後搞一下就能夠了

而後這樣的話,就能夠作到:$O(km^2 + n\times m)$解決了

可能會被卡常,而後稍微優化一下複雜度上屆便可

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
using namespace std;
#define N 2505
#define ll long long
#define mod 998244353
int f[N][305],g[N][305],f1[N],f0[N];
int n,c,k,C0,C1,D0,D1,M,S,bel[N],sz[N],hat[N],ht[N],s[N];
void get_f0()
{
    f0[0]=1;
    for(int i=1;i<=n;i++)if(hat[i]==-1)
        for(int j=M;j>=sz[i];j--)f0[j]=(f0[j]+f0[j-sz[i]])%mod;
    for(int j=1;j<=M;j++)(f0[j]+=f0[j-1])%=mod;
}
void get_f1()
{
    f1[0]=1;
    for(int i=1;i<=c;i++)if(s[i]&&ht[i]==-1)
        for(int j=M;j>=s[i];j--)f1[j]=(f1[j]+f1[j-s[i]])%mod;
    for(int j=1;j<=M;j++)(f1[j]+=f1[j-1])%=mod;
}
inline int get(int v0,int v1)
{
    int l0=max(C0-v0,0),r0=min(C1-v0,M),l1=max(D0-v1,0),r1=min(D1-v1,M);
    if(l0>r0||l1>r1)return 0;return (ll)(f1[r0]-(l0?f1[l0-1]:0))*(f0[r1]-(l1?f0[l1-1]:0))%mod;
}
void solve()
{
    memset(f,0,sizeof(f));memset(g,0,sizeof(g));memset(hat,-1,sizeof(hat));S=0;
    memset(ht,-1,sizeof(ht));memset(f1,0,sizeof(f1));memset(f0,0,sizeof(f0));memset(s,0,sizeof(s));
    scanf("%d%d%d%d%d%d",&n,&c,&C0,&C1,&D0,&D1);M=max(max(C0,C1),max(D0,D1));
    for(int i=1;i<=n;i++)scanf("%d%d",&bel[i],&sz[i]),s[bel[i]]+=sz[i],S+=sz[i];
    scanf("%d",&k);for(int i=1,x,y;i<=k;i++)scanf("%d%d",&x,&y),ht[bel[x]]=hat[x]=y;
    get_f0();get_f1();C1=max(S-C1,0),D1=max(S-D1,0);swap(C0,C1);swap(D0,D1);
    if(C0>C1||D0>D1)return puts("0"),void();g[0][0]=1;int S0=0,S1=0;
    for(int i=1;i<=c;i++)if(ht[i]!=-1)
    {
        for(int j=S0;~j;j--)for(int k=S1;~k;k--)f[j][k]=g[j][k];
        for(int j=1;j<=n;j++)if(bel[j]==i&&hat[j]!=-1)
        {
            S1+=sz[j];
            if(hat[j]==1)for(int k=S0;~k;k--){for(int l=S1;l>=sz[j];l--)g[k][l]=g[k][l-sz[j]];for(int l=0;l<sz[j];l++)g[k][l]=0;}
            else if(hat[j]!=0)for(int k=S0;~k;k--)for(int l=S1;l>=sz[j];l--)g[k][l]=(g[k][l]+g[k][l-sz[j]])%mod;
            if(hat[j]==3)for(int k=S0;~k;k--){for(int l=S1;l>=sz[j];l--)f[k][l]=f[k][l-sz[j]];for(int l=0;l<sz[j];l++)f[k][l]=0;}
            else if(hat[j]!=2)for(int k=S0;~k;k--)for(int l=S1;l>=sz[j];l--)f[k][l]=(f[k][l]+f[k][l-sz[j]])%mod;
        }
        S0+=s[i];S0=min(S0,M);
        for(int j=S0;~j;j--)for(int k=S1;~k;k--)g[j][k]=((j>=s[i]?g[j-s[i]][k]:0)+f[j][k])%mod;
    }
    int ans=0;
    for(int i=0;i<=S0;i++)for(int j=0;j<=S1;j++)ans=(ans+(ll)g[i][j]*get(i,j))%mod;
    printf("%d\n",(ans+mod)%mod);
}
int main(){int T;scanf("%d",&T);while(T--)solve();}

E

難度評級 2

一道不失幽默的樹上貪心數據結構問題

其實看一看就能發現,對每一個子樹,按照從大到小排序後,按位置合併便可

這樣就能拿到$60$分的優秀成績了

而後正解就是上述算法優化一下便可

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <iostream>
#include <bitset>
#include <set>
using namespace std;
#define N 200005
#define ll long long
multiset<int ,greater<int > >s[N];
vector<int >v;
struct node{int to,next;}e[N];int head[N],cnt,a[N],n,idx[N];
void add(int x,int y){e[cnt]=(node){y,head[x]};head[x]=cnt++;}
void merge(int x,int y)
{
    if(s[x].size()<s[y].size())swap(s[x],s[y]);
    for(;s[y].size();s[x].erase(s[x].begin()),s[y].erase(s[y].begin()))
        v.push_back(max(*s[x].begin(),*s[y].begin()));
    for(int i=0;i<v.size();i++)s[x].insert(v[i]);
    v.clear();
}
void dfs(int x)
{
    for(int i=head[x];i!=-1;i=e[i].next)
        dfs(e[i].to),merge(x,e[i].to);
    s[x].insert(a[x]);
}
int main()
{
    scanf("%d",&n);memset(head,-1,sizeof(head));
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int i=2,x;i<=n;i++)scanf("%d",&x),add(x,i);
    dfs(1);long long ans=0;
    while(s[1].size())ans+=*s[1].begin(),s[1].erase(s[1].begin());
    printf("%lld\n",ans);
}

F

不會,滾

相關文章
相關標籤/搜索