• “科大讯飞杯”第18届上海大学程序设计联赛春季赛暨高校网络友谊赛


    鸽了快一个月的题解

    A. 组队比赛 (Nowcoder 5278 A)

    题目大意

    给定(a,b,c,d),俩俩分组,使得两组中数的和的差最小。

    解题思路

    最小最大一组另外的一组即可。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    int main(void) {
        ios::sync_with_stdio(false); 
        cin.tie(0); cout.tie(0);
        int a[4];
        cin>>a[0]>>a[1]>>a[2]>>a[3];
        sort(a,a+4);
        int ans=abs(a[0]+a[3]-a[1]-a[2]);
        cout<<ans<<endl;
        return 0;
    }
    


    B. 每日一报 (Nowcoder 5278 B)

    题目大意

    符合条件的结构体排序。

    解题思路

    符合条件的结构体排序。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    const double eps=1e-5;
    
    struct data{
        int ri,num;
        double t;
        bool operator < (const data & a){
            if (ri>a.ri) return true;
            if (ri<a.ri) return false;
            if (t-a.t>eps) return true;
            if (t-a.t<eps) return false;
            if (num<a.num) return true;
            return false;
        }
    };
    
    int main(void) {
        ios::sync_with_stdio(false); 
        cin.tie(0); cout.tie(0);
        vector<data> qwq;
        int n;
        cin>>n;
        double tt;
        for(int r,nu,i=1;i<=n;++i){
            cin>>r>>nu>>tt;
            if (tt-38.0<-eps) continue;
            qwq.push_back({r,nu,tt});
        }
        sort(qwq.begin(),qwq.end());
        cout<<qwq.size()<<endl;
        for(auto i:qwq){
            cout<<i.ri<<' '<<i.num<<' '<<fixed<<setprecision(1)<<i.t<<endl;
        }
        return 0;
    }
    


    C. 最长非公共子序列 (Nowcoder 5278 C)

    题目大意

    给定(s_{1},s_{2}),求出最长的非公共子序列的长度,不存在输出(-1)

    解题思路

    相等即为(-1)否则为长度最大的那个。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    int main(void) {
        ios::sync_with_stdio(false); 
        cin.tie(0); cout.tie(0);
        string s1,s2;
        cin>>s1>>s2;
        if (s1==s2) cout<<"-1"<<endl;
        else cout<<max(s1.size(),s2.size())<<endl;
        return 0;
    }
    


    D. 最大字符集 (Nowcoder 5278 D)

    题目大意

    给定一个(n),求一个集合,满足以下条件:

    • 每个字符串由 0 和 1 组成。
    • 每个字符串长度在 1 到 n 之间,且两两长度不同。
    • 集合中任何一个字符串都不是其他字符串的子串。

    最大化集合元素个数,输出任意一种可能的集合情况。

    解题思路

    构造题。(1,2)特殊处理,其余的最大个数为(n-1),长度从(2)(n),每个元素首尾为(1)中间为(0)

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    int main(void) {
        ios::sync_with_stdio(false); 
        cin.tie(0); cout.tie(0);
        int n;
        cin>>n;
        if (n==1){
            cout<<"1"<<endl;
            cout<<"1"<<endl;
        }else if (n==2){
            cout<<"2"<<endl;
            cout<<"1"<<endl;
            cout<<"00"<<endl;
        }else{
            cout<<n-1<<endl;
            for(int i=2;i<=n;++i){
                string s(i,'0');
                s[0]='1';
                s[i-1]='1';
                cout<<s<<endl;
            }
        }
        return 0;
    }
    


    E. 美味的序列 (Nowcoder 5278 E)

    题目大意

    给定一个序列,每次可以从头或尾取一个数,每次取后序列里的所有数减一。问最优的取法,取出来的数的和是多少。

    解题思路

    很容易发现怎么取结果都是一样的(将减一操作作用在另一个初始全为0的数组),于是答案就是所有数的和减去(frac{n(n-1)}{2})

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    int main(void) {
        ios::sync_with_stdio(false); 
        cin.tie(0); cout.tie(0);
        int n;
        cin>>n;
        LL ans=-(LL)(n)*(n-1)/2;
        LL qwq=0;
        while(n--){
            cin>>qwq;
            ans+=qwq;
        }
        cout<<ans<<endl;
        return 0;
    }
    


    F. 日期小助手 (Nowcoder 5278 F)

    题目大意

    给定一个日期,输出该日期后最近的母亲节或父亲节是什么时候。

    解题思路

    求出当前年和下一年的母亲父亲节日期比较即可。
    恰好练一练类。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    struct data{
        int y,m,d;
        bool operator < (const data & a){
            if (y<a.y) return true;
            if (y>a.y) return false;
            if (m<a.m) return true;
            if (m>a.m) return false;
            if (d<a.d) return true;
            if (d>a.d) return false;
            return false;
        }
    };
    
    
    int run[108]={0};
    
    bool check(int y){
        if (y%400==0) return true;
        if (y%100!=0&&y%4==0) return true;
        return false;
    }
    
    pair<data,data> solve(int y){
        int x=6-(y-2000+run[y-2000]);
        int xx=3-(y-2000+run[y-2000]);
        while(x<0){
            x+=7;
        }
        while(xx<0){
            xx+=7;
        }
        data a={y,5,x+8};
        data b={y,6,xx+15};
        return (make_pair(a,b));
    }
    
    
    int main(void) {
        ios::sync_with_stdio(false); 
        cin.tie(0); cout.tie(0);
        for(int i=1;i<=105;++i)
            if (check(i+2000)) run[i]++;
        for(int i=1;i<=101;++i) run[i]+=run[i-1];
        int t;
        cin>>t;
        while(t--){
            data noww;
            cin>>noww.y>>noww.m>>noww.d;
            auto qwq=solve(noww.y);
            auto qaq=solve(noww.y+1);
            if (noww<qwq.first){
                cout<<"Mother's Day: May "<<qwq.first.d<<"th, "<<qwq.first.y<<endl;
            }else if (noww<qwq.second){
                cout<<"Father's Day: June "<<qwq.second.d;
                if (qwq.second.d==21) cout<<"st, ";
                else cout<<"th, ";
                cout<<qwq.second.y<<endl;
            }else{
                cout<<"Mother's Day: May "<<qaq.first.d<<"th, "<<qaq.first.y<<endl;
            }
        }
        return 0;
    }
    


    G. 血压游戏 (Nowcoder 5278 G)

    题目大意

    给定一棵树,根的编号是(s)(从(1)开始),每个节点有(a_i)只松鼠,它们不断向根节点移动,且会依次发生以下事件:

    • 如果一个节点上有 2 只或 2 只以上的松鼠,他们会打架,然后这个节点上松鼠的数量会减少 1;
    • 根节点的所有松鼠移动到地面,位于地面上的松鼠不会再打架;
    • 所有松鼠同时朝它们的父节点移动。

    问最终有多少只松鼠到达地面。

    解题思路

    容易发现不同深度之间的松鼠互不干扰,所以我们每次就针对同一深度的松鼠考虑。

    (dp[i])表示到达节点(i)的松鼠数量,很容易可以转移,但每个深度的求解复杂度是(O(n)),最坏情况即链为(O(n^{2}))会炸。

    但我们发现松鼠在往根跑的时候好多情况是一样的(即减少的松鼠数为深度的减少数),只是在多处松鼠的所谓的"汇聚点"即(LCA)处会发生变化。于是我们可以可以把树压缩,保留那些关键的转移点(即(LCA)),去掉那些可以预知变化的点(即起始点到LCA间的点)。

    其实这就是要建一棵虚树,用栈维护当前建的一条从根节点开始的链。虚树(DP)

    最终复杂度即为(O(n))

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    struct data{
        int deep,t,id;
    
        bool operator < (const data & a) const{
            if (deep<a.deep) return true;
            if (deep>a.deep) return false;
            if (t<a.t) return true;
            return false;
        }
    };
    
    const int N=2e5+8;
    
    vector<data> po;
    
    vector<int> edge[N];
    
    int up[N][32];
    
    LL dp[N];
    
    int deep[N];
    
    int n,s,t;
    
    LL cnt[N];
    
    LL ans;
    
    void DFS(int u,int fa){
        deep[u]=deep[fa]+1;
        po.push_back({deep[u],++t,u});
        up[u][0]=fa;
        for(auto v:edge[u]){
            if (v==fa) continue;
            DFS(v,u);
        }
    }
    
    int lca(int u,int v){
        if (u==v) return u;
        if (deep[u]<deep[v]) swap(u,v);
        for(int i=31;i>=0;--i){
            if (deep[up[u][i]]>=deep[v])
                u=up[u][i];
        }
        if (u==v) return u;
        for(int i=31;i>=0;--i){
            if (up[u][i]!=up[v][i]){
                u=up[u][i];
                v=up[v][i];
            }
        }
        return up[u][0];
    }
    
    int st[N],top;
    
    int head[N],to[N*2],nxt[N*2],num;
    
    void add(int u,int v){
        num++;
        nxt[num]=head[u];
        to[num]=v;
        head[u]=num;
    }
    
    void DP(int u,int fa){
        for(int v,i=head[u];i;i=nxt[i]){
            v=to[i];
            if (v==fa) continue;
            DP(v,u);
            if (dp[v]) dp[u]+=max(1ll,dp[v]-deep[v]+deep[u]+1);
            dp[v]=0;
            head[u]=0;
        }
        if (dp[u]) dp[u]=max(1ll,dp[u]-1);
    }
    
    void solve(int l,int r){
        num=0;
        top=0;
        st[top]=s;
        int la=s;
        for(int i=l;i<=r;++i){
            dp[po[i].id]=cnt[po[i].id];
            int cur=po[i].id;
            int fa=lca(la,cur);
            while(top>0&&deep[st[top-1]]>=deep[fa]){
                add(st[top-1],st[top]);
                --top;
            }
            if (st[top]!=fa){
                add(fa,st[top]);
                st[top]=fa;
            }
            st[++top]=cur;
            la=cur;
        }
        while(top>0){
            add(st[top-1],st[top]);
            --top;
        }
        DP(s,s);
        ans+=dp[s];
        dp[s]=0;
    }
    
    int main(){
        read(n);
        read(s);
        for(int i=1;i<=n;++i) read(cnt[i]);
        for(int u,v,i=1;i<n;++i){
            read(u);
            read(v);
            edge[u].push_back(v);
            edge[v].push_back(u);
        }
        DFS(s,s);
        for(int i=1;i<=31;++i)
            for(int j=1;j<=n;++j)
                up[j][i]=up[up[j][i-1]][i-1];
        sort(po.begin(),po.end());
        int l=1;
        if (cnt[po[0].id]) ans=max(1ll,cnt[po[0].id]-1);
        for(int i=2;i<n;++i){
            if (po[i].deep!=po[i-1].deep){
                solve(l,i-1);
                l=i;
            }
        }
        solve(l,n-1);
        write(ans,'
    ');
    }
    


    H. 纸牌游戏 (Nowcoder 5278 H)

    题目大意

    给定长度为(n)的数字串,每个数字(0)(9),要求从中选取(k)个数字组成一个非负整数,使得它可以被(3)整除且最大。

    解题思路

    首先对数字串中的数字从大到小排序,然后设(dp[i][j][k])表示当前第(i)个数,还可以取(j)个数,当前选的数组成的数对(3)取模为(k),后继是否存在可行方案(记忆化搜索,含义似乎难以描述qwq),转移就看这第(i)个数选或不选两种方式。时间复杂度(O(nk))爆了空间和时间。

    稍加思索会发现我们定义的状态里有好多***冗余重复 ***的状态,如(9999),我们会考虑了多次选(1)(9)(2)(9)的情况或(3)(9)的情况,于是我们改变定义方式为(dp[i][j][k])为当前考虑选择数字(i)(从9开始考虑),还有(j)个可以选,对(3)取模为(k)后继是否存在可行方案,转移即可。时间复杂度(O(9n*3))

    特判全是(0)(k eq 1)的情况。

    多组数据,不能每组(memset),于是玄学操作——自定义真假未定义。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N=1e5+8;
    
    char s[N];
    
    int cc[10];
    
    int tt=0;
    
    int dp[10][N][3];
    
    int sum[10];
    
    bool DFS(int num,int k,int mo,int cnt[]){
        if (k==0) return mo==0;
        if (num<0) return false;
        if (k>sum[num]) return false; //可行性剪枝
        if (dp[num][k][mo]>tt) return dp[num][k][mo]==tt+2;
        for(int i=min(cnt[num],k);i>=0;--i){
            if (DFS(num-1,k-i,(mo+num*i)%3,cnt)){
                cc[num]=i;
                dp[num][k][mo]=tt+2;
                return true;
            }
        }
        dp[num][k][mo]=tt+1;
        return false;
    }
    
    int main(void) {
        int kase; read(kase);
        for (int ii = 1; ii <= kase; ii++){
            scanf("%s",s);
            int k;
            read(k);
            int cnt[10]={0};
            int len=strlen(s);
            for(int i=0;i<len;++i)
                cnt[s[i]-'0']++;
            for(int i=0;i<10;++i) cc[i]=0;
            sum[0]=cnt[0];
            for(int i=1;i<10;++i) sum[i]=cnt[i]+sum[i-1];
            bool qwq=DFS(9,k,0,cnt);
            if (qwq&&(cc[0]!=k||k==1)){
                char ans[k+1]={0};
                int cr=0;
                for(int i=9;i>=0;--i){
                    while(cc[i]){
                        ans[cr++]=i+'0';
                        cc[i]--;
                    }
                }
                printf("%s
    ",ans);
            }else puts("-1");
            tt+=3;
        }
        return 0;
    }
    


    I. 古老的打字机 (Nowcoder 5278 I)

    题目大意

    给定(n)个串(s_{i})以及对应的价值(v_{i}),现在会敲击打字机(m)次,每次会等概率的敲击出一个小写字母或者退格键,退格键的作用是将当前得到的字符串的最后一个字母删除,当然如果当前是空串,则无事发生。问得到的字符串(t)的价值的期望的(27^{m})倍是多少。

    字符串(t)的价值定义为:每个串(s_i)在得到的串t中的出现次数(c_i)与价值(v_i)的乘积,即(sumlimits_{k=1}^{n} sumlimits_{i=1}^{|t|} sumlimits_{j=1}^{|t|}v_{k}[s_k=substr(t,i,j)]).

    解题思路

    根据期望的定义,答案就是(frac{ ext{乱七八糟}}{27^{m}}),我们现在考虑 乱七八糟 怎么算。

    乱七八糟 就是全部可能的字符串乘以对应的价值,直接计算的复杂度是(27^{m})。我们考虑如何合并。

    我们先按长度进行分类,对于同一长度的字符串(t),它在(27^{m})的情况中出现的次数都是一样的。这一长度的总价值计算,从得到的字符串(t)来考虑显然不现实,我们就经典转换成考虑每个(s_{i})对该长度总价值的贡献。

    设当前考虑的字符串长度为(l),对于一个长度为(|s_{i}|)小于等于(l)的串(s_{i}),它对字符串(t)长度为(l)时的总价值贡献就为(v_{i} imes (l-|s_{i}|+1) imes 26^{l-|s_{i}|} imes dfrac{dp[m][l]}{26^{l}})

    (dp[m][l])表示敲击(m)次,得到串(t)长度为(l)的方案数。这个一开始预处理即可得到。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const LL mo=1e9+7;
    
    const int N=1e3+8;
    
    int n,m;
    
    int len[N];
    
    LL val[N];
    
    LL ans;
    
    LL dp[N][N];
    
    LL p26[N];
    
    char s[N];
    
    LL qpower(LL a,LL b){
        LL qwq=1;
        while(b){
            if (b&1) qwq=qwq*a%mo;
            b>>=1;
            a=a*a%mo;
        }
        return qwq;
    }
    
    LL inv(LL x){
        return qpower(x,mo-2);
    }
    
    int main(void) {
        read(n);
        read(m);
        for(int i=1;i<=n;++i){
            scanf("%s",s);
            len[i]=strlen(s);
            read(val[i]);
        }
        p26[0]=1;
        for(int i=1;i<=1004;++i) p26[i]=p26[i-1]*26ll%mo;
        dp[0][0]=1;
        for(int i=1;i<=m;++i)
            for(int j=0;j<=i;++j){
                if (j==0) dp[i][j]=(dp[i-1][j]+dp[i-1][j+1])%mo;
                else if (j>=i-1) dp[i][j]=dp[i-1][j-1]*26ll%mo;
                else dp[i][j]=(dp[i-1][j-1]*26ll%mo+dp[i-1][j+1])%mo;
            }
        LL tmp=1;
        LL qwq=1;
        for(int i=0;i<=m;++i){
            for(int j=1;j<=n;++j){
                if (len[j]>i) continue;
                ans=(ans+(i-len[j]+1ll)*p26[i-len[j]]%mo*val[j]%mo*dp[m][i]%mo*qwq%mo)%mo;
            }
            tmp=tmp*26%mo;
            qwq=inv(tmp);
        }
        write(ans,'
    ');
        return 0;
    }
    


    J. 能到达吗 (Nowcoder 5278 J)

    题目大意

    给定一张(n*m)的图,左上角坐标((1,1)),右下角坐标((n,m)),有(k)个障碍物,第(i)个障碍物坐标为((x_{i},y_{i}))。你可以在空地上上下左右走动,但不能跳出边界。问俩俩能到达的无序点对(即从一个点可以到达另外一个点)有多少。答案模(1e9+7)

    解题思路

    很显然,如果一个连通块有(cnt)个点,那么这个连通块对答案的贡献就为(dfrac{cnt*(cnt-1)}{2}+cnt),我们考虑如何求出每个连通块中的点数。

    由于(n,m leq 2 cdot 10^{5}),而(k leq 10^{6}),我们用一根扫描线对这张图从上往下扫描,当前第(i)行,在该行的障碍物把该行分成了若干条互不相交的线段,这些线段归属于哪个连通块我们可以用并查集维护。

    然后考虑第(i+1)行,在该行的障碍物也把该行分成了若干条互不相交的线段,现在我们对第(i)行和第(i+1)行的线段进行 合并 ,这里的 合并 指的是连通块的合并,即如果这两行中的两条线段是相交的,则它们应属于同一连通块,合并完后我们拿第(i+1)行的线段与(i+2)行的线段继续合并即可。合并的过程就是一个模拟的过程。

    对于该行没有障碍物的,则视为一条线段,如果有连续若干行无障碍物,这我们可以把这若干行压成一行,视为一条线段,与前一行或后一行合并。

    特殊处理无障碍物的情况。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const LL mo=1e9+7;
    
    const int N=5e6+8;
    
    LL n,m,k;
    
    int f[N],tot;
    
    LL cnt[N];
    
    pair<int,int> po[N];
    
    struct data{
    	int l,r,id;
    };
    
    vector<data> seg[2];
    
    int cur;
    
    LL ans;
    
    LL inv2;
    
    int findfa(int x){
    	if (f[x]==x) return x;
    	else return f[x]=findfa(f[x]);
    }
    
    bool check(data & a,data & b){
    	return a.r>=b.l&&a.l<=b.r;
    }
    
    void unionn(){
    	if (seg[cur^1].empty()) return;
    	int l=0;
    	for(auto & i:seg[cur]){
    		while(true){
    			if (l<(int)seg[cur^1].size()&&check(i,seg[cur^1][l])){
    				int fa=findfa(i.id);
    				int fb=findfa(seg[cur^1][l].id);
    				if (fa!=fb){
    					f[fa]=fb;
    					cnt[fb]=(cnt[fb]+cnt[fa])%mo;
    				}
    				++l;
    			}else{
                    if (l==(int)seg[cur^1].size()||seg[cur^1][l].l>i.r){
                        l=max(l-1,0);
                        break;
                    }else ++l;
    			}
    		}
    	}
    }
    
    void work(int u,int d){
        if (u>d) return;
    	cur^=1;
    	while(!seg[cur].empty()) seg[cur].pop_back();
    	++tot;
    	f[tot]=tot;
    	cnt[tot]=(LL)(d-u+1ll)*m%mo;
    	seg[cur].push_back({1,(int)m,tot});
    	unionn();
    }
    
    void solve(int l,int r){
        if (l>r) return;
    	int la=0;
    	cur^=1;
        while(!seg[cur].empty()) seg[cur].pop_back();
    	for(int i=l;i<=r;++i){
    		if (po[i].second-la>1){
    			++tot;
    			f[tot]=tot;
    			cnt[tot]=po[i].second-la-1;
    			seg[cur].push_back({la+1,po[i].second-1,tot});
    		}
    		la=po[i].second;
    	}
    	if (m>la){
    		++tot;
    		f[tot]=tot;
    		cnt[tot]=m-la;
    		seg[cur].push_back({la+1,(int)m,tot});
    	}
    	unionn();
    }
    
    LL qpower(LL a,LL b){
    	LL qwq=1;
    	while(b){
    		if (b&1) qwq=qwq*a%mo;
    		b>>=1;
    		a=a*a%mo;
    	}
    	return qwq;
    }
    
    LL inv(LL x){
    	return qpower(x,mo-2);
    }
    
    int main(void) {
        int kase; read(kase);
        inv2=inv(2);
        for (int ii = 1; ii <= kase; ii++){
            tot=0;
            ans=0;
            while(!seg[0].empty()) seg[0].pop_back();
            while(!seg[1].empty()) seg[1].pop_back();
            read(n);
            read(m);
            read(k);
            if (k==0) ans=((n*m%mo)*((n*m-1ll)%mo)%mo*inv2%mo+n*m%mo)%mo;
            else{
                for(int i=1;i<=k;++i) read(po[i].first),read(po[i].second);
                sort(po+1,po+1+k);
                int la=0;
                int l=1;
                work(1,po[1].first-1);
                la=po[1].first;
                for(int i=2;i<=k;++i){
                    if (po[i].first!=la){
                        solve(l,i-1);
                        if (po[i].first-la>1) work(la+1,po[i].first-1);  //有空行
                        l=i;
                        la=po[i].first;
                    }
                }
                solve(l,k);
                la=po[k].first;
                if (n>la){
                    work(la+1,n);
                }
                for(int i=1;i<=tot;++i){
                    if (f[i]==i){
                        ans=(ans+cnt[i]*(cnt[i]-1)%mo*inv2%mo+cnt[i])%mo;
                    }
                }
            }
            write(ans,'
    ');
            }
        return 0;
    }
    


    K. 迷宫 (Nowcoder 5278 K)

    题目大意

    图上一些地方有障碍物(X),有一个地方是起点(S),有一个地方是终点(T)。现要求从起点移动到终点,一次操作可以上下左右移动一格,但不能超出边界。同时给定一个整数(d),可以使用一次技能,即从当前点(a)移动到另一点(b),两点的切比雪夫(横坐标差与纵坐标差的最大值)距离不得大于(d),使用一次技能算一步操作。问从起点到终点的最小操作次数,并输出一种可行移动方案。

    解题思路

    切比雪夫距离对应的就是一个正方形的范围。

    我们从起点开始BFS,再从终点开始BFS,记录每个点到起点和终点的距离。

    然后再枚举一个边长为(d+1)的正方形范围,设这个范围中到起点的最小值为(s)和到终点的最小值(t),则在该范围使用技能后的最小操作次数就是(s+t+1),对所有范围取最小值即为答案。

    至于维护二维正方形的最小值,用两次单调队列即可。先对行的每个数维护它右边(d)个的最小值,再对每一列,用这个最小值,维护每行向下(d)个数的最小值。

    至于输出方案,再记录前驱和取得的最小值的位置吧。

    特殊处理(d=0)的情况。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N=2e3+8;
    
    const int dx[4]={0,0,1,-1};
    
    const int dy[4]={-1,1,0,0};
    
    struct data{
        int val;
        int x,y;
    
        bool operator < (const data & a) const{
            return val<a.val;
        }
    
        operator int(){
            return val;
        }
    }st[N][N],en[N][N],ast,aen;
    
    char ma[N][N];
    
    int n,m,d;
    
    int ans;
    
    int sx,sy,ex,ey;
    
    pair<int,int> pre_st[N][N],pre_en[N][N];
    
    void BFS_st(){
        st[sx][sy].val=0;
        pre_st[sx][sy]=make_pair(sx,sy);
        queue<pair<int,int>> team;
        team.push(make_pair(sx,sy));
        while(!team.empty()){
            auto u=team.front();
            team.pop();
            for(int i=0;i<4;++i){
                auto v=make_pair(u.first+dx[i],u.second+dy[i]);
                if (ma[v.first][v.second]=='X') continue;
                if (st[v.first][v.second]>st[u.first][u.second]+1){
                    st[v.first][v.second].val=st[u.first][u.second].val+1;
                    team.push(v);
                    pre_st[v.first][v.second]=u;
                }
            }
        }
    }
    
    void BFS_en(){
        en[ex][ey].val=0;
        pre_en[ex][ey]=make_pair(ex,ey);
        queue<pair<int,int>> team;
        team.push(make_pair(ex,ey));
        while(!team.empty()){
            auto u=team.front();
            team.pop();
            for(int i=0;i<4;++i){
                auto v=make_pair(u.first+dx[i],u.second+dy[i]);
                if (ma[v.first][v.second]=='X') continue;
                if (en[v.first][v.second]>en[u.first][u.second]+1){
                    en[v.first][v.second].val=en[u.first][u.second].val+1;
                    team.push(v);
                    pre_en[v.first][v.second]=u;
                }
            }
        }
    }
    
    void pre_s(){
        deque<data> team;
        for(int i=1;i<=n;++i){
            for(int j=1;j<=min(m,d+1);++j){
                while(!team.empty()&&team.back()>=st[i][j]) team.pop_back();
                team.push_back(st[i][j]);
            }
            st[i][1]=team.front();
            for(int j=min(m,d+1)+1;j<=m;++j){
                while(!team.empty()&&team.front().y<j-d) team.pop_front();
                while(!team.empty()&&team.back()>=st[i][j]) team.pop_back();
                team.push_back(st[i][j]);
                st[i][j-d]=team.front();
    
            }
            while(!team.empty()) team.pop_back();
        }
        for(int i=1;i<=max(1,m-d);++i){
            for(int j=1;j<=min(n,d+1);++j){
                while(!team.empty()&&team.back()>=st[j][i]) team.pop_back();
                team.push_back(st[j][i]);
            }
            st[1][i]=team.front();
            for(int j=min(n,d+1)+1;j<=n;++j){
                while(!team.empty()&&team.front().x<j-d) team.pop_front();
                while(!team.empty()&&team.back()>=st[j][i]) team.pop_back();
                team.push_back(st[j][i]);
                st[j-d][i]=team.front();
            }
            while(!team.empty()) team.pop_back();
        }
    }
    
    void pre_e(){
        deque<data> team;
        for(int i=1;i<=n;++i){
            for(int j=1;j<=min(m,d+1);++j){
                while(!team.empty()&&team.back()>=en[i][j]) team.pop_back();
                team.push_back(en[i][j]);
            }
            en[i][1]=team.front();
            for(int j=min(m,d+1)+1;j<=m;++j){
                while(!team.empty()&&team.front().y<j-d) team.pop_front();
                while(!team.empty()&&team.back()>=en[i][j]) team.pop_back();
                team.push_back(en[i][j]);
                en[i][j-d]=team.front();
    
            }
            while(!team.empty()) team.pop_back();
        }
        for(int i=1;i<=max(1,m-d);++i){
            for(int j=1;j<=min(n,d+1);++j){
                while(!team.empty()&&team.back()>=en[j][i]) team.pop_back();
                team.push_back(en[j][i]);
            }
            en[1][i]=team.front();
            for(int j=min(n,d+1)+1;j<=n;++j){
                while(!team.empty()&&team.front().x<j-d) team.pop_front();
                while(!team.empty()&&team.back()>=en[j][i]) team.pop_back();
                team.push_back(en[j][i]);
                en[j-d][i]=team.front();
            }
            while(!team.empty()) team.pop_back();
        }
    }
    
    void print_st(int x,int y){
        if (x==sx&&y==sy){
            printf("%d %d
    ",sx-1,sy-1);
            return;
        }
        print_st(pre_st[x][y].first,pre_st[x][y].second);
        printf("%d %d
    ",x-1,y-1);
    }
    
    void print_en(int x,int y){
        if (x!=ast.x||y!=ast.y) printf("%d %d
    ",x-1,y-1);
        if (x==ex&&y==ey) return;
        int tmp=x;
        x=pre_en[tmp][y].first;
        y=pre_en[tmp][y].second;
        while(x!=ex||y!=ey){
            printf("%d %d
    ",x-1,y-1);
            tmp=x;
            x=pre_en[tmp][y].first;
            y=pre_en[tmp][y].second;
        }
        if (x!=ast.x||y!=ast.y) printf("%d %d
    ",x-1,y-1);
    }
    
    int main(){
        read(n);
        read(m);
        read(d);
        for(int i=1;i<=n;++i) scanf("%s",ma[i]+1);
        for(int i=1;i<=n;++i) ma[i][0]=ma[i][m+1]='X';
        for(int i=1;i<=m;++i) ma[0][i]=ma[n+1][i]='X';
        for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){
            if (ma[i][j]=='S'){ sx=i; sy=j;}
            if (ma[i][j]=='T'){ ex=i; ey=j;}
            st[i][j]=en[i][j]={1000000007,i,j};
        }
        BFS_st();
        BFS_en();
        ans=1e9+7;
        if (d==0){
            for(int i=1;i<=n;++i) 
                for(int j=1;j<=m;++j) 
                    if (st[i][j]+en[i][j]<ans){
                        ans=st[i][j]+en[i][j];
                        ast=st[i][j];
                        aen=en[i][j];
                    }
        }else{
            pre_s();
            pre_e();
            for(int i=1;i<=max(1,n-d);++i)
                for(int j=1;j<=max(1,m-d);++j)
                    if (st[i][j]+en[i][j]+1<ans){
                        ans=st[i][j]+en[i][j]+1;
                        ast=st[i][j];
                        aen=en[i][j];
                    }
        }
        if (ans==1000000007) ans=-1;
        write(ans,'
    ');
        if (ans!=-1){
            print_st(ast.x,ast.y);
            print_en(aen.x,aen.y);
        }
        return 0;
    }
    


    L. 动物森友会 (Nowcoder 5278 L)

    题目大意

    一周七天,一周的某些天可以执行特定的事件,一天最多执行(e)次事件,同一个事件可以一天可以执行多次。

    现在列出要执行的某些事件和且执行该事件的次数,问执行完这些事件所需的最小天数。

    解题思路

    很显然,当天数足够大时,一定能执行完所有事件。

    天数对决策的可行具有单调性,于是我们二分天数,对于一个特定的天数,一周的某些天的执行次数是一定的,我们的任务就是分配这些执行次数,该去执行哪些事件,执行几次,这是带有反悔性质的抉择,跑一遍网络流即可。

    神奇的代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N=2e4+8;
    
    const int INF=1e9+7;
    
    int head[N],nxt[N*2],to[N*2],team[N*2],dis[N*2];
    
    LL b_flow[N*2],flow[N*2];
    
    int n,e,st,en,num;
    
    LL sum;
    
    void add(int u, int v, int w) {
    	num++;
    	nxt[num] = head[u];
    	to[num] = v;
    	flow[num] = w;
    	head[u] = num;
    	num++;
    	nxt[num] = head[v];
    	to[num] = u;
    	flow[num] = 0;
    	head[v] = num;
    }
    
    bool BFS() {
    	int l = 0, r = 1;
    	team[1] = st;
    	memset(dis, 0, sizeof(dis));
    	dis[st] = 1;
    	while (l < r) {
    		int u = team[++l];
    		for (int v, i = head[u]; i; i = nxt[i]) {
    			v = to[i];
    			if (dis[v] == 0 && flow[i]) {
    				dis[v] = dis[u] + 1;
    				team[++r] = v;
    			}
    		}
    	}
    	if (dis[en]) return true;
    	else return false;
    }
    
    LL DFS(int u, LL f) {
    	if (u == en) return f;
    	LL qwq = 0, tmp = 0;
    	for (int v, i = head[u]; i; i = nxt[i]) {
    		v = to[i];
    		if (dis[v] == dis[u] + 1 && flow[i]) {
    			qwq = DFS(v, min(f - tmp, flow[i]));
    			flow[i] -= qwq;
    			flow[i ^ 1] += qwq;
    			tmp += qwq;
    			if (tmp == f) return tmp;
    		}
    	}
    	return tmp;
    }
    
    bool check(int x){
        for(int i=2;i<=num;++i) flow[i]=b_flow[i];
        for(int i=head[st];i;i=nxt[i])
            flow[i]=(LL)((x-1)/7+((x-1)%7>=to[i]-1))*(LL)e;
        LL cnt=0;
        while(BFS()){
            cnt+=DFS(st,INF);
        }
        return cnt==sum;
    }
    
    int main(void) {
        read(n);
        read(e);
        num=1;
        st=0;
        sum=0;
        en=n+8;
        for(int i=1;i<=7;++i)
            add(st,i,0);
        for(int c,m,i=1;i<=n;++i){
            read(c);
            read(m);
            sum+=c;
            add(i+7,en,c);
            for(int v,j=1;j<=m;++j){
                read(v);
                add(v,i+7,INF);
            }
        }
        for(int i=2;i<=num;++i)
            b_flow[i]=flow[i];
        int l=0,r=(sum+1)*8;
        int ans=0;
        while(l+1<r){
            int mid=(l+r)>>1;
            if (check(mid)) ans=mid,r=mid;
            else l=mid;
        }
        printf("%d
    ",ans);
        return 0;  
    }
    


  • 相关阅读:
    构建git+gerrit+repo的Android代码服务器
    简单学习:repo入门
    git checkout 命令详解
    python学习第一天
    window下的git工具msysgit的使用
    git基本操作
    echarts 使用示例
    管道命令
    jquery修改文档结构
    JavaScript正则表达式
  • 原文地址:https://www.cnblogs.com/Lanly/p/12865951.html
Copyright © 2020-2023  润新知