2016 ACM/ICPC Asia Regional Shenyang Online

紀念(ˇˍˇ) 想~
B
題意:樹上的兩個操作
一、對路徑上的邊權進行修改
二、求路徑上邊權連續的段數
思路:樹鏈剖分操作,小心邊界。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <cstdlib>
#define ll o<<1
#define rr o<<1|1
#define CLR(a, b) memset(a, (b), sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 4e4 + 10;
const int INF = 1e9 + 10;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int MOD = 1e9 + 7;
struct Tree {
    int l, r, sum, lc, rc, lazy;
};
Tree tree[MAXN << 2];
void PushUp(int o){
    tree[o].sum = tree[ll].sum + tree[rr].sum;
    tree[o].lc = tree[ll].lc; tree[o].rc = tree[rr].rc;
    if(tree[ll].rc == tree[rr].lc) tree[o].sum--;
}
void PushDown(int o) {
    if(tree[o].lazy != -1) {
        tree[ll].lazy = tree[rr].lazy = tree[o].lazy;
        tree[ll].lc = tree[ll].rc = tree[rr].lc = tree[rr].rc = tree[o].lazy;
        tree[ll].sum = tree[rr].sum = 1;
        tree[o].lazy = -1;
    }
}
void Build(int o, int l, int r) {
    tree[o].l = l; tree[o].r = r;
    tree[o].lc = tree[o].rc = -1, tree[o].sum = 0; tree[o].lazy = -1;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    Build(ll, l, mid); Build(rr, mid + 1, r);
}
void Update(int o, int L, int R, int v) {
    if(tree[o].l == L && tree[o].r == R) {
        tree[o].lc = tree[o].rc = v;
        tree[o].sum = 1; tree[o].lazy = v;
        return ;
    }
    PushDown(o);
    int mid = (tree[o].l + tree[o].r) >> 1;
    if(R <= mid) Update(ll, L, R, v);
    else if(L > mid) Update(rr, L, R, v);
    else {Update(ll, L, mid, v); Update(rr, mid+1, R, v);}
    PushUp(o);
}
int Query(int o, int L, int R) {
    if(tree[o].l == L && tree[o].r == R) {
        return tree[o].sum;
    }
    PushDown(o);
    int mid = (tree[o].l + tree[o].r) >> 1;
    if(R <= mid) return Query(ll, L, R);
    else if(L > mid) return Query(rr, L, R);
    else return Query(ll, L, mid) + Query(rr, mid + 1, R) - (tree[ll].rc == tree[rr].lc);
}
int Color(int o, int pos) {
    if(tree[o].l == tree[o].r) {
        return tree[o].lc;
    }
    PushDown(o);
    int mid = (tree[o].l + tree[o].r) >> 1;
    if(pos <= mid) return Color(ll, pos);
    else return Color(rr, pos);
}
struct Edge {
    int from, to, next;
};
Edge edge[MAXN * 2];
int head[MAXN], edgenum;
void init() { edgenum = 0; CLR(head, -1); }
void addEdge(int u, int v) {
    Edge E = {u, v, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
int son[MAXN], num[MAXN];
int top[MAXN], pos[MAXN], id;
int dep[MAXN], pre[MAXN];
void DFS1(int u, int fa, int d) {
    dep[u] = d; pre[u] = fa; num[u] = 1; son[u] = -1;
    for(int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].to;
        if(v == fa) continue;
        DFS1(v, u, d + 1);
        num[u] += num[v];
        if(son[u] == -1 || num[son[u]] < num[v]) {
            son[u] = v;
        }
    }
}
void DFS2(int u, int T) {
    top[u] = T; pos[u] = ++id;
    if(son[u] == -1) return ;
    DFS2(son[u], T);
    for(int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].to;
        if(v == pre[u] || v == son[u]) continue;
        DFS2(v, v);
    }
}
int GetSum(int u, int v) {
    if(u == v) return 0;
    int f1 = top[u], f2 = top[v];
    int ans = 0;
    int lastu = -1, lastv = -1;
    while(f1 != f2) {
        if(dep[f1] < dep[f2]) {
            ans += Query(1, pos[f2], pos[v]);
            int C = Color(1, pos[v]);
            if(lastv == C) {
                ans--;
            }
            lastv = Color(1, pos[f2]);
            v = pre[f2]; f2 = top[v];
        }
        else {
            ans += Query(1, pos[f1], pos[u]);
            int C = Color(1, pos[u]);
            if(lastu == C) {
                ans--;
            }
            lastu = Color(1, pos[f1]);
            u = pre[f1]; f1 = top[u];
        }
    }
    if(u == v) return ans - (lastu == lastv);
    if(dep[u] > dep[v]) {
        ans += Query(1, pos[son[v]], pos[u]);
        if(lastu == Color(1, pos[u])) {
            ans--;
        }
        if(lastv == Color(1, pos[son[v]])) {
            ans--;
        }
    }
    else {
        ans += Query(1, pos[son[u]], pos[v]);
        if(lastu == Color(1, pos[son[u]])) {
            ans--;
        }
        if(lastv == Color(1, pos[v])) {
            ans--;
        }
    }
    return ans;
}
void Change(int u, int v, int z) {
    int f1 = top[u], f2 = top[v];
    while(f1 != f2) {
        if(dep[f1] < dep[f2]) {
            swap(u, v);
            swap(f1, f2);
        }
        Update(1, pos[f1], pos[u], z);
        u = pre[f1], f1 = top[u];
    }
    if(u == v) return ;
    if(dep[u] > dep[v]) swap(u, v);
    Update(1, pos[son[u]], pos[v], z);
}
int s[MAXN], e[MAXN], c[MAXN];
int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF) {
        init();
        for(int i = 1; i <= n - 1; i++) {
            scanf("%d%d%d", &s[i], &e[i], &c[i]);
            addEdge(s[i], e[i]); addEdge(e[i], s[i]);
        }
        DFS1(1, -1, 1); id = 0; DFS2(1, 1); Build(1, 1, id);
        for(int i = 1; i <= n - 1; i++) {
            if(dep[s[i]] > dep[e[i]]) {
                swap(s[i], e[i]);
            }
            Update(1, pos[e[i]], pos[e[i]], c[i]);
        }
        while(m--) {
            char op[10]; scanf("%s", op); int x, y, z;
            if(op[0] == 'C') {
                scanf("%d%d%d", &x, &y, &z);
                Change(x, y, z);
            }
            else {
                scanf("%d%d", &x, &y);
                printf("%d\n", GetSum(x, y));
            }
        }
    }
    return 0;
}

