zoukankan      html  css  js  c++  java
  • CCPC-Wannafly Summer Camp 2019 全记录

     // 7.19-7.29 东北大学秦皇岛校区十天训练营,题目都挂在了Vjudge上。训练期间比较忙,没空更博总结,回来继续补题消化。

    Day1

    这天授课主题是简单图论,节奏挺好,wls两小时理完图论里的基本知识点。

    下午的赛题就偏入门了(简单图论无疑),只涉及到最短路问题和简单的搜索以及一些奇怪的技巧。(差分约束呢?最小生成树呢?强连通分量呢?)

     A - Jzzhu and Cities (补)

    把火车线路加上跑Dijkstra就好了,标记火车线路,相等时也要push。在最短路上的火车线路不能被取消,剩下的全部能取消。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    typedef long long ll;
    const int maxn = 200010;
    struct Edge {
        int to;
        bool istrain;
        ll w;
        Edge(int v, bool is, ll ww):to(v), istrain(is), w(ww){}
        bool operator<(const Edge& a)const {
            if(w==a.w) return istrain;  // 非火车节点先更新
            return w > a.w;
        }
    };
    vector<Edge> G[maxn];
    
    bool vis[maxn];
    int d[maxn];
    
    int Dijkstra() {
        memset(d, 0x3f, sizeof(d));
        memset(vis, 0, sizeof(vis));
        d[1] = 0;
        int res = 0;
    
        priority_queue<Edge> q;
        q.push(Edge(1, 0, 0));
        while(!q.empty()) {
            Edge tmp = q.top(); q.pop();
            int u = tmp.to;
            if(vis[u]) continue;
    
            vis[u] = 1;
        //    d[u] = tmp.w;
            if(tmp.istrain) ++res;
    
            for(int i=0;i<G[u].size();i++) {
                int v = G[u][i].to;
                if(!vis[v] && d[v]>=d[u]+G[u][i].w) {
                    d[v] = d[u] + G[u][i].w;
                    q.push(Edge(v, G[u][i].istrain, d[v]));
                }
            }
    
        }
        return res;
    
    }
    
    int main() {
        int n, m, k;
        cin>>n>>m>>k;
        int u, v, w;
        for(int i=0;i<m;i++) {
            scanf("%d %d %d", &u, &v, &w);
            G[u].push_back(Edge(v, 0, w));
            G[v].push_back(Edge(u, 0, w));
        }
        for(int i=0;i<k;i++) {
            scanf("%d %d", &v, &w);
            G[1].push_back(Edge(v, 1, w));
            // G[v].push_back(Edge(1, 1, w));
        }
    
        printf("%d
    ", k-Dijkstra());
    
        return 0;
    }
    View Code

     B - Phillip and Trains 

    BFS 注意打标记!!!(虽然只是3*100的地图也要爆内存!)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    using namespace std;
    int n, k, sx;
    char mp[3][110];
    bool vis[3][110];
    struct node {
        int x, y;
        node(int _x, int _y):x(_x), y(_y) {}
    };
    
    bool check(int x, int y) {
        if(x<0 || x>2)
            return false;
        if(y>=n)
            return true;
        if(mp[x][y]=='.')
            return true;
    
        return false;
    }
    
    bool bfs() {
        queue<node> q;
        q.push(node(sx, 0));
    
        while(q.size()) {
            node now = q.front(); q.pop();
            if(now.y>=n) {
                return true;
            }
    
        //    printf("(%d,%d) -> ", now.x, now.y);
            
            int nx = now.x, ny = now.y+1;
            if(!check(nx, ny))  continue; // 向右走一步
    
            for(int i=-1;i<=1;i++) {      // 尝试三个方向移动
                nx = now.x + i;
                if(check(nx, ny) && check(nx, ny+1) && check(nx, ny+2) && !vis[nx][ny+2]) {
                    q.push(node(nx, ny+2));
                    vis[nx][ny+2] = 1;
                }
            }
        }
        return false;
    }
    
    
    int main() {
        int t; cin>>t;
        while(t--) {
            scanf("%d %d", &n, &k);
            getchar();
            memset(vis, 0, sizeof(vis));
            for(int i=0;i<3;i++) {
                scanf("%s", mp[i]);
                
                if(mp[i][0]=='s')
                    sx = i;
                
            }
            printf("%s
    ", bfs()?"YES":"NO");
        }
        
        return 0;
    }
    View Code

     C - A Mist of Florescence (补)

    构造题,技巧就是设计井字形的连通块,把其他颜色块涂到井字的格子上。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    int a, b, c, d;
    char ans[50][50];
    void solve() {
        for(int i=1;i<=12;i++) {
            for(int j=1;j<50;j++) {
                if(i%2==1 && j%2==1 && a) ans[i][j] = 'A', --a;
                else ans[i][j] = 'D';
            }
        }
    
        for(int i=13;i<=24;i++) {
            for(int j=1;j<50;j++) {
                if(i%2==1 && j%2==1 && b) ans[i][j] = 'B', --b;
                else ans[i][j] = 'D';
            }
        }
    
        --c;
        --d;
        for(int i=25;i<=36;i++) {
            for(int j=1;j<50;j++) {
                if(i%2==1 && j%2==1 && c) ans[i][j] = 'C', --c;
                else ans[i][j] = 'D';
            }
        }
    
        for(int j=1;j<50;j++) {
            ans[37][j] = 'C';
        }
    
        for(int i=38;i<50;i++) {
            for(int j=1;j<50;j++) {
                if(i%2==1 && j%2==1 && d) ans[i][j] = 'D', --d;
                else ans[i][j] = 'C';
            }
        }
    
    
    }
    int main() {
        cin>>a>>b>>c>>d;
        solve();
        printf("49 49
    ");
        for(int i=1;i<50;i++) {
            for(int j=1;j<50;j++)
                printf("%c", ans[i][j]);
            printf("
    ");
        }
        
        return 0;
    }
    View Code

     E - Igor In the Museum 

    DFS到墙的边界 对每块编号!

    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    int n, m, k;
    char mp[1010][1010];
    int v[1010][1010], id;  // v[i][j]: mp[i][j]的分类编号   id: 当前编号
    int res[1010*1010];        // res[id]: 第id块的答案
    
    const int dx[] = {0, 0, 1, -1};
    const int dy[] = {1, -1, 0, 0};
    
    int ans;
    void dfs(int x, int y) {
        if(mp[x][y]=='*') {
            ans++;
            return;
        }
        v[x][y] = id;
        for(int i=0;i<4;i++) {
            int nx = x+dx[i], ny = y+dy[i];
            if(nx>=0 && nx<m && ny>=0 && ny<n && !v[nx][ny]) {
                dfs(nx, ny);
            }
        }
    }
    int main() {
        scanf("%d %d %d", &m, &n, &k);
        getchar();
        for(int i=0;i<m;i++) {
            scanf("%s", mp[i]);
        }
    
        for(int i=0;i<m;i++) {
            for(int j=0;j<n;j++) {
                if(mp[i][j]=='.' && !v[i][j]) {
                    ++id;
                    ans = 0;
                    dfs(i, j);
                    res[id] = ans;
                } 
            }
        }
    
        while(k--) {
            int x, y;
            scanf("%d %d", &x, &y);
            printf("%d
    ", res[v[x-1][y-1]]);
        }
    
        return 0;
    }
    View Code

     F - The Cild and Toy (补)

    贪心!由于每去掉一个点,等价于去掉了所有与它相连的边,就是问去掉全部边的最小代价。答案当然就是每条边两个节点权值小的那头的总和。都不用建图!!

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int maxn = 1010;
    int n, m;
    int w[maxn];
    
    int main() {
        cin>>n>>m;
        for(int i=1;i<=n;i++) {
            scanf("%d", &w[i]);
        }
        int u, v, ans = 0;
        for(int i=0;i<m;i++) {
            scanf("%d %d", &u, &v);
            ans += min(w[u], w[v]);
        }
        printf("%d
    ", ans);
        
        return 0;
    }
    View Code

     G - New Year Permutation

    对连通部分排序就完事了

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    #include <algorithm>
    using namespace std;
    int n, num[310], id;
    int mp[310][310];
    bool vis[310];
    
    int ans[310];
    struct list {
        vector<int> num;
        vector<int> id;
    }L[310];
    
    void dfs(int x, int id) {
        vis[x] = 1;
    
        L[id].num.push_back(num[x]);
        L[id].id.push_back(x);
    
        for(int i=1;i<=n;i++) {
            if(mp[x][i]) {
                if(!vis[i])
                    dfs(i, id);
            }
        }
    }
    
    int main() {
        cin>>n;
        for(int i=1;i<=n;i++) {
            scanf("%d", &num[i]);
        }
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=n;j++) {
                scanf("%1d", &mp[i][j]);
            }
        }
    
        for(int i=1;i<=n;i++) {
            if(!vis[i])
                dfs(i, ++id);
        }
    
        for(int i=1;i<=id;i++) {
            // for(int j=0;j<L[i].num.size();j++) {
            //     printf("%d:%d ", L[i].num[j], L[i].id[j]);
            // }
            // cout<<endl;
            
            sort(L[i].num.begin(), L[i].num.end());
            sort(L[i].id.begin(), L[i].id.end());
    
    
            for(int j=0;j<L[i].num.size();j++) {
                ans[L[i].id[j]] = L[i].num[j];
            }
        }
        for(int i=1;i<=n;i++) {
            printf("%d%c", ans[i], i!=n?' ':'
    ');
        }
        return 0;
    }
    View Code

     H - Alyona and the Tree (补)

    从根开始dfs就好,每个节点为max(0LL, now+w[i])

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int maxn = 100010;
    typedef long long ll;
    
    struct Edge {
        int to;
        ll w;
        Edge(int v, ll ww):to(v), w(ww) {}
    };
    vector<Edge> G[maxn];
    int n, vw[maxn];
    int ans;
    void dfs(int u, int fa, ll now) {
        for(int i=0;i<G[u].size();i++) {
            int v = G[u][i].to;
            if(v!=fa) {
                if(vw[v]>=now+G[u][i].w) {
                    --ans;
                    // printf("%d->%d
    ", u, v);
                    dfs(v, u, max(now+G[u][i].w, 0LL));
                }
            }
        }
    
    }
    
    int main() {
        cin>>n; ans = n;
        for(int i=1;i<=n;i++) {
            scanf("%d", &vw[i]);
        }
        int u, w;
        for(int i=2;i<=n;i++) {
            scanf("%d %d", &u, &w);
            G[i].push_back(Edge(u, w));
            G[u].push_back(Edge(i, w));
        }
    
        dfs(1, -1, 0);
        printf("%d
    ", ans-1);
        return 0;
    }
    View Code

     L - Love Triangle 

    假的三元环??

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int maxn = 5010;
    int n, tot;
    int love[maxn], low[maxn];
    bool vis[maxn];
    bool ans;
    void dfs(int u) {
        if(ans) return;
    
        vis[u] = true;
        low[u] = ++tot;
        int v = love[u];
        if(v && !vis[v]) {
            dfs(v);
            if(love[v] && low[love[v]]==low[u]+2 && love[love[v]]==u) {
                ans = true;
                return;
            }
        }
    }
    
    int main() {
        cin>>n;
        for(int i=1;i<=n;i++) {
            scanf("%d", &love[i]);
        }
        ans = false;
        for(int i=1;i<=n;i++) {
            if(!vis[i] && !ans)
                dfs(i);
        }
        printf("%s
    ", ans?"YES":"NO");
        return 0;
    }
    View Code

     N - News Distribution 

    并查集

    #include <iostream>
    #include <cstdio>
    using namespace std;
    const int maxn = 500100;
    int n, m;
    int fa[maxn];
    int g[maxn], cnt[maxn];
    
    int Find(int x) {
        return fa[x]==x?x:(fa[x]=Find(fa[x]));
    }
    
    void Union(int x, int y) {
        int a = Find(x);
        int b = Find(y);
        if(a==b) return;
        fa[a] = b;
    }
    
    int main() {
        cin>>n>>m;
        for(int i=1;i<=n;i++) fa[i] = i;
        while(m--) {
            int k;
            scanf("%d", &k);
            for(int i=0;i<k;i++) {
                scanf("%d", &g[i]);
            }
            for(int i=1;i<k;i++) {
                Union(g[0], g[i]);
            }
        }
        for(int i=1;i<=n;i++) {
            cnt[Find(i)]++;
        }
        for(int i=1;i<=n;i++) {
            printf("%d%c", cnt[Find(i)], i!=n?' ':'
    ');
        }
        return 0;
    }
    View Code

     O - NP-Hard Problem (补)

    二分图裸题。。。

    #include<iostream>
    #include<cstdio>
    #include<vector>
    using namespace std;
    typedef long long ll;
    const int maxn = 100010;
    int n, m;
    vector<int> G[maxn];
    int deg[maxn];
    int id[maxn];
    vector<int> ans[2];
    bool dfs(int u) {
        for(int i=0;i<G[u].size();i++) {
            int v = G[u][i];
            if(!id[v]) {
                id[v] = 3 - id[u];
                if(id[v]==1)
                    ans[0].push_back(v);
                else
                    ans[1].push_back(v);
                if(!dfs(v)) return false;
            } else if(id[v]==id[u]) return false;
        }
        return true;
    }
    
    int main() {
        cin>>n>>m;
        int u, v;
        for(int i=0;i<m;i++) {
            scanf("%d %d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
            ++deg[u];
        }
        for(int i=1;i<=n;i++) {
            if(!deg[i]) continue;
    
            if(!id[i]) {
                id[i] = 1;
                ans[0].push_back(i);
                if(!dfs(i)) 
                    return 0 * printf("-1
    ");
            }
    
    
        }
        
        for(int k=0;k<=1;k++) {
            printf("%d
    ", ans[k].size());
            for(int i=0;i<ans[k].size();i++) {
                printf("%d%c", ans[k][i], i==ans[k].size()-1?'
    ':' ');
            }
        }
    
        return 0;
    }
    View Code

    Day2

    第二天的主题是简单数论,下午挂的是CF上的一场区域赛:2015 ACM National Contest Romania

    注意文件读入要求!!!(第一题让我WA了4次样例。。。尝试两种方法写)

     A - Por Costel and Azerah

    子序列里和为偶数的个数,简单dp(或组合数计算)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const ll mod = 1e9+7;
    const int maxn = 1000100;
    int arr[maxn], n;
    ll a[maxn], b[maxn];
    
    int main() {
        
        freopen("azerah.in", "r", stdin);
        freopen("azerah.out", "w", stdout);
            int t; cin>>t;
        while(t--) {
            scanf("%d", &n);
            for(int i=1;i<=n;i++) {
                scanf("%lld", &arr[i]);
            }
            a[1] = b[1] = 0;
            if(arr[1]%2==0) b[1] = 1;
            else a[1] = 1;
            for(int i=2;i<=n;i++) {
                if(arr[i]%2==0) {
                    b[i] = (b[i-1]*2+1) % mod;
                    a[i] = (a[i-1]*2)% mod;
                }else {
                    b[i] = (a[i-1] + b[i-1]) %mod;
                    a[i] = (a[i-1] + b[i-1] + 1) %mod;
                }
            //    cout<<b[i]<<endl;
            }
            cout<<b[n]<<endl;
        }
        return 0;
    }
    
    /*
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    const ll mod = 1e9+7;
    const int maxn = 1000100;
    int arr[maxn], n;
    
    ll pow(ll a, ll n) {
        ll res = 1;
        while(n) {
            if(n%2) {
                res = res * a %mod;
            }
            a = a*a % mod;
            n >>= 1;
        }
        return res;
    }
    int main() {
        freopen("azerah.in", "r", stdin);
        freopen("azerah.out", "w", stdout);
        
        int t; cin>>t;
        while(t--) {
            ll odd = 0, even = 0;
            scanf("%d", &n);
            for(int i=1;i<=n;i++) {
                scanf("%lld", &arr[i]);
                if(arr[i]%2==0) even++;
                else odd++;
            }
            if(odd==0)
                cout<<(pow(2, even)-1+mod)%mod<<endl;
            else
                cout<<(pow(2, odd-1)*pow(2, even)%mod-1+mod)%mod<<endl;
        }
        return 0;
    }
    
     */
    View Code

     B - Por Costel and the Algorithm (补)

    给了一段Bellman-Ford代码,调整边的顺序让它最多执行两次。

    记录最短路上的边即可,dfs输出边的顺序。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<vector>
    using namespace std;
    typedef long long ll;
    const int maxn = 100100;
    
    int n, m;
    struct Edge{
        int to;
        int id;
        ll w;
        Edge(int v, int i, ll _w):to(v), id(i), w(_w) {}
        bool operator<(const Edge& e)const {
            return w > e.w;
        }
    };
    vector<Edge> G[maxn];
    
    ll d[maxn];
    int e[maxn];  // e[v]: 连向v的边的编号
    bool path[2*maxn];
    
    void Dijkstra() {
        memset(d, 0x3f, sizeof(d));
        d[1] = 0;
    
        priority_queue<Edge> q;
        q.push(Edge(1, 0, 0));
        while(!q.empty()) {
            Edge tmp = q.top(); q.pop();
            int u = tmp.to;
            for(int i=0;i<G[u].size();i++) {
                int v = G[u][i].to;
                if(d[v]>d[u]+G[u][i].w) {
                    d[v] = d[u] + G[u][i].w;
    
                    path[e[v]] = 0;
                    path[G[u][i].id] = 1;
                    e[v] = G[u][i].id;
    
                    q.push(Edge(v, 0, d[v]));
                }
            }
        }
    }
    
    
    void dfs(int u) {
        for(int i=0;i<G[u].size();i++) {
            int m = G[u][i].id, v = G[u][i].to;
            //printf("%d->%d %d", u, v, m);
            if(path[m]) {
                printf("%d ", m);
                dfs(v);
            }
        }
    }
    
    int main() {
        freopen("algoritm.in", "r", stdin);
        freopen("algoritm.out", "w", stdout);
        int t; cin>>t;
        while(t--) {
            memset(e, 0, sizeof(e));
            memset(path, 0, sizeof(path));
            for(int i=0;i<maxn;i++) G[i].clear();
    
            scanf("%d %d", &n, &m);
            for(int i=1;i<=m;i++) {
                int u, v; ll w;
                scanf("%d %d %lld", &u, &v, &w);
                G[u].push_back(Edge(v, i, w));
            }
            Dijkstra();
    
            dfs(1);
            for(int i=1;i<=m;i++) {
                if(!path[i])
                    printf("%d ", i);
            }
            printf("
    ");
        }
        return 0;
    }
    View Code

     C - Por Costel and Bujor

     D - Por Costel and the Censorship Committee

     E - Por Costel and the Cipher

     F - Por Costel and the Alien Invasion

     G - Por Costel and the Orchard

     H - Por Costel and the Match

    并查集裸题(可以用带权并查集做?)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const int maxn = 100010;
    int n, m;
    int fa[maxn*2];
    int Find(int x) {
        return fa[x]==x?x:(fa[x]=Find(fa[x]));
    }
     
    void Union(int x, int y) {
        int a = Find(x), b= Find(y);
        if(a!=b) 
            fa[a] = b;
    }
     
    int main() {
        freopen("meciul.in", "r", stdin);
        freopen("meciul.out", "w", stdout);
        int t; cin>>t;
        while(t--) {
            scanf("%d %d", &n, &m);
            for(int i=1;i<=2*n;i++) fa[i] = i;
            int x, y;
            while(m--) {
                scanf("%d %d", &x, &y);
                if(Find(x)!=Find(y) && Find(x+n)!=Find(y+n)) {
                    printf("YES
    ");
                    Union(x, y+n);
                    Union(x+n, y);
                } else {
                    printf("NO
    ");
                }
            }
        }
        return 0;
    }
    View Code

     I - Por Costel and the Match

    求[n/i],分块(也可以只计算k=sqrt(n)的结果,ans*2 - k*k就是最终答案)

    ll ans = 0;
    for(int i=1;i<=n;) {
        ans += n/i * (n/(n/i) - i +1);
        i = n/(n/i) + 1;
    }

     J - Por Costel and Pinball

     K - Por Costel and the Firecracker (补)

    没有内存限制的话就是水题,可是完全没有优化空间。。。

    百度之,学会了分块打表

    把第一次查询看成了x1,debug了半天。。。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    const int maxn = 10000010;
    const int mod = 10000003;
    ll n, a, b, x1, q, q1;
    ll ans[maxn/100+10];
    void pre() {
        ans[0] = x1;
        ll last = x1;
        for(ll i=1;i<maxn;i++) {
            last = (last*i % n + a) % n;
            if(i%100==0) {
                ans[i/100] = last;
            }
        //    cout<<i<<' '<<ans[i]<<endl;
        }
    
    }
    
    ll cal(ll k) {
        if(k%100==0) return ans[k/100];
        
        ll last = ans[k/100];
        for(ll i=k/100*100+1;i<=k;i++) {
            last = (last*i % n + a) % n;
        }
        return last;
    }
    
    int main() {
        freopen("pocnitoare.in", "r", stdin);
        freopen("pocnitoare.out", "w", stdout);
    
        cin>>n>>a>>b>>x1>>q>>q1;
    
        pre();
        printf("%lld
    ", x1=cal(q1-1));  // 第一次查询是q1-1而不是x1-1!!!
        for(int i=1;i<q;i++) {
            q1 = (i*x1 % mod + b) % mod + 1;
            printf("%lld
    ", x1=cal(q1-1));
    
        }
        return 0;
    }
    View Code

     L - Por Costel and the Semipalindromes

    WA了半天,原来是1左移出现了溢出,ll范围一定要写成 (1LL<<k)

    #include<iostream>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    char res[1010];
    int tmp[1010];
    int main() {
        freopen("semipal.in", "r", stdin);
        freopen("semipal.out", "w", stdout);
        int t; cin>>t;
        while(t--) {
            int n; ll k;
            scanf("%d %lld", &n, &k);
            
            res[1] = res[n] = 'a';
    
            if(k>(1LL<<(n-2))) {
                k -= (1LL<<(n-2));
                res[1] = res[n] = 'b';
            } 
            --k;
            for(int i=0;i<n-2;i++) {
                tmp[n-1-i] = (k>>i)&1;
            }
            for(int j=2;j<n;j++) {
                if(tmp[j]) {
                    res[j] = 'b';
                } else {
                    res[j] = 'a';
                }
            }
    
            for(int i=1;i<=n;i++) {
                putchar(res[i]);
            }
            puts("");
        }
        return 0;
    }
    View Code

    Day3

    题目来自于NAIPC 2016北美邀请赛。

     E - K-Inversions

    给定一个只含有AB的字符串(长度不超过1000000),求间隔为1~n-1的B-A有多少对。

    FFT模板题

     F - Mountain Scenes

    给长度总和为n的木条以及相框的宽度w和高度h,问能组成多少种不同的山峰形状。

    大家的签到dp题,我的自闭题。。。 j 没有从0开始样例死都调不出来 噗

    也就是dp[i][0] = 1

    dp[i][j] 为宽度 i 用了总长 j 的木条的方案数。 dp[i+1][j] = sum( dp[i][j-k] ) ( 0<=k<=h, j )

    #include<iostream>
    #include<cstdio>
    using namespace std;
    typedef long long ll; 
    const int maxn = 10010;
    const int mod = 1e9+7;
    
    int n, w, h;
    ll dp[110][maxn];
    int main() {
        
        cin>>n>>w>>h;
        // for(int i=0;i<=h && i<=w;i++) dp[i][0] = 1;
        dp[0][0] = 1;
        for(int i=1;i<=w;i++) {
             for(int j=0;j<=n;j++) {  // j=0 开始。。。坑死自己了
                for(int k=0;k<=h && k<=j;k++) {
                    dp[i][j] = (dp[i][j] + dp[i-1][j-k]) % mod;
                }
            }
        }
    
        ll ans = 0;
        for(int i=1;i<=n;i++)
            ans = (ans + dp[w][i])%mod;
    
        cout<<(ans-min(n/w, h))%mod<<endl;
        return 0;
    }
    View Code

     G - Symmetry

    平面上有 n 个点( n<=1000),问至少加入多少点,使全部点与某一个点中心对称,或者全部点关于某条过其中两点的直线对称。

    计算几何。

    对于要求1,枚举全部点再check剩下点是否中心对称,复杂度O(n^2logn),可行。

    对于要求2,枚举两点所得直线再check剩下点是否关于直线对称,复杂度O(n^3logn),TLE。

    看题解好像是把直线去重才能过。

     I - Tourists

     求树上所有整除关系的两对节点的最短路之和。

     计算所有符合条件的节点复杂度O(nlogn),树上两点 u, v 的最短路为 dep[u] + dep[v] - 2*dep[lca(u, v)] + 1。

     找了个LCA的板子就1A了 O.O (第四个板子是改进的树链剖分求LCA,复杂度O(n + mlogn),是其中最快的。)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn = 500010;
    struct Edge {
        int to, next;
    }edges[maxn];
    int head[maxn], tot;
    
    int fa[maxn], siz[maxn], dep[maxn], top[maxn];
    // top[u] == u : u不是重儿子
    // top[u] == fa: fa的重儿子是u
    void add(int u, int v) {
        edges[++tot].to = v;
        edges[tot].next = head[u];
        head[u] = tot;
    }
    
    void dfs(int u) { // 求fa, siz, dep, top
        int maxSon = 0, son = 0;
        top[u] = u;
        siz[u] = 1;
        for(int i=head[u];i;i=edges[i].next) {
            int v = edges[i].to;
            if(v==fa[u]) continue;
    
            fa[v] = u;
            dep[v] = dep[u] + 1;
            dfs(v);
            siz[u] += siz[v];
    
            if(siz[v]>maxSon)
                maxSon = siz[son=v];
        }
        if(son)    // 重儿子
            top[son] = u;
    }
    
    int Find(int u) {
        return u==top[u]?u:top[u]=Find(top[u]);
    }
    
    int LCA(int u, int v) {
        if(Find(u)!=Find(v))
            return dep[top[u]]<dep[top[v]] ? LCA(u, fa[top[v]]):LCA(v, fa[top[u]]);
        else
            return dep[u]<dep[v] ? u:v;
    }
    
    
    int main() {
        int n;
        cin>>n;
        for(int i=0;i<n-1;i++) {
            int u, v;
            scanf("%d %d", &u, &v);
            add(u, v);
            add(v, u);
        }
        memset(fa, -1, sizeof(fa));
        dfs(1);
    
        long long ans = 0;
        for(int i=1;i<=n/2;i++) {
            for(int j=i*2;j<=n;j+=i) {
                ans += dep[i]+dep[j]-2*dep[LCA(i, j)] + 1;
            }
        }
        printf("%lld
    ", ans);
        return 0;
    }
    View Code

    Day4

     A - One-dimensional Japanese Crossword

    简单模拟题。

     B - Passwords

    简单数学题。

     C - Journey

     E - Jamie and Alarm Snooze

    简单模拟题。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    
    bool check(int h, int m) {
    //    cout<<h<<":"<<m<<endl;
        if(h%10==7 || m%10==7) return true;
        return false;
    }
    int main()
    {
        int x, h, min;
        scanf("%d", &x);
        scanf("%d %d", &h, &min);
    
        int y = 0;
        while(!check(h, min)) {
            if(x==60) {
                if(--h<0) h = 23;
            } else {
                min -= x;
                if(min<0) {
                    min += 60;
                    if(--h<0) h = 23;
                }
            }
            ++y;
        }
    
        printf("%d
    ", y);
        return 0;
    }
    View Code

     F - Jamie and Binary Sequence (changed after round)

    给定 n 和 k,将 n 分解成 k 个二次幂之和,求2的指数最小且字典序最大的一组解,不存在则输出 No。

    思维题。

    把 n 转成二进制,要保证最高次幂最小,当二进制位1个数小于k时,把最高位2^n拆成 2^(n-1) + 2^(n-1),这样多了一个1。

    当拆分之后1的数量大于k时,要保证字典序最大,再从最低位开始拆分,每次只拆一个1,新的低位加2。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    
    ll n;
    int k, bits[100100], cnt, high, len;
    int tmp[65];
    
    int main()
    {
        cin>>n>>k;
        ll nn = n;
        for(int i=0;nn;i++) {
            tmp[i] = nn&1;
            nn >>= 1; 
            len = i;
            if(tmp[i]) ++cnt;
        }
        for(int i=len;i>=0;i--) {
            bits[len-i] = tmp[i];
        }
        high = len;  // 记录最高位指数
        // for(int i=0;i<=len;i++) cout<<bits[i];
    
        if(cnt>k) return 0 * printf("No
    ");
    
        int pos = 0;
        while(cnt<k) {
            if(cnt+bits[pos]<=k) { // 最小化y=max(ai)
                cnt += bits[pos];
                bits[pos+1] += 2*bits[pos];
                bits[pos] = 0;
                ++pos;
                len = max(pos, len);   // 忘记更新,WA66组
            } else {
                pos = len; 
                while(!bits[pos]) --pos;
                while(cnt<k) {
                    ++cnt;
                    bits[pos] -= 1;
                    bits[++pos] += 2;
                    
                }
            }
        }
    
        printf("Yes
    ");
        int up = max(pos, len);
        for(int i=0;i<=up;i++) {
            for(int j=0;j<bits[i];j++)
                printf("%d ", high-i);
        }
        return 0;
    }
    View Code

     G - Jamie and Interesting Graph

     需要构造出一个n个节点m条边的图,满足最小生成树的权为素数,1到n的最短路为素数。

     构造1~n的一条链为最小生成树,权为p = 100019。剩下不停加边,边权为2p。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int p = 100019;
    int main() {
        int n, m;
        cin>>n>>m;
        printf("%d %d
    ", p, p);
        printf("1 2 %d
    ", p-(n-2));
        for(int i=2;i<n;i++)
            printf("%d %d 1
    ", i, i+1);
        m -= n-1;
    
        for(int i=1;i<n && m;i++)
        for(int j=i+2;j<=n && m;j++) {
            printf("%d %d %d
    ", i, j, 2*p);
            --m;
            
        }
    
        return  0;
    }
    View Code

     H - 树上染色

     很经典的树形dp。

    // f[u][j]: 以u为根节点,涂了 j 个黑点的子树对答案的最大贡献

    对于一条边,左边子树有 x 个黑点,则右边有 k-x 个黑点,左边白点为 size[v] - x,右边白点为 n - size[v] - (k-x),按照题意计算这部分贡献 val。

    状态转移方程为 f[u][j] = max( f[u][j], f[u][j-x] + f[v][x] + val),j 倒序枚举更新。

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #define max(x, y) ((x) > (y) ? (x) : (y))
    #define min(x, y) ((x) < (y) ? (x) : (y))
    using namespace std;
    typedef long long ll;
    const int maxn = 2010;
    struct Edge {
        int u, w;
        Edge(int uu, int ww):u(uu), w(ww) {}
    };
    vector<Edge> G[maxn];
    int siz[maxn], n, kk;
    ll f[maxn][maxn];  // f[u][j]: 节点i含有j个黑点的子树对答案的最大贡献
    
    void dfs(int u, int fa) {
        f[u][0] = f[u][1] = 0;
        siz[u] = 1;
        for(int i=0;i<G[u].size();i++) {
            int v = G[u][i].u;
            if(v==fa) continue;
            
            dfs(v, u);
            siz[u] += siz[v];
     
        }
        for(int i=0;i<G[u].size();i++) {
            int v = G[u][i].u, w = G[u][i].w;
            if(v==fa) continue;
            
            for(int j=min(kk, siz[u]);j>=0;j--) {
                for(int k=0;k<=min(j, siz[v]);k++) {
                    if(f[u][j-k]!=-1) {
                        ll val = (1LL*k*(kk-k)+ 1LL*(siz[v]-k)*(n-kk-(siz[v]-k)))*w;
                        f[u][j] = max(f[u][j], f[u][j-k]+f[v][k]+val);
                    }
                }
            }
        }
    }
    
    
    int main() {
        scanf("%d %d", &n, &kk);
        for(int i=1;i<n;i++) {
            int u, v, w;
            scanf("%d %d %d", &u, &v, &w);
            G[u].push_back(Edge(v, w));
            G[v].push_back(Edge(u, w));
    
        }
        memset(f, -1, sizeof(f));
        dfs(1, -1);
        printf("%lld
    ", f[1][kk]);
    
        return 0;
    }
    View Code

    Day5

    Day6

  • 相关阅读:
    Flex 布局教程:语法篇(转载)
    【Go】【Http】Go实现Http相关知识点
    【Git】Git相关开发流程
    【Go】杂七杂八GoLang
    【Go】初识Context与Context键值对的可能情况
    jmeter-通过json提取器 提取所有数据 给下个接口使用
    C# 后台调用存储过程超时处理方法,
    IE11脚本错误-调用的对象无效-
    IE11浏览器arrt,全选反选失效无效修改方法
    如何学习计算机知识
  • 原文地址:https://www.cnblogs.com/izcat/p/11274543.html
Copyright © 2011-2022 走看看