• 【考试总结】20220308


    开幕雷击:\(\text{CTT simulation}\)

    把图用 \(\rm tarjan\) 化成 \(\rm DAG\),并计算每个强连通分量里面出现过的点的数量和强连通分量的大小

    最直接的想法是使用一个最小链覆盖来求最小消耗,更简单的建图方式也能做

    对于 \(\rm scc\) 之间的边,直接连 \((u,v,+\infty)\)\(i'\)\(T\)\(scc_i\) 的大小,\(i'\)\(i\) 连反向无穷边表示可以走多点路径,\(S\) 向每个点连强连通分量里面出现过的字符数量

    如果当前强连通分量是 大小大于 \(1\) 或者有出路 且每个点都是出现过的字符,那么 \(i\)\(i'\) 连出现过的字符数量 \(-1\) 否则是本原数量

    容易发现这样子的建图符合实际含义

    Code Display
    const int N=1e5+10,inf=0x3f3f3f3f;
    struct Network_Flow{
        struct edge{int to,nxt,lim;}e[N<<2];
        int head[N],dep[N],S,T,ecnt=1,cur[N];
        inline void adde(int u,int v,int w){
            e[++ecnt]={v,head[u],w}; head[u]=ecnt;
            return ;
        }
        inline void add(int u,int v,int w){return adde(u,v,w),adde(v,u,0);}
        inline bool bfs(){
            queue<int> q; q.push(S); rep(i,1,T) cur[i]=head[i],dep[i]=0; dep[S]=1;
            while(q.size()){
                int fr=q.front(); q.pop();
                for(int i=head[fr];i;i=e[i].nxt) if(e[i].lim){
                    int t=e[i].to; if(dep[t]) continue;
                    dep[t]=dep[fr]+1; q.push(t);
                }
            } return dep[T];
        }
        inline int dfs(int x,int in){
            if(x==T) return in; int out=0;
            for(int i=head[x];i;cur[x]=i,i=e[i].nxt) if(e[i].lim){
                int t=e[i].to; if(dep[t]!=dep[x]+1) continue;
                int res=dfs(t,min(in,e[i].lim));
                e[i].lim-=res; e[i^1].lim+=res;
                in-=res; out+=res;
                if(!in) break;
            }
            if(!out) dep[x]=0; return out;
        }
        inline int dinic(){
            int sum=0;
            while(bfs()) sum+=dfs(S,inf);
            return sum;
        }
        inline void init(){
            ecnt=1; S=62<<1|1; T=S+1;
            rep(i,1,T) head[i]=0;
            return ;
        }
    }F;
    int n,num[100];
    char s[N];
    inline int decode(char s){
        if(s>='a'&&s<='z') return s-'a'+1;
        if(s>='A'&&s<='Z') return s-'A'+27;
        return s-'0'+53;
    }
    vector<int> G[N];
    int dfn[N],low[N],scc,bel[N],stk[N],siz[N],cnt[N],top,tim;
    bool ins[N],out[N];
    inline void tarjan(int x){
        ins[stk[++top]=x]=1; dfn[x]=low[x]=++tim;
        for(auto t:G[x]){
            if(!dfn[t]) tarjan(t),ckmin(low[x],low[t]);
            else if(ins[t]) ckmin(low[x],dfn[t]);
        }
        if(dfn[x]==low[x]){
            ++scc;
            do{
                cnt[scc]+=num[stk[top]];
                siz[scc]++;
                ins[stk[top]]=0;
                bel[stk[top]]=scc;
            }while(stk[top--]!=x);
        } return ;
    }
    signed main(){
        freopen("graph.in","r",stdin); freopen("graph.out","w",stdout);
        int Test=read(); while(Test--){
            scanf("%s",s+1); n=strlen(s+1);
            rep(i,1,62){
                num[i]=0;
                bel[i]=dfn[i]=low[i]=siz[i]=cnt[i]=0;
                ins[i]=out[i]=0;
                G[i].clear();
            }
            top=tim=scc=0;
            rep(i,1,n) num[decode(s[i])]=1;
            F.init();
            int m=read();
            while(m--){
                char lnk[10];
                scanf("%s",lnk+1);
                int v1=decode(lnk[1]),v2=decode(lnk[2]);
                G[v1].push_back(v2); 
            }
            rep(i,1,62) if(!dfn[i]) tarjan(i);
            for(int i=1;i<=62;++i){
                for(int t:G[i]) if(bel[t]!=bel[i]){
                    F.add(bel[i],bel[t]+scc,inf);
                    out[bel[i]]=1;
                }
            }
            for(int i=1;i<=scc;++i){
                if(cnt[i]) F.add(F.S,i,cnt[i]);
                F.add(i+scc,i,inf);
                //使走过多个 scc 可行
                if(siz[i]==cnt[i]&&(cnt[i]>1||out[i])){
                    F.add(i,i+scc,cnt[i]-1);
                    //如果只有一个无出路的满点那么可以走掉
                    //如果多于一个点的满scc就必须带来损耗
                }else F.add(i,i+scc,cnt[i]);
                F.add(i+scc,F.T,siz[i]);// 空点也要放流
            }
            print(F.dinic());
        }
        return 0;
    }
    

    想不出

    \(\sqrt3\) 的斜率就是保证点发出的射线不经过另外一个整点,那么变化坐标轴可以让南偏东/西变成向下、向左

    使用平面图欧拉公式 \(V+F=E+S+1\),这里 \(V\) 是射线端点和交点个数之和 \(I+n\),容易发现 \(E\) 恰好是二倍交点也就是 \(2I\),所以所求就是 \(F=I-n+1\)

    对于具有左上右下关系的一对点 \((a,b)\) 一定是 \(a\) 出发向下,\(b\) 出发向左,否则调整过后更为优秀,类似地我们可以得出存在一条 \(x\) 上升的过程中单调不降的分界线满足分界线上面射线向下,分界线下面射线向左

    找到分界线可以逐点判定,其实本质上是二次函数求最值的过程,那么如果这个点左上的点数大于右下的点数,那么射线朝左,否则朝上

    最后模拟连边过程写个 \(\Theta(n^2)\) 连边的并查集即可,当然使用 \(\rm KD-Tree\) 优化建图可以低于这个复杂度

    最后的问题是处理坐标轴变换,一种可能的方式是设 \(y=\pm\sqrt 3x\) 为新的 \(x/y\) 轴,那么 \(x/y\) 坐标数值大小关系可以通过 \(y=\mp\sqrt 3x+b\) 映射到 \(y\) 轴来比较

    Code Display
    const int N=2010;
    int x[N],y[N],ans,n,d[N],xid[N],yid[N],mp[N],dir[N];
    const double get_b(int x,int y,double k){return y-k*x;}
    struct BIT{
        int c[N];
        inline void insert(int x,int v){for(;x<=n;x+=x&(-x)) c[x]+=v; return ;}
        inline int query(int x){int res=0; for(;x;x-=x&(-x)) res+=c[x]; return res;}
        inline int query(int l,int r){return query(r)-query(l-1);}
    }suf,pre;
    int per[N];
    struct Dsu{
        int fa[N];
        inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
        inline void init(){rep(i,1,n) fa[i]=i; return ;}
        inline void merge(int x,int y){fa[find(x)]=find(y);}
    }T;
    signed main(){
        freopen("surface.in","r",stdin); freopen("surface.out","w",stdout);
        n=read();
        rep(i,1,n) x[i]=read(),y[i]=read(),xid[i]=yid[i]=i;
        const double K=sqrt(3);
        sort(xid+1,xid+n+1,[&](const int a,const int b){
            return get_b(x[a],y[a],-K)>get_b(x[b],y[b],-K);
        });
        sort(yid+1,yid+n+1,[&](const int a,const int b){
            return get_b(x[a],y[a],K)>get_b(x[b],y[b],K);
        });
        for(int i=1;i<=n;++i) per[yid[i]]=i;
        for(int i=1;i<=n;++i) mp[i]=per[xid[i]];
        rep(i,1,n) suf.insert(i,1);
        for(int i=1;i<=n;++i){
            int down=suf.query(1,mp[i]-1),lef=pre.query(mp[i]+1,n);
            suf.insert(mp[i],-1);
            pre.insert(mp[i],1);
            if(down<lef) dir[i]=1;//turn left
        }
        T.init();
        for(int i=1;i<=n;++i) if(!dir[i]){
            for(int j=i+1;j<=n;++j) if(dir[j]&&mp[i]>mp[j]) T.merge(i,j),++ans;
        }
    
        set<int> st;
        rep(i,1,n) st.insert(T.find(i));
        print(ans-n+st.size());
        return 0;
    }
    

    题目名称

    集合的 \(\rm OGF\)

    \[F(x)=\sum_{i=1}^nx^{l_i}\frac{1-x^{r_i-l_i+1}}{1-x} \]

    那么考虑找到答案的 \(\rm OGF\),考虑写答案作:

    \[Ans=\frac1{24}\left(aF^4(x)+bF(x^3)F(x)+cF^2(x^2)+dF(x^2)F^2(x)+eF(x^4)\right) \]

    不难发现在选 \(4\) 个意义下只有上面 \(5\) 种算子是合法的

    尝试根据实际含义来找到 \(a,b,c,d,e\) 的值,可能的选值种类集合有以下几种,需要让不合法的种类计算次数为 \(0\) 否则为 \(1\)

    • \(abcd\) 型,只在 \(a\) 里面算,由于外面带 \(\frac 1{24}\) 的系数,所以要算 \(24\) 次,也就是 \((4!)a=24\)

    • \(aaaa\) 型:在每个部分都只会计算 \(1\) 次,所以限制 \(a+b+c+d+e=0\)

    • \(aabc\) 型:在 \(a\) 中表现为四选 \(2\) 之后 \(2\)\(1\);在 \(d\)\(F(x^2)\) 带走 \(aa\) 之后 \(bc\) 可以换顺序,两种方案,得到 \(12a+2d=0\)

    • \(aabb\) 型:

      \(a\) 中表现为两组占四个

      \(c,d\) 中是 \((a,b),(b,a)\) 算两次

      \(d\) 中一组在 \(F(x^2)\) 里面算,另外一组在 \(F^2(x)\) 里面算,系数都是 \(1\)

      所以有 \(\binom 42a+2c+2d=0\)

    • \(aaab\) 型:

      \(a\) 中是 \(\binom 43\)

      \(b\) 中是 \(1\)

      \(d\) 中是删掉 \(aa\) 之后换顺序,两种方案

      所以有 \(4a+b+2d=0\)

    是不是再推广一下就能到选任意个的容斥了

    方程组是满秩的,可以得到 \(a,b,c,d,e\) 的值。至此答案可以得出为

    \[Ans=\frac{1}{24}\left[A^4(x)+8A(x^3)A(x)+3A^2(x^2)-6A^2(x)A(x^2)-6A(x^4)\right] \]

    可以暴力计算 \([x^s]A(x^3)A(x),[x^s]A^2(x^2),[x^s]A^2(x)A(x^2)\),对于剩下两项需要使用一类折半搜索来解决

    \(A^4(x)\) 为先,计算出来 \(\left(\sum_{i=1}^nx^{l_i}-{x^{r_i+1}}\right)^2\) 之后逐个枚举 \(x^c\),那么要计算得到

    \[\begin{aligned}&[x^{d=s-c}]\left(\frac 1{1-x}\right)^4\sum_{i=1}^{siz} coef_i x^{t_i}\\ &=\sum_{i=1}^{siz}[x^{t_i}] coef_i x^{t_i}[x^{d-t_i}]\left(\frac 1{1-x}\right)^4\\ &=\sum_{i=1}^{siz} coef_i\binom{d-t_i+3}3\\ &=\sum_{i=1}^{siz}\frac 16 coef_i\left[(d+1)(d+2)(d+3)-(3d^2+12d+11)t_i+(3d+6)t_i^2-t_i^3\right]\\ \end{aligned}\]

    最后一步直接将组合数展开作下降幂除阶乘的形式就能得到了,那么对 \(d\) 之外的项维护前缀和,每次查询 \(c_i\) 就可以找到 \(\max i\to c+t_i\le s\) 前缀和来得到答案

    另外的 \(A(x^2)A^2(x)\) 前置一个恒等式

    \[\sum_{i=0}^n\binom{i+2}i(-1)^{n-i}=\left\lfloor\frac{(n+2)^2}4\right\rfloor \]

    证明

    \(\rm RHS\) 的实际含义是从 \(\sum\limits_{i\le j\le n+2}[j\equiv n\mod2]\)

    \(\binom {i+2}i=\binom {i+2}2\)\(\binom {i+2}2\) 的含义是在 \(i+2\) 里面选择一对点

    对于和 \(n\) 同奇偶性的一对点 \((x,j)\) 发现计算次数是 \((-1)^n\sum\limits_{k=j}^n(-1)^{k}\) 由于奇偶性相同,\((-1)^{j+n}=1\) 而其他项消掉了,不同奇偶性的类似,正好全消掉了是 \(0\)


    和上面的一样推推:

    \[\begin{aligned}&[x^{d=s-c}]\frac{1}{(1-x)^3(1+x)}\sum_{i=1}^{siz}coef_ix^{t_i}\\ &=\sum_{i=1}^{siz}[x^{t_i}]coef_ix^{t_i}[x^{d-t_i}]\frac{1}{(1-x)^3(1+x)}\\ &=\sum_{i=1}^{siz}coef_i\sum_{j=0}^{d-t_i} \binom{j+2}j(-1)^{d-t_i-j}\\ &=\sum_{i=1}^{siz}coef_i\left\lfloor\frac{(d-t_i+2)^2}4\right\rfloor \end{aligned}\]

    下取整的计算考虑继续拆成 \((d-t_i+2)^2\) 的和以及 \((d-t_i+2)^2\mod 4\) 的值的和即可,前面可以直接拆成下面

    \[=\frac14\sum_{i=1}^{siz}coef_i\left[(d+2)^2-2(d+2)t+t^2\right] \]

    后面分开 \(t_i\) 的奇偶性计算,查询找 \(d\) 的奇偶性减去再除 \(4\) 即可,这里使用 \(A(x^2)\) 来做前缀和就只用维护一个了

    Code Display
    const int N=1010;
    int n,s,l[N],r[N],ans;
    signed main(){
        freopen("count.in","r",stdin); freopen("count.out","w",stdout);    
        n=read(); s=read(); 
        rep(i,1,n) l[i]=read(),r[i]=read();
        if(s%4==0){
            for(int i=1;i<=n;++i){
                if(l[i]*4<=s&&s<=r[i]*4){
                    ckdel(ans,6); 
                    break;
                }
            }
        }
        rep(i,1,n) rep(j,1,n){
            int L=max(0ll,s-r[j]),R=s-l[j];
            if(R<l[i]*3||L>r[i]*3) continue;
            ckmax(L,l[i]*3); ckmin(R,r[i]*3);
            while(L%3) ++L; while(R%3) --R;
            if(R<L) continue;
            ckadd(ans,mul(8,(R-L)/3+1));
        }
        vector<pair<int,int> >now,tmp;
        rep(i,1,n) rep(j,1,n){
            now.emplace_back(l[i]+l[j],1);
            now.emplace_back(r[i]+r[j]+2,1);
            now.emplace_back(r[i]+l[j]+1,mod-1);
            now.emplace_back(r[j]+l[i]+1,mod-1);
        }   
        sort(now.begin(),now.end());
        for(auto t:now){
            if(!tmp.size()||tmp.back().fir!=t.fir) tmp.push_back(t);
            else ckadd(tmp.back().sec,t.sec);
        }
        now.clear();
        for(auto t:tmp) if(t.sec) now.push_back(t);
        if(s%2==0){
            for(auto t:now) if(t.fir<=s/2){
                int d=s/2-t.fir;
                ckadd(ans,mul(3,(d+1)*t.sec%mod));
            }
        }
        if(1){
            vector<int> sum[4];
            int siz=now.size();
            rep(i,0,3) sum[i].resize(siz);
            for(int i=0;i<siz;++i){
                sum[0][i]=now[i].sec;
                sum[1][i]=mul(now[i].sec,now[i].fir);
                sum[2][i]=mul(now[i].sec,mul(now[i].fir,now[i].fir));
                sum[3][i]=mul(now[i].sec,ksm(now[i].fir,3));
            }
            rep(i,1,siz-1) rep(j,0,3) ckadd(sum[j][i],sum[j][i-1]);
            int contr=0,pter=siz-1;
            for(auto t:now){
                while(pter>=0&&now[pter].fir>s-t.fir) --pter;
                if(pter<0) break;
                int d=s-t.fir;
                int tsum=0;
                ckadd(tsum,mul(sum[0][pter],mul(mul(d+1,d+2),d+3)));
                ckdel(tsum,mul(sum[1][pter],(3*d*d+12*d+11)%mod));
                ckadd(tsum,mul(sum[2][pter],(3*d+6)%mod));
                ckdel(tsum,sum[3][pter]);
                ckadd(contr,mul(tsum,t.sec));
            }
            ckadd(ans,mul(ksm(6,mod-2),contr));
        }
        if(1){
            vector<pair<int,int> >poly;
            rep(i,1,n) poly.emplace_back(2*l[i],1),poly.emplace_back(2*r[i]+2,mod-1);
            vector<int> sum[3];
            int siz=poly.size();
            rep(i,0,2) sum[i].resize(siz);
            for(int i=0;i<siz;++i){
                sum[0][i]=poly[i].sec;
                sum[1][i]=mul(poly[i].sec,poly[i].fir);
                sum[2][i]=mul(poly[i].sec,mul(poly[i].fir,poly[i].fir));
            }
            rep(i,1,siz-1) rep(j,0,2) ckadd(sum[j][i],sum[j][i-1]);
            int pter=siz-1,contr=0;
            for(auto t:now){
                while(pter>=0&&poly[pter].fir>s-t.fir) --pter;
                if(pter<0) break;
                int d=s-t.fir,tsum=0;
                ckadd(tsum,mul(sum[0][pter],(d+2)*(d+2)%mod));
                ckdel(tsum,mul(2*(d+2)%mod,sum[1][pter]));
                ckadd(tsum,sum[2][pter]);
                if(d&1) ckdel(tsum,sum[0][pter]);
                ckadd(contr,mul(tsum,t.sec));
            }
            ckdel(ans,mul(3*contr%mod,(mod+1)/2));
        }
        print(mul(ans,ksm(24,mod-2)));
        return 0;
    }
    

  • 相关阅读:
    [Mise] Refetch API data when a state value changes with the `$watch` property in Alpine JS
    Android之用自定义的shape去实现shadow效果
    http抓包以及网速限定
    ios成长之每日一遍(day 7)
    ios成长之每日一遍(day 6)
    ios成长之每日一遍(day 5)
    ios成长之每日一遍(day 4)
    ios成长之每日一遍(day 3)
    ios成长之每日一遍(day 2)
    ios成长之每日一遍(day 1)
  • 原文地址:https://www.cnblogs.com/yspm/p/15982790.html
Copyright © 2020-2023  润新知