ZJOI2018场外vp被虐记&&部分题解

ZJOI没过几天:
教练:明天我们做ZJOI.
众人:那我怕是要爆零了
wxh:(冒金光)
教练:这次ZJOI很难,全场最高130。
众人:那我怕是连10分都没了
wxh:(冒金光)

第二天:
mcfx:(开场做t1)
wxh:(30min后开始写代码)
我:???
(2h后)
mcfx:(还在写t1)
wxh:(拿起水杯走出了机房,电脑上显示FC:找不到差异)
我:???
mcfx:我去改改FLOJ把wxh标成金光怎么样?(20min后)改好了
我:这个好,要不要加个atcoder的皇冠上去
wxh:(冒金光)
(3h后)
mcfx:(还在写t1,已经4k了)
wxh:(写了一会代码就趴在桌上)
我:(终于会t2暴力了)
(4h后)
mcfx:(还在写t1)
wxh:(观看mcfx写t1)
我:怎么这个大样例还没过
(过了一会)
mcfx:我这个写不完了
wxh:我发现t3部分分有规律
owenowl:t1 50真好拿
我:(刚过了t2,是推T1还是看t3规律呢?算了,我选择去找规律)
(结束时)
mcfx:又水了一场比赛,得分0
wxh:(冒金光)
我:woc t1 50分那么sb(t3没发现任何规律,得分0)

(下午讲评时)
wxh:t2傻逼题你们怎么不会啊
众人:wxh赛高!wxh冒金光!

早知道看t1时应该顺便推一下部分分。。。整个机房一堆人50分
结果5h用了4h做t2,最后没时间推t1了,110滚粗(现场赛有debuff分数肯定比这个低),然后那个冒金光的wxh 2h秒了t2,最后得分190

ZJOI2018 Problem B.历史

想了半天这个序列应该有啥性质,后来才发现应该对每个点考虑。这样就简单多了,对于每个点,答案就是 sum[u]1max(0,mx[u]1(sum[u]mx[u])) sum[u]=vua[u],mx[u]=maxvu(a[u]) ),即把一堆点硬点一个顺序,尽量使相邻编号不一样的对数最多。显然直接考虑往出现次数最多的点的空隙里面丢。

化一下可以发现是满足 2mxsum[u]+1
时会有特殊贡献 2(sum[u]mx) ,否则就是 sum[u]1 (其实我觉得第一步最难,后面好像挺简单的。。。)

发现这个东西很像树剖,假设把 mx 当成重儿子,那么从一个叶子到根最多只会经过 O(loga[i]) 条轻链,所以用LCT来暴力维护,至于复杂度应该也不会高。。。

