2017 Multi-University Training Contest

来源:互联网 发布:图书借阅管理系统 php 编辑:程序博客网 时间:2024/06/11 22:34

HDU - 6069- Counting Divisors(区间筛)

题意:

  求。l和r为1e12,但是,r-l不到1e6。d函数为因子个数。

思路:

  一个数的因子个数可以由它的素因子个数推出, ,那么因子个数为。可以理解成第一个素数一共有0-x1,一共x1+1种选择。

  那么现在就是要知道[l,r]区间内,所有数字的素因子。而我们又知道,"l和r为1e12,r-l不到1e6"这个条件,在《挑战》的2.6的数学中有介绍一个东西叫做区间筛,可以在nlogn的复杂度内求出[l,r]区间内的素数。我们可以借鉴它的思想,先求出[1,sqrt(r)]区间内的素数,然后在不断删除[l,r]中它的倍数,每个v[i]在经过[1,sqrt(r)]区间内的素数筛选后,剩下的值,若不等于1,则必为素数。(这个好理解的吧?类似求一个数x的素因子的时候,只需要遍历sqrt(x)内的素数),那么就能知道每个数字不同的素因子的个数了。累乘最后加和即可。

  埃氏筛,数字大的时候,for循环里要加LL!!!不然j = i * i会爆。

 

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 1e6 + 5;const int mod = 998244353;LL v[maxn];int notprime[maxn];//vector<int>prime;LL num[maxn];int prime[maxn];int sieve(LL mx){    int ret = 0;    for(int i = 2; i <= mx; i++)    {        if(!notprime[i])        {            prime[ret++] = i;            if(1LL * i * i > mx)    continue;            for(int j = i * i; j <= mx; j += i)            {                notprime[j] = 1;            }        }    }    return ret;}int main(){    int p = sieve(maxn - 5);    int T;    scanf("%d", &T);    while(T--)    {        LL l, r;        int k;        scanf("%lld%lld%d", &l, &r, &k);        for(int i = 0; i <= r - l; i++)   num[i] = 1, v[i] = l + i;        int mx = sqrt(r);        for(int j = 0; j < p; j++)        {            int o = prime[j];            if(o > mx) break;            for(LL i = (l + o - 1) / o * o; i <= r; i += o)            {                LL idx = i - l, cnt = 0;                while(v[idx] % o == 0)                {                    v[idx] /= o;                    cnt++;                }                if(cnt) num[idx] = num[idx] * (1LL * cnt * k + 1) % mod;            }        }        long long ans = 0;        for(LL i = 0; i <= r - l; i++)        {            LL temp = (v[i] == 1) ? 1 : k + 1;            num[i] = num[i] * temp % mod;            ans = (ans + num[i]) % mod;         }         printf("%lld\n", ans);    }    return 0;}

HDU - 6070 - Dirt Ratio(二分+线段树)

题意:

  题意大概就是能否找到一区间,使得  区间内不同的数字的个数 / 区间长度 的值 最小。

思路:

  区间最优比率问题,往往是需要二分答案来解决的。如果我们假定答案为mid,那么很容易由题意得到。,即得到size(l,r)代表[l,r]区间内不同数字的数量。我们发现我们需要快速的找到[1,n]之间这个值,最小的区间的值是否满足小于等于0。那么需要维护一个区间最小值,所以要用线段树。所以是二分+线段树。

  具体的操作呢,我们枚举区间右端点,想一下每个区间维护的值由两部分组成,第一部分是size。考虑到每个数字只对[自己上一次出现的位置+1,当前位置]的各个区间有贡献度1。只需要枚举右端点时候,对这部分+1即可。第二部分是长度*mid,只需要对枚举到的位置中的每个区间-mid即可。

#include <bits/stdc++.h>using namespace std;#define lson l, mid, rt << 1#define rson mid + 1, r, rt << 1 | 1const int maxn = 60000 + 6;int a[maxn], last[maxn], pre[maxn];double lazy[maxn << 2];double tree[maxn << 2];int T, n;void pushup(int rt){tree[rt] = min(tree[rt << 1], tree[rt << 1 | 1]);}void pushdown(int rt){    if(lazy[rt])    {        lazy[rt << 1] += lazy[rt];        lazy[rt << 1 | 1] += lazy[rt];        tree[rt << 1] += lazy[rt];        tree[rt << 1 | 1] += lazy[rt];        lazy[rt] = 0;    }}void build(int l, int r, int rt){    lazy[rt] = tree[rt] = 0;    if(l == r)  return ;    int mid = (l + r) / 2;    build(lson);    build(rson);    pushup(rt);}void update(int ql, int qr, double delta, int l, int r, int rt){    if(ql <= l && r <= qr)    {        tree[rt] += delta;        lazy[rt] += delta;        return ;    }    pushdown(rt);    int mid = (l + r) / 2;    if(ql <= mid)   update(ql, qr, delta, lson);    if(qr > mid)    update(ql, qr, delta, rson);    pushup(rt);}double query(int ql, int qr, int l, int r, int rt){    if(ql <= l && r <= qr)  return tree[rt];    pushdown(rt);    int mid = (l + r) / 2;    double ret = n;    if(ql <= mid)   ret = min(ret, query(ql, qr, lson));    if(qr > mid)    ret = min(ret, query(ql, qr, rson));    pushup(rt);    return ret;}bool judge(double mid){    build(1, n, 1);    for(int i = 1; i <= n; i++)    {        update(pre[i] + 1, i, 1.0, 1, n, 1);        update(1, i, -mid, 1, n, 1);        if(query(1, i, 1, n, 1) <= 0.0) return true;    }    return false;}int main(){    scanf("%d", &T);    while(T--)    {        scanf("%d", &n);        for(int i = 1; i <= n; i++) last[i] = pre[i] = 0;        for(int i = 1; i <= n; i++)        {            scanf("%d", &a[i]);            pre[i] = last[a[i]];            last[a[i]] = i;        }        double lb = 0.0, rb = 1.0;        for(int i = 0; i < 20; i++)        {            double mid = (lb + rb) / 2;            if(judge(mid))  rb = mid;            else lb = mid;        }        printf("%.6f\n", rb);    }    return 0;}


 