C
題意:n 個不同的位置,m 個學生,要求任意兩個學生之間隔至少k 個位置,問你方案數。
思路:我們先每隔k 個放完,這樣剩下x=nm(k+1) 個位置,相當於m 個有順序的盒子放x 個相同蘋果,允許放空,則方案數Cm1m+x1 ,因爲位置是不同的,我們考慮每個位置作爲第一個盒子,則有nCm1m+x1 ,最後發現每個結果重複算了m 次,我們除以m 即可。

第一個和第二個不要寫反了······

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <cstdlib>
#define ll o<<1
#define rr o<<1|1
#define CLR(a, b) memset(a, (b), sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 3e6 + 10;
const int INF = 1e9 + 10;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) { x += y; x %= MOD; }
LL fac[MAXN];
LL pow_mod(LL a, int n) {
    LL ans = 1;
    while(n) {
        if(n & 1) {
            ans = ans * a % MOD;
        }
        a = a * a % MOD;
        n >>= 1;
    }
    return ans;
}
LL C(LL n, LL m) {
    return fac[n] * pow_mod(fac[n - m], MOD - 2) % MOD * pow_mod(fac[m], MOD - 2) % MOD;
}
int main()
{
    fac[0] = 1;
    for(int i = 1; i <= MAXN - 1; i++) {
        fac[i] = fac[i - 1] * i % MOD;
    }
    int t; scanf("%d", &t);
    while(t--) {
        int n, m, k; scanf("%d%d%d", &n, &m, &k);
        int x = n - m * (k + 1);
        if(m == 1) {
            printf("%d\n", n);
        }
        else if(x < 0) {
            printf("0\n");
        }
        else {
            printf("%lld\n", C(x + m - 1, m - 1) * n % MOD * pow_mod(m, MOD - 2) % MOD);
        }
    }
    return 0;
}