#include<bits/stdc++.h>
#define maxn 401000
#define LLD "%lld"
using namespace std;
typedef long long ll;
int ch[maxn][2],fa[maxn],pa[maxn],n,Q;
ll atg[maxn],val[maxn],ans,dp[maxn],a[maxn],son[maxn];
vector<int>G[maxn];
void addedge(int u,int v){
    G[u].push_back(v);
    G[v].push_back(u);
}
typedef pair<ll,int> par;
void dfs(int u){
    dp[u]=a[u];
    par mx=par(a[u],u);
    for(int i=0,p;i<G[u].size();++i)if((p=G[u][i])!=pa[u])
        pa[p]=u,dfs(p),mx=max(mx,par(dp[p],p)),dp[u]+=dp[p],fa[p]=u;
    if(mx.first*2>dp[u]+1){
        son[u]=mx.second;
        if(son[u]!=u)ch[u][1]=son[u];
        ans+=val[u]=2*(dp[u]-mx.first);
    } else ans+=val[u]=dp[u]-1;
}
int isroot(int x){
    if(!x||!fa[x])return 1;
    return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
}
void rotate(int p){
    int q=fa[p],y=fa[q],k=(ch[q][1]==p);
    if(!isroot(q))ch[y][ch[y][1]==q]=p;
    fa[ch[q][k]=ch[p][k^1]]=q;
    fa[ch[p][k^1]=q]=p;
    fa[p]=y;
}
void pd(int p){
    if(!p)return ;
    if(atg[p]){
        if(ch[p][0])atg[ch[p][0]]+=atg[p],dp[ch[p][0]]+=atg[p];
        if(ch[p][1])atg[ch[p][1]]+=atg[p],dp[ch[p][1]]+=atg[p];
        atg[p]=0;
    }
}
void splay(int x){
    static int sta[maxn];
    int tp=0;
    for(int y=x;!isroot(y);y=fa[y])sta[++tp]=y;
    while(tp)pd(sta[tp--]);
    while(!isroot(x)){
        int y=fa[x];
        if(!isroot(y)){
            if((ch[fa[y]][1]==y)^(ch[y][1]==x))rotate(x);
            else rotate(y);
        }
        rotate(x);
    }
    pd(x);
}
int gettop(int x){
    while(ch[x][0])x=ch[x][0];
    splay(x);
    return x;
}
ll find(int z){
    splay(z);
    return dp[z];
}
void access(int x,ll add){
    a[x]+=add;
    int X=x,y=x;
    for(;x;x=fa[x]){
        splay(x);
        dp[x]+=add;
        dp[ch[x][0]]+=add;
        atg[ch[x][0]]+=add;
    }
    x=X,splay(x);
    if(a[x]*2>dp[x]+1){
        son[x]=x;
        ans-=val[x];
        val[x]=2*(dp[x]-a[x]);
        ans+=val[x];
        ch[x][1]=0;
    } else {
        if(son[x]){
            ll mx=(son[x]==x?a[x]:find(son[x]));
            splay(x);
            if(mx*2<=dp[x]+1){
                ans-=val[x];
                val[x]=dp[x]-1;
                ans+=val[x];
                son[x]=0;
                ch[x][1]=0;
            } else ans+=2*add,val[x]+=add*2;
        } else ans+=add,val[x]+=add;
    }
    y=gettop(x);
    while(pa[y]){
        splay(pa[y]);
        int z=pa[y];
        if(dp[y]*2>dp[z]+1){
            ans-=val[z];
            val[z]=2*(dp[z]-dp[y]);
            ans+=val[z];
            ch[z][1]=y;
            son[z]=y;
        } else {
            if(son[z]){
                ll mx=(son[z]==z?a[z]:find(son[z]));
                splay(z);
                if(mx*2<=dp[z]+1){
                    ans-=val[z];
                    val[z]=dp[z]-1;
                    ans+=val[z];
                    son[z]=0;
                    ch[z][1]=0;
                } else ans+=2*add,val[z]+=2*add;
            } else ans+=add,val[z]+=add;
        }
        splay(z);
        y=gettop(z);
    }
}
int main(){
    scanf("%d%d",&n,&Q);
    for(int i=1;i<=n;++i)scanf(LLD,&a[i]);
    for(int i=2,u,v;i<=n;++i)scanf("%d%d",&u,&v),addedge(u,v);
    dfs(1);
    printf(LLD"\n",ans);
    while(Q--){
        int A,w;
        scanf("%d%d",&A,&w);
        access(A,w);
        printf(LLD"\n",ans);
    }
}

ZJOI2018 Problem C.迷宫

看见这题完全没思路,还跑去找规律,然后没发现任何规律。。。

显然有一个答案上界 K ,即建一些点分别代表余数为0...K1。实际上,一些点是完全等价的。我们只需要求出等价类的个数。好吧假设看过题解了(并陷入了迷思),所以就解释一下那个算法流程好了。。。

现在,我们手上有 1...K1 0 早就消失了。把这些数都乘上m,令 d=gcd(K,m) ,把结果去重会得到 K/d 个结果,这些数当然是 d 的倍数。然后,这些数应该考虑加一个[0,m)的数了,由于大于K-m+1的数都能走到 0 ,所以根据题解的结论,这些数每个都代表一个等价类,我们将他们拿走并计入答案,并递归这个东西。

f(L,K),表示现在还剩 1...L ,在 mod K 意义下。可以推出如下过程:
d=gcd(L,K)
如果 d=1 ,那么所有数都代表不同的类,答案为 L
如果LK/d,说明乘 m 后不重复,答案还是L
这个时候,上一层算出的等价类为 (L,K) ,由于多了一层相当于多了 m 个选择,所以这一层已经被删除了0(Km(KL),K)(膜意义下的区间)若这区间不为空,那么这一层应当删去 m(KL)/d ,并递归调用 f(K/dm(KL)/d,K/d) (具体看题解)