HDU - 6071 - Lazy Running(同余最短路)

题意:

  题目要求是只有四个点,然后连边成正方形,问从2号点出发,再回到2号点且走过的总距离大于K的最短的路径是多少。

思路:

  我们可以通过,构造同余最短路。每当找到一个长度为k的从某点(1,2,3,4)出发到达点2的最短路时,一定会存在一个2m+k的答案。所以只要计算出各个点到点2的,%2m为[0, k - 1],的最短路即可。

 

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int maxn = 60000 + 6;const int INF = 0x3f3f3f3f;typedef pair<int, long long>pil;typedef pair<int, int>pii;vector<pii>G[10];LL d[5][maxn];int vis[5][maxn];LL spfa(int st, LL mod, LL k){    queue<pil> que;    memset(vis, 0, sizeof(vis));    memset(d, 0x3f, sizeof(d));    que.push({st, 0});    d[st][0] = 0;    vis[st][0] = 1;    LL ans = 1e18 + 5000;    while(que.size())    {        int u = que.front().first;        LL dist = que.front().second;        que.pop();        vis[u][dist % mod] = 0;        for(auto o : G[u])        {            int v = o.first, cost = o.second;            LL next_dis = dist + cost;            if(v == st)            {                if(next_dis < k)                    ans = min(ans, next_dis + (k - next_dis + mod - 1) / mod * mod);                else                    ans = min(ans, next_dis);            }            if(next_dis < d[v][next_dis % mod])            {                d[v][next_dis % mod] = next_dis;                if(vis[v][next_dis % mod] == 0)                {                    vis[v][next_dis % mod] = 1;                    que.push({v, next_dis});                }            }        }    }    return ans;}int main(){    int T_T;    scanf("%d", &T_T);    while(T_T--)    {        LL k;        int d12, d23, d34, d41;        scanf("%lld%d%d%d%d", &k, &d12, &d23, &d34, &d41);        for(int i = 0; i <= 5; i++) G[i].clear();        G[1].push_back({2, d12});        G[1].push_back({4, d41});        G[2].push_back({1, d12});        G[2].push_back({3, d23});        G[3].push_back({2, d23});        G[3].push_back({4, d34});        G[4].push_back({1, d41});        G[4].push_back({3, d34});        int m = min(d12, d23);        m = m * 2;        printf("%lld\n", spfa(2, m, k));    }    return 0;}

HDU - 6073 - Matching In Multiplication(拓扑+贡献度)

题意:

  T组测试数据,给出两个顶点集合,左边为U,右边为V,每个顶点集合的大小为n,然后有n行输入,第i行的输入v1,w1,v2,w2,表示集合U中的i顶点与集合V中的v1,v2顶点相连,且边的权值为w1,w2。求两个集合的所有完美匹配的权值之和。一个完美匹配的权值为该匹配所有边的权值相乘,且数据保证至少存在一个完美匹配。