C
題意:f(0)=0,f(1)=1,f(n)=f(n2)+2f(n1)(n2)
g(n)=ni=0f(i)2 。求解xg(ny)%(s+1) .

思路:由歐拉定理aphi(b)==1(modb)
則有ac%b=ac%phi(b)%b
g(n)=g(n1)+f(n)2
f(n)2=(f(n2)+2f(n1))2
f(n)2=f(n2)2+4f(n1)f(n2)+4f(n1)2
f(n1)f(n2)=(f(n3)+2f(n2))f(n2)
f(n1)f(n2)=2f(n2)2+f(n2)f(n3)
維護f(n)f(n1)f(n1)2f(n)2g[n] ,然後矩陣加速即可。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <cstdlib>
#define ll o<<1
#define rr o<<1|1
#define CLR(a, b) memset(a, (b), sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 2e6 + 10;
const int INF = 1e9 + 10;
const double PI = acos(-1.0);
const double eps = 1e-6;
//const int MOD = 1e9 + 7;
LL MOD;
void add(LL &x, LL y) { x += y; x %= MOD; }
LL gcd(LL a, LL b) {
    return b == 0 ? a : gcd(b, a % b);
}
struct Ma {
    LL a[4][4];
};
Ma multi(Ma x, Ma y) {
    Ma z; CLR(z.a, 0);
    for(int i = 0; i < 4; i++) {
        for(int k = 0; k < 4; k++) {
            if(x.a[i][k] == 0) continue;
            for(int j = 0; j < 4; j++) {
                add(z.a[i][j], x.a[i][k] * y.a[k][j] % MOD);
            }
        }
    }
    return z;
}
LL Solve(Ma x, LL n) {
    if(n == 0) return 0;
    if(n == 1) return 1;
    if(n == 2) return 5;
    n -= 2;
    Ma y; CLR(y.a, 0);
    for(int i = 0; i < 4; i++) {
        y.a[i][i] = 1;
    }
    while(n) {
        if(n & 1LL) {
            y = multi(x, y);
        }
        x = multi(x, x);
        n >>= 1;
    }
    return (((y.a[0][3] + 2 * y.a[1][3] % MOD) % MOD + 4 * y.a[2][3] % MOD) % MOD + 5 * y.a[3][3] % MOD) % MOD;
}
LL pow_mod(LL a, LL n, int p) {
    LL ans = 1LL;
    while(n) {
        if(n & 1LL) {
            ans = ans * a % p;
        }
        a = a * a % p;
        n >>= 1LL;
    }
    return ans;
}
LL euler(LL n) {
    LL ans = n;
    for(LL i = 2; i * i <= n; i++) {
        if(n % i == 0) {
            ans = ans / i * (i - 1);
            while(n % i == 0) {
                n /= i;
            }
        }
    }
    if(n > 1) {
        ans = ans / n * (n - 1);
    }
    return ans;
}
int main()
{
    int t; scanf("%d", &t);
    while(t--) {
        LL n, y, x, s;
        scanf("%lld%lld%lld%lld", &n, &y, &x, &s);
        MOD = euler(s + 1);
        Ma X;
        X.a[0][0] = 0; X.a[0][1] = 0; X.a[0][2] = X.a[0][3] = 1;
        X.a[1][0] = 0; X.a[1][1] = 1; X.a[1][2] = X.a[1][3] = 4;
        X.a[2][0] = 1; X.a[2][1] = 2; X.a[2][2] = X.a[2][3] = 4;
        X.a[3][0] = X.a[3][1] = X.a[3][2] = 0; X.a[3][3] = 1;
        LL ans = pow_mod(x, Solve(X, n * y), s + 1);
        printf("%lld\n", ans);
    }
    return 0;
}