至于为什么要用__int128,显然是因为没判区间是否为空,这样会算出负数导致爆ll,判了就不用__int128了。

#include<bits/stdc++.h>
#define D "%lld"
using namespace std;
typedef long long ll;
long long m,K,T;
ll sol(ll L,ll K){
    ll d=__gcd(m,K);
    if(d==1||L<=K/d)return L;
    if((K-L)>K/m)return K/d;
    return m/d*(K-L)+sol(K/d-m/d*(K-L),K/d);
}
int main(){
    scanf(D,&T);
    while(T--){
        scanf(D D,&m,&K);
        printf(D "\n",sol(K-1,K)+1);
    }
}

ZJOI2018 Problem A.线图 题解?不存在的,自己看jiry题解。再见

#include<bits/stdc++.h>
#define mod 998244353
#define maxn 5010
#define maxm 100100
#define _c2(x) (1ll*(x)*((x)-1)/2)
#define pf(x) (1ll*(x)*(x))
using namespace std;
typedef pair<int,int> par;
int ans,anses[1<<20],E[maxn][22],K,n,pd[30][30];
int dgr[maxm],dgsum[maxm],dgsqr[maxm],fa[maxn],dp[maxn][20],P[2][1<<11];
vector<int>G[maxn],T[maxn];
vector<par>B[maxm],C;
void addedge(int u,int v){
    G[u].push_back(v);
    G[v].push_back(u);
}
int cal(int n,int m,int K){
    if(K==1)return m;
    for(int i=1;i<=n;++i)dgr[i]=dgsum[i]=dgsqr[i]=0;
    for(auto p:C)dgr[p.first]++,dgr[p.second]++;
    if(K==2){
        int ans=0;
        for(int i=1;i<=n;++i)
            ans=(ans+_c2(dgr[i]))%mod;
        return ans;
    } else if(K==3){
        int ans=0;
        for(auto p:C)ans=(ans+_c2(dgr[p.first]+dgr[p.second]-2))%mod;
        return ans;
    } else if(K==4){
        int ans=0;
        for(auto p:C){
            int X=dgr[p.first]+dgr[p.second]-2;
            dgsqr[p.first]=(dgsqr[p.first]+pf(X))%mod;
            dgsqr[p.second]=(dgsqr[p.second]+pf(X))%mod;
            dgsum[p.first]=(dgsum[p.first]+X)%mod;
            dgsum[p.second]=(dgsum[p.second]+X)%mod;
        }
        for(int i=1;i<=n;++i){
            int sqr=dgsqr[i],sum=dgsum[i],dg=dgr[i];
            ans=(ans+1ll*sum*sum-sqr+1ll*(dg-1)*sqr-5ll*(dg-1)*sum+6ll*_c2(dg))%mod;
        }
        return 1ll*ans*(mod+1)/2%mod;
    } else {
        int id=0,cnt=0;
        for(int i=1;i<=n;++i)B[i].clear();
        for(auto p:C)
            B[p.first].push_back(par(p.second,++cnt)),
            B[p.second].push_back(par(p.first,cnt));
        C.clear();
        for(int i=1;i<=n;++i)
            for(int j=0;j<B[i].size();++j)
                for(int k=0;k<j;++k)
                    C.push_back(par(B[i][j].second,B[i][k].second));
        return cal(m,C.size(),K-1);
    }
}
int build(string tree,int K,int& cnt){
    int rt=1,id=1;
    for(int i=0;i<tree.length();++i){
        if(tree[i]=='0'){
            rt=fa[rt];
        } else {
            T[rt].push_back(++id);
            T[id].push_back(rt);
            C.push_back(par(rt,id));
            fa[id]=rt,rt=id;
        }
        if(!rt)return -1;
        else if(id>K)return -1;
    }
    return id;
}
string gethash(int rt,int S,int f){
    vector<string> v;
    for(auto p:T[rt])if(p!=f&&(S>>p-1&1))
        v.push_back(gethash(p,S,rt));
    sort(v.begin(),v.end());
    string ret="";
    for(auto p:v)ret+="1"+p+"0";
    return ret;
}
int zip(string s){
    int S=1;
    for(int i=0;i<s.length();++i)S=S<<1|(s[i]=='1');
    return S;
}
int getdp(int u,int f,int K){
    for(int i=1;i<=K;++i)dp[u][i]=0;
    for(auto p:G[u])if(p!=f)
        getdp(p,u,K);
    for(int i=1;i<=K;++i){
        int flg=0;
        for(int j=1;j<i;++j)if(pd[i][j])
            dp[u][i]=dp[u][j],flg=1;
        if(flg)continue;
        int cnt=0;
        vector<int>L;
        L.clear();
        for(auto p:T[i])if(p!=fa[i])
            T[p].size()==1?cnt++:(L.push_back(p),0);
        if(L.size()+cnt>G[u].size()-(f!=0))continue;
        int S=1<<L.size(),p=0,np=1;
        for(int j=0;j<S;++j)P[0][j]=P[1][j]=0;
        P[np][0]=1;
        for(auto v:G[u])if(v!=f){
            for(int j=0,ns=0;j<L.size();++j,ns=0)if(dp[v][L[j]]){
                for(int k=0;k<j;++k)if(pd[L[k]][L[j]])ns|=1<<k;
                for(int k=(S-1)^ns^(1<<j),D=(S-1)^ns^(1<<j);;k=(k-1)&D){
                    if(!P[np][k|ns]){
                        if(!k)break;
                        else continue;
                    }
                    P[p][k|(1<<j)|ns]=(P[p][k|(1<<j)|ns]+1ll*P[np][k|ns]*dp[v][L[j]])%mod;
                    if(!k)break;
                }
            }
            for(int j=0;j<S;++j)
                if(P[np][j])P[p][j]=(P[p][j]+P[np][j])%mod,P[np][j]=0;
            swap(p,np);
        }
        dp[u][i]=1ll*E[G[u].size()-L.size()-(f!=0)][cnt]*P[np][S-1]%mod;
    }
}
typedef unsigned long long ull;
map<ull,int>mp;
ull _gethash2(int n){
    ull ret=0;
    vector<int>v;
    for(int i=1;i<=n;++i)v.push_back(zip(gethash(i,(1<<n)-1,0)));
    sort(v.begin(),v.end());
    for(int i=0;i<n;++i)ret=ret*19260817+v[i];
    return ret;
}
void dfs(string tree,int K,int P){
    if(tree.length()==2*(K-1)){
        int _cnt=0;
        for(int i=1;i<=K;++i)T[i].clear();
        C.clear();
        if(build(tree,K,_cnt)!=K)return ;
        int ss=zip(gethash(1,(1<<K)-1,0));
        if(~anses[ss])return ;
        anses[ss]=(mp.count(_gethash2(K))?mp[_gethash2(K)]:mp[_gethash2(K)]=cal(K,K-1,P));
        for(int i=0;i<(1<<K)-1;++i){
            string gt=gethash(__builtin_ctz(i&-i)+1,i,0);
            if(gt.length()!=2*(__builtin_popcount(i)-1))continue;
            anses[ss]=(anses[ss]-anses[zip(gt)]+mod)%mod;
        }
        for(int i=1;i<=K;++i)
            for(int j=i;j<=K;++j)
                pd[i][j]=pd[j][i]=(gethash(i,(1<<K)-1,fa[i])==gethash(j,(1<<K)-1,fa[j]));
        getdp(1,0,K);
        int val=0;
        for(int i=1;i<=n;++i)val=(val+dp[i][1])%mod;
        ans=(ans+1ll*anses[ss]*val)%mod;
        return ;
    }
    dfs(tree+"1",K,P);
    dfs(tree+"0",K,P);
}
int main(){
//  freopen("line.in","r",stdin);
//  freopen("line.out","w",stdout);
//  freopen("ex_line2.in","r",stdin);
    memset(anses,-1,sizeof(anses));
    scanf("%d%d",&n,&K);
    for(int i=2,u,v;i<=n;++i)
        scanf("%d%d",&u,&v),addedge(u,v);
    for(int i=0;i<=n;++i)
        for(int j=*E[i]=1;j<=i&&j<=20;++j)
            E[i][j]=(E[i-1][j]+E[i-1][j-1])%mod;
    for(int i=1;i<=K+1;++i)dfs("",i,K);
    printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值