思路:

  这篇博客讲的很好了。http://blog.csdn.net/mr__kid/article/details/76684660

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int mod = 998244353;const int maxn = 300000 + 5;typedef pair<int, int>pii;int d[maxn * 2];int vis[maxn * 2];int used[maxn * 2];vector<pii>G[maxn * 2];LL ans;void addEdge(int u, int v, int w){    G[u].push_back({v, w});    G[v].push_back({u, w});    d[u]++, d[v]++;}void Topological(int n){    memset(vis, 0, sizeof(vis));    queue<int>que;    for(int i = n + 1; i <= 2 * n; i++)        if(d[i] == 1)  que.push(i);    while(que.size())    {        int u = que.front();que.pop();        vis[u] = 1;//        cout << "u = " << u <<endl;        if(d[u] > 1)   continue;        for(auto o : G[u])        {            int v = o.first, w = o.second;            if(vis[v])  continue;            d[v]--;            if(d[v] == 1)            {                if(u > n)   ans = ans * w % mod;                que.push(v);            }        }    }}LL part[2];void dfs(int u, int fa, int lv, int eg){    vis[u]++;    for(auto o : G[u])  if(o.first != fa)    {        int v = o.first, w = o.second;        if(v == eg)   part[lv] = part[lv] * w % mod;        if(vis[v])  continue;        part[lv] = part[lv] * w % mod;        dfs(v, u, 1 - lv, eg);    }}int main(){    int T;    scanf("%d", &T);    while(T--)    {        int n;        scanf("%d", &n);        for(int i = 1; i <= 2 * n; i++) G[i].clear(), d[i] = 0;        for(int i = 1; i <= n; i++)        {            int v1, w1, v2, w2;            scanf("%d%d%d%d", &v1, &w1, &v2, &w2);            addEdge(i, v1 + n, w1);            addEdge(i, v2 + n, w2);        }        ans = 1;        Topological(n);//        cout << ans << endl;        for(int i = 1; i <= n; i++)        {            if(vis[i] == 0)            {                part[0] = part[1] = 1;                dfs(i, -1, 1, i);                ans = ans * (part[0] + part[1]) %mod;//                printf("part1 = %lld, part2 = %lld\n", part[0], part[1]);            }        }        printf("%lld\n", ans);    }    return 0;}/*10054 88 2 831 45 3 855 45 1 523 15 5 762 62 3 33*/


HDU - 6075 - Questionnaire(water)

题意:

  给出n个数字,%m==k的数字数量 是否 不小于 其他数字的数量 的判断结果。构造出一个m和k。

思路:

  水题。%2的结果必然满足。

#include <bits/stdc++.h>using namespace std;int main(){    int T;    scanf("%d", &T);    while(T--)    {        int n;        scanf("%d" ,&n);        int cnt = 0;        for(int i = 0; i < n; i++)        {            int x;            scanf("%d", &x);            cnt += (x & 1);        }        if(cnt >= n - cnt)        {            printf("2 1\n");        }        else        {            printf("2 0\n");        }    }    return 0;}


HDU - 6077 - Time To Get Up(water)

模拟即可。

 

#include <bits/stdc++.h>using namespace std;char s[50][50];bool judge(int x, int y){return s[x][y] == 'X';}int solve(int i, int  j){    int a = judge(i, j + 1);    int b = judge(i + 1, j + 3);    int c = judge(i + 4, j + 3);    int d = judge(i + 6, j + 1);    int e = judge(i + 4, j);    int f = judge(i + 1, j);    int g = judge(i + 3, j + 1);    int sum = a + b + c +d + e  + f + g;    if(sum == 2)return 1;    else if(sum == 5)    {        if(c == 0 && f == 0)    return 2;        else if(c == 1 && f == 0)   return 3;        else if(c == 1 && f == 1)   return 5;    }    else if(sum == 3)   return 7;    else if(sum == 4)   return 4;    else if(sum == 7)   return 8;    else if(g == 0) return 0;    else if(b == 0) return 6;    else if(e == 0) return 9;}int main(){    int T;    scanf("%d", &T);    while(T--)    {        for(int i = 0; i < 7; i++)        {            scanf("%s", s[i]);        }        int x1 = solve(0, 0);        int x2 = solve(0, 5);        int x3 = solve(0, 12);        int x4 = solve(0, 17);        printf("%d%d:%d%d\n", x1, x2, x3, x4);    }    return 0;}

HDU - 6078 - Wavel Sequence(DP)

题意:

  想在有两个序列a,b。从两个序列中挑出相同数量的数字,按照原本的顺序。求满足以下条件的子序列的种数。

  1. 两个序列相同

  2.满足如下形式: ,的子序列的种数。

思路:

  首先是一个类LCS的东西。定义dp[i][j][k]为a串前i个且以b串j结尾且上升状态为k时的方案数。

#include <bits/stdc++.h>using namespace std;const int mod = 998244353;const int maxn = 2000 + 5;int a[maxn], b[maxn];long long dp[maxn][maxn][2];void fun(long long &a, long long b){    a = (a + b) % mod;}int main(){    int T;    scanf("%d", &T);    while(T--)    {        int n, m;        scanf("%d%d", &n, &m);        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);        for(int i = 1; i <= m; i++) scanf("%d", &b[i]);        memset(dp, 0, sizeof(dp));        long long ans = 0;        for(int i = 1; i <= n; i++)        {            long long sum0 = 0, sum1 = 1;            for(int j = 1; j <= m; j++)            {                fun(dp[i][j][0], dp[i - 1][j][0]), fun(dp[i][j][1], dp[i - 1][j][1]);                if(a[i] == b[j])                {                    fun(dp[i][j][0], sum1);                    fun(dp[i][j][1], sum0);                }                if(a[i] < b[j])                {                    fun(sum1, dp[i - 1][j][1]);                }                else if(a[i] > b[j])                {                    fun(sum0, dp[i - 1][j][0]);                }            }        }        for(int i = 1; i <= m; i++) fun(ans, dp[n][i][0]), fun(ans, dp[n][i][1]);        printf("%lld\n", ans);    }    return 0;}


 

原创粉丝点击