• 2017南开ACM校赛(网络赛) 民间题解


    orz 首先说一下这个只是民间题解,可能会有很多错误

    程序还没有评测,所以可能存在问题

    C题比赛的时候没想到。。后来发现是个模板题,所以没有代码

    希望这份题解能对读者有所启发吧。。。

    A题

    直接倒序枚举即可

    因为一个数n最短减sqrt(n)次就可以变成回文数

    所以复杂度是sqrt(n)的

    (也可以利用字符串做法,会更快)

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    using namespace std;
    
    inline bool ok(int x){
        int a[10], n = 0;
        while(x != 0){
            a[n++] = x%10;
            x/=10;
        }
        for(int i = 0; i < n; i++){
            if(a[i] != a[n-i-1]) return false;
        }
        return true;
    }
    
    int huiwen(int x){
        for(int i = x; i >= 0; i--){
            if(ok(i)) return i;
        }
        return 0;
    }
    int x;
    int main(){
        cin>>x;
        if(x == 0) { cout<<x<<endl; return 0; }
        while(x != 0){
            int y = huiwen(x);
            if(y == 0) break;
            cout<<y<<endl;
            x -= y;
        }
    }

    B题 

    动态规划,不妨设当前已经规划完了前面i个点的分配方案

    即dp[i][m]表示从第i点以后要用m条边来匹配,所能获得的最大权值

    那么如果一个方案合法就必须满足,之前的分配数x和y之间有

    x - (i-1)*(i-2) <= y  也就是之前区间相互连接,这样会使与后方连的边尽量少

    那么如果x - (i-1)*(i-2) > y ,意思就是之前区间不管如何连接,后方不可能与前方匹配

    比如4 4 4 2 2这种情况, 后面的2个2是无法与前面的3个4匹配,即不合法

    还有就是后面每个都至少分配一条边

    所以每次dp都需要判断一下这两个条件

    然后我们还可以发现,我们只需要给出一个2*m的合法拆分方案,就是一个合法的解,跟给出顺序是无关的

    所以不妨设第一个点分配最多的边,剩下的大小也是按照次序的

    这样会给dp带来很大方便

    然后利用记忆化搜索来dp就很好写了

    (代码中注释掉的那一部分可以给出一个方案)

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    using namespace std;
    typedef long long LL;
    LL dp[501][2002], a[501], G[501][2002], H[501][2002];
    int n, m, M;
    const LL inf = 1e8;
    LL dfs(int x, int m, int Max){
        if(n-x+1 > m) return -inf;
        if(M - m - (x-1)*(x-2) > m) return -inf;
        if(x == n)    
            if(m <= n-1 && m <= Max) return a[m];
            else return -inf;
        if(H[x][m] > 0) return dp[x][m];
        H[x][m] = 1;
        for(int i = min(n-1, min(Max, m)); i >= 1; i--){
            int k = dfs(x+1, m-i, i) + a[i];
            if(k > 0){
                if(k > dp[x][m]){
                    dp[x][m] = k;
                    G[x][m] = m-i;
                }
            }
        }
        return dp[x][m];
    }
    
    int main(){
        cin>>n>>m; M = 2*m;
        for(int i = 1; i < n; i++) cin>>a[i];
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= 2*m; j++)
                dp[i][j] = -inf;
        cout<<dfs(1, 2*m, m)<<endl;
        /*int x = M;
        for(int i = 1; i <= n; i++){
            cout<<G[i][x]<<" ";
            x = G[i][x];
        }*/
    }

    C题

    费用流,最长K可重区间集问题

    模板题

    见http://hzwer.com/5842.html

    D题

    计算几何

    不妨先把凸多边形的每条边,往它的单位向量的法向量方向平移r的长度

    这样构成了一个新的凸多边形,

    在这个新的凸多边形求最远的那两个点就是答案

    构成新的凸多边形的过程需要用到半平面交算法(模板)

    求最远的两个点需要用到旋转卡壳算法(模板)

    所以就是模板题

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    const int maxn = 200500;
    const double eps = 1e-7;
    struct Vector{
        double x, y;
        Vector(double _x=0, double _y=0):x(_x), y(_y) {}
        double len() {    return sqrt(x*x + y*y); }
        Vector operator /(double v){ return Vector(x/v, y/v); }
        Vector operator *(double v){ return Vector(x*v, y*v); }
        void print(){
            cout<<x<<" "<<y<<endl;
        }
    }points[maxn], pp[maxn], px, py;
    typedef Vector Point;
    Vector operator - (Vector A, Vector B)
    { return Vector(A.x - B.x, A.y - B.y); }
    Vector operator + (Vector A, Vector B)
    { return Vector(A.x + B.x, A.y + B.y); }
    Vector operator < (const Point &A, const Point &B)
    { return (A.x == B.x) ? A.y < B.y : A.x < B.x; }
    double Distance(Point A, Point B)
    { return sqrt(pow(A.x - B.x, 2) + pow(A.y - B.y, 2)); }
    struct Line{
        Point p;
        Vector v;
        double ang;
        Line() {}
        Line(Point A, Point B){
            p = A;
            v = (A-B)/Distance(A, B);
            ang = atan2(v.y, v.x);
        }
        bool operator <(const Line &L) const { return ang < L.ang; }    
    }lines[maxn];
    double Cross(Vector A, Vector B)
    { return A.x*B.y - A.y*B.x; }
    bool OnLeft(Line L, Point p){
        return Cross(L.v, p - L.p) > 0;
    }
    Point GetIntersection(Line a, Line b){
        Vector u = a.p - b.p;
        double t = Cross(b.v, u)/Cross(a.v, b.v);
        return a.p+a.v*t;
    }
    int HalfplaneIntersection(Line *L, int n, Point *poly){
        sort(L, L+n);
        int first, last;
        Point *p = new Point[n];
        Line *q = new Line[n];
        q[first = last = 0] = L[0];
        for(int i = 1; i < n; i++){
            while(first < last && !OnLeft(L[i], p[last-1])) last--;
            while(first < last && !OnLeft(L[i], p[first])) first++;
            q[++last] = L[i];
            if(fabs(Cross(q[last].v, q[last-1].v)) < eps){
                last--;
                if(OnLeft(q[last], L[i].p)) q[last] = L[i];
            }
            if(first < last) p[last-1] = GetIntersection(q[last-1], q[last]);
        }
        while(first < last && !OnLeft(q[first], p[last-1])) last--;
        if(last - first <= 1) return 0;
        p[last] = GetIntersection(q[last], q[first]);
        int m = 0;
        for(int i = first; i <= last; i++) poly[m++] = p[i];
        return m;
    }
    
    int n, r;
    int main(){
        cin>>n>>r;
        cin>>points[0].x>>points[0].y;
        for(int i = 1; i < n; i++){
            cin>>points[i].x>>points[i].y;    
            lines[i-1] = Line(points[i-1], points[i]);
        }
        lines[n-1] = Line(points[n-1], points[0]);
        for(int i = 0; i < n; i++){
            Vector v = lines[i].v;
            v = Vector(-v.y, v.x);
            v = v/v.len();
            lines[i].p = lines[i].p + v*r;
            //lines[i].p.print();
        }
        int N = HalfplaneIntersection(lines, n, pp);
        pp[N] = pp[0];
        int now = 1;
        double ans = 0;
        //cout<<N<<endl;
        for(int i = 0; i < N; i++){
            while(Cross(pp[i+1]-pp[i], pp[now]-pp[i]) < Cross(pp[i+1]-pp[i], pp[now+1]-pp[i])){
                now++;
                if(now == N) now = 0;    
            }
            if(Distance(pp[now], pp[i]) > ans){
                ans = Distance(pp[now], pp[i]);
                px = pp[now]; py = pp[i];
            }
        }
        cout<<px.x<<" "<<px.y<<" ";
        cout<<py.x<<" "<<py.y<<endl;
    }

    E题

    bfs问题

    利用宽度优先搜索和哈希判重,可以很快做出来

    因为状态数 9!只有10^5的数量级

    这样可以得到很多分数,但是询问过多,也会超时

    所以直接以最后的结果为根,然后建立一个搜索树

    这样每次查询都是询问父亲到根的一条链

    因为深度不超过32

    所以每次查询的复杂度也就只有O(32)

    就可以通过了

    (如果发现结点不在树上,就是无解)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <queue>
    #define mp make_pair
    #define fi first
    #define se second
    using namespace std;
    typedef long long LL;
    typedef pair<LL, int> PLI;
    typedef pair<int, int> PII;
    const int MOD = 123456;
    const int dx[4] = {0, 1, 0, -1};
    const int dy[4] = {1, 0, -1, 0};
    const char ch[4] = {'u', 'l', 'd', 'r'};
    vector<char> V;
    vector<PLI> G[MOD+1];
    vector<PII> pa;
    LL Pow[20];
    LL get(int r, int c){
        return Pow[(2-r)*3 + (2-c)];
    }
    
    void Hinsert(LL x, int v){
        G[x%MOD].push_back(mp(x, v));
    }
    int Hfind(LL x){
        for(int i = 0; i < G[x%MOD].size(); i++){
            PII u = G[x%MOD][i];
            if(u.fi == x) return u.se;
        }
        return -1;
    }
    void Herase(){
        for(int i = 0; i < MOD; i++) G[i].clear();
    }
    int n, tot, x;
    LL nowS;
    queue<PLI> Q; 
    queue<PII> Qxy;
    
    int main(){
        freopen("我钦定wps第一", "r", stdin);
        Pow[0] = 1;
        for(int i = 1; i < 12; i++) Pow[i] = Pow[i-1]*10;
        Q.push(mp(123456780, 0));
        Qxy.push(mp(2, 2));
        Hinsert(123456780, 0);
        pa.push_back(mp(0, 0));
        while(!Q.empty()){
            PLI x = Q.front(); Q.pop();
            PII loc = Qxy.front(); Qxy.pop();
            if(x.se >= 1000) break;
            //cout<<x.fi<<endl;
            for(int i = 0; i < 4; i++){
                int newx = loc.fi + dx[i];
                int newy = loc.se + dy[i];
                if(newx < 0 || newx > 2) continue;
                if(newy < 0 || newy > 2) continue;
                int t = x.fi/get(newy, newx)%10;
                LL newS = x.fi - t*get(newy, newx) + t*get(loc.se, loc.fi);
                if(Hfind(newS) == -1){
                    Hinsert(newS, ++tot);
                    pa.push_back(mp(Hfind(x.fi), i));
                    Q.push(mp(newS, x.se+1));
                    Qxy.push(mp(newx, newy));
                }
            }
        }
        cin>>n;
        while(n--){
            LL state = 0;
            for(int i = 0; i < 9; i++) cin>>x, state = state*10 + x;
            if(Hfind(state) != -1){
                x = Hfind(state);
                while(x != 0){
                    cout<<ch[pa[x].se];
                    x = pa[x].fi;
                }
                cout<<endl;
            } else cout<<"unsolvable"<<endl;
        }
    }

    F题

    矩阵优化问题

    考虑由递推式构造一个线性变换矩阵

    a[i] = p*a[i-1] + (q-p)*a[i-2] + m*i^c

    把i^c看成b[i]

    因为c小于等于2, 那么线性变换矩阵就A可以这样构造

    1 1 1

    0 1 2

    0 0 1

    这个矩阵乘i次后,A[0][0] = 1, A[1][1] = i, A[2][2] = i^2

    然后和a[i]的递推式合在一起

    就是1 1 1 0 0

          0 1 2 0 0

          0 0 1 0 0

          0 0 0 0 q-p

          0 0 0 1  p

    只需要在A[c][4]的位置填上m就可以了

    最后A自乘(n-3)次,与行向量(1, 3, 9, a1, a2)乘起来就是答案

    矩阵可以进行快速幂,所以复杂度是O(logn)

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    using namespace std;
    const int MOD = 10007;
    int Mn = 5;
    typedef long long LL;
    struct Matrix{
        LL mat[10][10];
        Matrix() { memset(mat, 0, sizeof(mat)); }
        Matrix operator *(const Matrix &B) const{
            Matrix ans;
            for(int i = 0; i < Mn; i++){
                for(int j = 0; j < Mn; j++){
                    LL temp = 0;
                    for(int k = 0; k < Mn; k++)
                        (temp += mat[i][k]*B.mat[k][j]) %= MOD;
                    ans.mat[i][j] = (temp + MOD) % MOD;
                }
            }
            return ans;
        }
        void print(){
            for(int i = 0; i < Mn; i++){
                for(int j = 0; j < Mn; j++)
                    cout<<mat[i][j]<<" ";
                cout<<endl;
            }
        }
    }A;
    Matrix powmod(Matrix A, int b){
        Matrix ans;
        for(int i = 0; i < Mn; i++) ans.mat[i][i] = 1;
        for(; b; b >>= 1, A = A*A) if(b&1) ans = ans*A;
        return ans;
    }
    int a1, a2, p, q, m, c, n;
    int main(){
        cin>>a1>>a2>>p>>q>>m>>c>>n;    
        if(n == 1) { cout<<a1<<endl;     return 0; }
        if(n == 2) { cout<<a2<<endl; return 0; }
        A.mat[0][0] = A.mat[1][1] = A.mat[2][2] = A.mat[4][3] = 1;
        A.mat[4][4] = p; A.mat[3][4] = q-p;
        A.mat[0][2] = 1; A.mat[0][1] = 1; 
        A.mat[1][2] = 2; 
        A.mat[c][4] = m;
        A = powmod(A, n-2);
        LL a[5] = {1, 3, 9, a1, a2}, ans = 0;
        
        for(int i = 0; i < 5; i++) (ans += A.mat[i][4]*a[i]) %= MOD;
        cout<<ans<<endl;
    }

    G 题

    离线莫队算法

    将询问分块,使得普通暴力的时间也能在n*sqrt(n)的时间内跑完

    可以自行搜索莫队算法,算是模板题了

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <algorithm>
    #define pb push_back
    using namespace std;
    const int maxn = 50050;
    int a[maxn], b[maxn], numa[maxn], numb[maxn], va[maxn], vb[maxn], ANS[maxn];
    int ans, n, m, k, x, y;
    struct Data{
        int l, r, id, rank;
        bool operator <(const Data& B) const{
            return rank == B.rank ? r < B.r : rank < B.rank;
        }
    };    
    vector<Data> Q;
    
    inline void Insert(int x){
        int aa = a[x]^numa[a[x]], bb = b[x]^numb[b[x]];
        ans -= numa[a[x]]*vb[aa^k];
        ans -= va[bb^k]*numb[b[x]];
        if((aa^k) == bb) ans += numa[a[x]]*numb[b[x]];
        va[aa] -= numa[a[x]];     vb[bb] -= numb[b[x]];
        
        numa[a[x]]++;    numb[b[x]]++;
        
        aa = a[x]^numa[a[x]], bb = b[x]^numb[b[x]];
        va[aa] += numa[a[x]]; vb[bb] += numb[b[x]];
        ans += numa[a[x]]*vb[aa^k];
        ans += va[bb^k]*numb[b[x]];
        if((aa^k) == bb) ans -= numa[a[x]]*numb[b[x]];
    }
    
    inline void Erase(int x){
        int aa = a[x]^numa[a[x]], bb = b[x]^numb[b[x]];
        ans -= numa[a[x]]*vb[aa^k];
        ans -= va[bb^k]*numb[b[x]];
        if((aa^k) == bb) ans += numa[a[x]]*numb[b[x]];
        va[aa] -= numa[a[x]];     vb[bb] -= numb[b[x]];
        
        numa[a[x]]--;    numb[b[x]]--;
        
        aa = a[x]^numa[a[x]],      bb = b[x]^numb[b[x]];
        va[aa] += numa[a[x]];     vb[bb] += numb[b[x]];
        ans += numa[a[x]]*vb[aa^k];
        ans += va[bb^k]*numb[b[x]];
        if((aa^k) == bb) ans -= numa[a[x]]*numb[b[x]];
    }
    
    int main(){
        cin>>n>>m>>k;
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
        int len = sqrt(m+0.5);
        for(int i = 1; i <= m; i++){
            scanf("%d %d", &x, &y);
            Q.pb((Data){x, y, i, x/len});
        }
        sort(Q.begin(), Q.end());
        int l = Q[0].l, r = Q[0].r; ans = 0;
        for(int i = l; i <= r; i++) Insert(i);
        ANS[Q[0].id] = ans;
        for(int i = 1; i < Q.size(); i++){
            while(l > Q[i].l) Insert(--l);
            while(r < Q[i].r) Insert(++r);
            while(l < Q[i].l) Erase(l++);
            while(r > Q[i].r) Erase(r--);
            ANS[Q[i].id] = ans;
        }
        for(int i = 1; i <= m; i++) printf("%d
    ", ANS[i]);
    }

    H 题

    模拟题

    注意给出的时间不一定是排好序的

    用队列模拟进出站的情况

    然后如果不够,就加一列火车

    最后输出答案即可

    (我这里直接用了平衡树(set),复杂度优化一个n,变成nlogn)

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <set>
    #define fi first
    #define se second
    #define mp make_pair
    #define pb push_back
    using namespace std;
    typedef pair<int, int> PII;
    struct Data{
        PII t;
        int type;
        bool operator <(const Data &B) const{
            return (t == B.t) ? type < B.type : t < B.t;
        }
    };
    vector<Data> data;
    PII add(PII A, int B){
        PII temp;
        temp.fi = (A.se+B)/60 + A.fi;
        temp.se = (A.se+B)%60;
        return temp;
    }
    int T, D, na, nb, x, y, ansa, ansb;
    char ch;
    set<PII> Sa, Sb;
    int main(){
        cin>>T;
        while(T--){
            ansa = ansb = 0;
            data.clear();
            Sa.clear(); Sb.clear();
            
            cin>>D;
            cin>>na>>nb;
            for(int i = 0; i < na; i++) {
                cin>>x; cin>>ch; cin>>y;
                data.pb((Data){mp(x, y), 1});
                cin>>x; cin>>ch; cin>>y;
                Sb.insert(add(mp(x, y), D));
            }
            for(int i = 0; i < nb; i++){
                cin>>x; cin>>ch; cin>>y;
                data.pb((Data){mp(x, y), 2});
                cin>>x; cin>>ch; cin>>y;
                Sa.insert(add(mp(x, y), D));
            }
            sort(data.begin(), data.end()); 
            for(int i = 0; i < data.size(); i++){
                if(data[i].type == 1){
                    if(Sa.empty()){
                        Sa.insert(mp(0, 0));
                        ansa++;
                    } 
                    if(*Sa.begin() > data[i].t){
                        Sa.insert(mp(0, 0));
                        ansa++;
                    }
                    Sa.erase(*Sa.begin());
                } else{
                    if(Sb.empty()){
                        Sb.insert(mp(0, 0));
                        ansb++;
                    } 
                    if(*Sb.begin() > data[i].t){
                        Sb.insert(mp(0, 0));
                        ansb++;
                    }
                    Sb.erase(*Sb.begin());
                }
            }
            cout<<ansa<<" "<<ansb<<endl;
        }
    }

    I 题

    数据结构

    维护一个二进制串的字典树即可

    每次枚举两个数相加,然后在字典树中删除这2个数,再进行xor查找

    查找之后,再把这两个数加回去

    复杂度是O(n^2logv)

    (顺便暴力n^3+黑科技或许也能过)

    #include <iostream>
    #include <cstdio>
    #define fi first
    #define se second
    using namespace std;
    typedef long long LL;
    typedef pair<int, int> PII;
    const int maxn = 1010;
    PII node[maxn*100];
    int G[maxn*100][2];
    int tot, n;
    LL Pow[50], a[maxn];
    void Insert(LL v){
        //fi = 0 se = 1
        int x = 0;
        for(int i = 0; i <= 31; i++){
            LL k = (v&Pow[31-i]) > 0 ? 1 : 0;
            G[x][k]++;
            if(k){
                if(node[x].se == 0) node[x].se = ++tot;
                x = node[x].se;
            } else{
                if(node[x].fi == 0) node[x].fi = ++tot;
                x = node[x].fi;
            }
        }
    }
    void Erase(LL v){
        int x = 0;
        for(int i = 0; i <= 31; i++){
            LL k = (v&Pow[31-i]) > 0 ? 1 : 0;
            G[x][k]--;
            x = k ? node[x].se : node[x].fi;
        }
    }
    LL Find(LL v){
        int x = 0;
        LL ans = 0;
        for(int i = 0; i <= 31; i++){
            LL k = (v&Pow[31-i]) > 0 ? 0 : 1;
            if(G[x][k]){
                ans += Pow[31-i];
                x = k ? node[x].se : node[x].fi;
            } else {
                x = k ? node[x].fi : node[x].se;
            }
        }
        return ans;
    }
    int main(){
        Pow[0] = 1;
        for(int i = 1; i <= 31; i++) Pow[i] = Pow[i-1]*2;
        cin>>n;
        for(int i = 0; i < n; i++) cin>>a[i], Insert(a[i]);
        LL ans = 0;
        for(int i = 0; i < n; i++){
            for(int j = i+1; j < n; j++){
                Erase(a[i]); Erase(a[j]);
                ans = max(ans, Find(a[i]+a[j]));
                Insert(a[i]); Insert(a[j]);
            }
        }
        cout<<ans<<endl;
    }

    J 题

    排列组合

    orz 好像没什么好说的

    注意一些特殊情况就行

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int a[3], H1[3][10000], H2[3][10000], n;
    int main(){
        cin>>n;
        for(int i = 0; i < 3; i++) cin>>a[i];
        for(int i = 0; i < 3; i++){
            for(int j = -2; j <= 2; j++)
                H1[i][((a[i]+j)%n+n)%n] = 1;
        }
        for(int i = 0; i < 3; i++) cin>>a[i];
        for(int i = 0; i < 3; i++){
            for(int j = -2; j <= 2; j++)
                H2[i][((a[i]+j)%n+n)%n] = 1;
        }
        int ans1, ans2, ans3, t1, t2, t3;
        ans1 = ans2 = ans3 = 1;
        for(int i = 0; i < 3; i++){
            t1 = t2 = t3 = 0;
            for(int j = 0; j < n; j++){
                if(H1[i][j]) t1++;
                if(H2[i][j]) t2++;
                if(H1[i][j] && H2[i][j]) t3++;
            }
            ans1 *= t1;
            ans2 *= t2;
            ans3 *= t3;
            //cout<<ans1<<" "<<ans2<<" "<<ans3<<endl;
        }
        cout<<ans1+ans2-ans3<<endl;
        
    }
  • 相关阅读:
    JS放在head和放在body中的区别
    模板模式(Template Pattern)
    原型模式
    Linux下的头文件搜索路径
    How to Change the Default Theme Appearance [editing with no theme]
    版本控制
    What is libacl.so.1 ?
    交叉编译器 arm-linux-gnueabi 和 arm-linux-gnueabihf 的区别
    mount --bind 的妙用
    mount的bind选项
  • 原文地址:https://www.cnblogs.com/Saurus/p/6676814.html
Copyright © 2020-2023  润新知