G
題意:奇偶數定義——連續偶數個奇數或者連續奇數個偶數,問你區間[L,R] 奇偶數個數。
思路:數位dpdp[i][j][k] 表示處理到第i 個位置當前位置填數字j 且以數字j 結尾的連續段長度爲k 的方案數。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <cstdlib>
#define ll o<<1
#define rr o<<1|1
#define CLR(a, b) memset(a, (b), sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 5e5 + 10;
const int INF = 1e9 + 10;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) { x += y; x %= MOD; }
LL dp[20][10][20];
int a[20];
LL DFS(int pos, int now, int num, int yes) {
    //cout << pos << ' ' << now << ' ' << num  << endl;
    if(pos == -1) {
        if(num == 0) return 0LL;
        if(now & 1) {
            if(num & 1) return 0LL;
            else return 1LL;
        }
        else {
            if(num & 1) return 1LL;
            else return 0LL;
        }
    }
    if(!yes && dp[pos][now][num] != -1) return dp[pos][now][num];
    int End = yes ? a[pos] : 9;
    LL ans = 0;
    for(int i = 0; i <= End; i++) {
        if(num == 0) {
            if(i == 0) {
                ans += DFS(pos - 1, i, 0, yes && i == End);
            }
            else {
                ans += DFS(pos - 1, i, 1, yes && i == End);
            }
            continue;
        }
        if(now & 1) {
            if(i % 2 == 0) {
                if(num & 1) continue;
                ans += DFS(pos - 1, i, 1, yes && i == End);
            }
            else {
                ans += DFS(pos - 1, i, num + 1, yes && i == End);
            }
        }
        else {
            if(i & 1) {
                if(num % 2 == 0) continue;
                ans += DFS(pos - 1, i, 1, yes && i == End);
            }
            else {
                ans += DFS(pos - 1, i, num + 1, yes && i == End);
            }
        }
    }
    if(!yes) dp[pos][now][num] = ans;
    return ans;
}
LL Count(LL n) {
    int len = 0;
    while(n) {
        a[len++] = n % 10;
        n /= 10;
    }
    return DFS(len - 1, 0, 0, 1);
}
int main()
{
    int t, kcase = 1; scanf("%d", &t);
    while(t--) {
        LL L, R; scanf("%lld%lld", &L, &R);
        CLR(dp, -1);
        printf("Case #%d: %lld\n", kcase++, Count(R) - Count(L - 1));
    }
    return 0;
}

I
題意:給定N 個數字,每次若相鄰的數字a[i]a[i+1] 滿足gcd(a[i],a[i+1])!=1 則可以消去這兩個數並得到價值b[i]+b[i+1] ,問你可以得到的最大價值。
思路:區間dp ,枚舉中間點,但是注意頭尾的判斷。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <cstdlib>
#define ll o<<1
#define rr o<<1|1
#define CLR(a, b) memset(a, (b), sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int MAXN = 300 + 10;
const int INF = 1e9 + 10;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int MOD = 1e9 + 7;
bool vis[MAXN][MAXN];
LL dp[MAXN][MAXN];
LL gcd(LL a, LL b) {
    return b == 0 ? a : gcd(b, a % b);
}
LL a[MAXN], b[MAXN], sum[MAXN];
LL DFS(int l, int r) {
    if(dp[l][r] != -1) return dp[l][r];
    if(l >= r) return dp[l][r] = 0;
    if(l + 1 == r) {
        if(vis[l][r]) {
            return dp[l][r] = b[l] + b[r];
        }
        else {
            return dp[l][r] = 0;
        }
    }
    LL ans = 0;
    if(vis[l][r]) {
        ans = DFS(l + 1, r - 1);
        if(sum[r - 1] - sum[l] == ans) {
            ans += b[l] + b[r];
        }
    }
    for(int k = l; k < r; k++) {
        ans = max(ans, DFS(l, k) + DFS(k + 1, r));
    }
    dp[l][r] = ans;
    return ans;
}
int main()
{
    int t; scanf("%d", &t);
    while(t--) {
        int n; scanf("%d", &n);
        for(int i = 1; i <= n; i++) {
            scanf("%lld", &a[i]);
        }
        CLR(vis, false);
        for(int i = 1; i <= n; i++) {
            for(int j = i; j <= n; j++) {
                if(gcd(a[i], a[j]) != 1) {
                    vis[i][j] = true;
                }
            }
        }
        sum[0] = 0;
        for(int i = 1; i <= n; i++) {
            scanf("%lld", &b[i]);
            sum[i] = sum[i - 1] + b[i];
        }
        CLR(dp, -1);
        printf("%lld\n", DFS(1, n));
    }
    return 0;
}

J
題意:問你<=N 的素數個數。

論文題目?

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <cstdlib>
#define ll o<<1
#define rr o<<1|1
#define CLR(a, b) memset(a, (b), sizeof(a))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int INF = 1e9 + 10;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int MOD = 1e9 + 7;
void add(LL &x, LL y) { x += y; x %= MOD; }
using namespace std;
const int MAXN = 100;
const int MAXM = 20010;
#define chkbit(ar, i) (((ar[(i) >> 6]) & (1 << (((i) >> 1) & 31))))
#define setbit(ar, i) (((ar[(i) >> 6]) |= (1 << (((i) >> 1) & 31))))
#define isprime(x) (( (x) && ((x)&1) && (!chkbit(ar, (x)))) || ((x) == 2))
LL dp[MAXN][MAXM];
//dp[n][m]表示區間[1, m]裏面不被前n個素數任意一個整除的個數
//dp[0][m] = m;
//dp[n][m] = dp[n - 1][m] - dp[n - 1][m / p[n]]
unsigned int ar[(10000010 >> 6) + 5] = {0};
int len = 0, p[1000000], num[10000010];
LL phi(LL m, int n) {
    if(n == 0) return m;
    if(p[n - 1] >= m) return 1;
    if(m < MAXM && n < MAXN) return dp[n][m];
    return phi(m, n - 1) - phi(m / p[n - 1], n - 1);
}
LL Lehmer(LL m) {
    if(m < 10000010) return num[m];
    int s = sqrt(0.9 + m);
    int y = cbrt(0.9 + m); int c = y;
    int a = num[y];
    LL res = phi(m, a) + a - 1;
    for(int i = a; p[i] <= s; i++) {
        res = res - Lehmer(m / p[i]) + Lehmer(p[i]) - 1;
    }
    return res;
}
int main()
{
    setbit(ar, 0), setbit(ar, 1);
    for(int i = 3; (i * i) < 10000010; i++, i++) {
        if(!chkbit(ar, i)) {
            int k = i << 1;
            for(int j = (i * i); j < 10000010; j += k) setbit(ar, j);
        }
    }
    for(int i = 1; i < 10000010; i++) {
        num[i] = num[i - 1];
        if(isprime(i)) p[len++] = i, num[i]++;
    }
    for(int i = 0; i < MAXN; i++) {
        for(int j = 0; j < MAXM; j++) {
            if(!i) dp[i][j] = j;
            else dp[i][j] = dp[i - 1][j] - dp[i - 1][j / p[i - 1]];
        }
    }
    LL n;
    while(scanf("%lld", &n) != EOF) {
        printf("%lld\n", Lehmer(n));
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章