2018年第十屆ACMICPC四川省大學程序設計競賽


..拿金了 沒給學校丟臉

A

....SB題啊 比賽的時候都沒看 裸的一個bitset前綴和

先開一個1e4*1e4的二維bitset數組 初始第i個數組的值為1 << i (即B[i]=1 B[i]<<=i)

很容易我們可以知道要單獨翻轉某一位而不去影響其他位的話 方法是唯一的

然后我們從可以后往前DP 就可以知道要單獨翻轉某一位的話需要翻轉那些位

最后的每次翻轉過后的答案就是   上一個答案^PreL-1 ^PreR

注意用cout的話容易System Error......(再噴一次OJ

/*Huyyt*/
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}};
const int mod = 1e9 + 7, gakki = 5 + 2 + 1 + 19880611 + 1e9;
const int MAXN = 1e5 + 5, MAXM = 1e5 + 5, N = 1e4 + 5;
const int MAXQ = 100010;
int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1;
inline void addedge(int u, int v)
{
        to[++tot] = v;
        nxt[tot] = Head[u];
        Head[u] = tot;
}
inline void read(int &v)
{
        v = 0;
        char c = 0;
        int p = 1;
        while (c < '0' || c > '9')
        {
                if (c == '-')
                {
                        p = -1;
                }
                c = getchar();
        }
        while (c >= '0' && c <= '9')
        {
                v = (v << 3) + (v << 1) + c - '0';
                c = getchar();
        }
        v *= p;
}
int n, m, l, r;
bitset<N> B[10005], pre[10005], ans;
int main()
{
        ios_base::sync_with_stdio(0);
        cin.tie(0);
        int T;
        read(T);
        while (T--)
        {
                ans.reset();
                read(n), read(m);
                for (int i = 1; i <= n; i++)
                {
                        B[i].reset();
                }
                B[0] = 1;
                for (int i = n; i >= 1; i--)
                {
                        B[i] = 1;
                        B[i] <<= i;
                        for (int j = 2 * i; j <= n; j += i)
                        {
                                B[i] = B[i] ^ B[j];
                        }
                }
                pre[0] = 0;
                for (int i = 1; i <= n; i++)
                {
                        pre[i] = pre[i - 1] ^ B[i];
                }
                for (int i = 1; i <= m; i++)
                {
                        read(l), read(r);
                        ans = ans ^ pre[l - 1] ^ pre[r];
                        printf("%d\n", ans.count());
                }
        }
        return 0;
}
View Code

B(比賽通過)

水題

C(比賽通過)

把不同的字符串看作是點

用AC自動機建邊 跑一遍即可

D

最后沒有時間了 沒做出來 其實蠻簡單的

在沒有確定根之前 滿足一對pilot要求的點是這兩個點的子樹

在確定了一個節點為根之后 就可以分為兩種情況

1.兩個pilot不是一條鏈上的 則兩個點的子樹+1

2.兩個pilot是一條鏈上的 則先全部節點+1 再把父親到這個兒子的子樹(不包括父親節點)-1 再把兒子的子樹+1

最后看值為m的點有多少個 即為答案+1 -1的這些操作用一個數組維護 dfs從父親到兒子作前綴和即可

可以倍增(NlogN)做 也可以tarjan+dfs序(N)

倍增:

加讀入掛的話 可以減到5500ms

/*Huyyt*/
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}};
const int mod = 1e9 + 7, gakki = 5 + 2 + 1 + 19880611 + 1e9;
const int MAXN = 1e5 + 5, MAXM = 1e5 + 5;
const int maxl = 25;
const int MAXQ = 100010;
int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1;
inline void addedge(int u, int v)
{
        to[++tot] = v;
        nxt[tot] = Head[u];
        Head[u] = tot;
}
int flag = 0;
int n, m, anser = 0;
int presum[MAXN];
int grand[MAXN][maxl];  //x向上跳2^i次方的節點,x到他上面祖先2^i次方的距離
int gw[MAXN][maxl];   //維護距離的數組
int depth[MAXN];//深度
int root;
int N; //N的意思是最多能跳幾層
void dfs(int x)//dfs建圖
{
        for (int i = 1; i <= N; i++) //第一個幾點就全部都是0,第二個節點就有變化了,不理解的話建議復制代碼輸出下這些數組
        {
                grand[x][i] = grand[grand[x][i - 1]][i - 1];  //倍增 2^i=2^(i-1)+2^(i-1)
        }
        for (int i = Head[x]; i; i = nxt[i])
        {
                int v = to[i];
                if (v != grand[x][0])
                {
                        depth[v] = depth[x] + 1;
                        grand[v][0] = x;
                        dfs(v);
                }
        }
}
void Init()
{
        tot = 1;
        anser = 0;
        for (int i = 1; i <= n; i++)
        {
                presum[i] = Head[i] = 0;
        }
        N = floor(log(n + 0.0) / log(2.0));//最多能跳的2^i祖先
        depth[root] = 0; //根結點的祖先不存在,用-1表示
        depth[0] = -1;
        memset(grand, 0, sizeof(grand));
}
int lca(int a, int b)
{
        if (depth[a] > depth[b])
        {
                swap(a, b);        //保證a在b上面,便於計算
        }
        int ans = 0;
        for (int i = N; i >= 0; i--) //類似於二進制拆分,從大到小嘗試
        {
                if (depth[a] < depth[b] && depth[grand[b][i]] >= depth[a]) //a在b下面且b向上跳后不會到a上面
                {
                        b = grand[b][i];        //先把深度較大的b往上跳
                }
        }
        if (a == b)
        {
                return a;
        }
        for (int j = N; j >= 0; j--) //在同一高度了,他們一起向上跳,跳他們不相同節點,當全都跳完之后grand【a】【0】就是lca,上面有解釋哈。
        {
                if (grand[a][j] != grand[b][j])
                {
                        a = grand[a][j];
                        b = grand[b][j];
                }
        }
        return grand[a][0];
}
int lca2(int a, int b)
{
        if (depth[a] > depth[b])
        {
                swap(a, b);        //保證a在b上面,便於計算
        }
        int ans = 0;
        for (int i = N; i >= 0; i--) //類似於二進制拆分,從大到小嘗試
        {
                if (depth[a] + 1 < depth[b] && depth[grand[b][i]] >= depth[a] + 1) //a在b下面且b向上跳后不會到a上面
                {
                        b = grand[b][i];        //先把深度較大的b往上跳
                }
        }
        return b;
}
void getadd(int x, int fa)
{
        presum[x] += presum[fa];
        for (int i = Head[x]; i; i = nxt[i])
        {
                int v = to[i];
                if (v != fa)
                {
                        getadd(v, x);
                }
        }
}
int main()
{
        ios_base::sync_with_stdio(0);
        cin.tie(0);
        root = 1;
        int T;
        scanf("%d",&T);
        while (T--)
        {
                int u, v;
                scanf("%d %d", &n, &m);
                Init();
                for (int i = 1; i <= n - 1; i++)
                {
                        scanf("%d %d", &u, &v);
                        addedge(u, v), addedge(v, u);
                }
                dfs(root);
                for (int i = 1; i <= m; i++)
                {
                        scanf("%d %d", &u, &v);
                        int now = lca(u, v);
                        //cout << "lca" << now << endl;
                        if (now == u || now == v)
                        {
                                int cnt = lca2(u, v);
                                //cout << "lca2 " << cnt << endl;
                                if (now == u)
                                {
                                        presum[1]++;
                                        presum[cnt]--;
                                        presum[v]++;
                                }
                                else
                                {
                                        presum[1]++;
                                        presum[cnt]--;
                                        presum[u]++;
                                }
                        }
                        else
                        {
                                presum[u]++, presum[v]++;
                        }
                }
                getadd(1, 0);
//                for (int i = 1; i <= n; i++)
//                {
//                        cout << "presum" << i << " " << presum[i] << endl;
//                }
                for (int i = 1; i <= n; i++)
                {
                        if (presum[i] == m)
                        {
                                //cout << i << endl;
                                anser++;
                        }
                }
                cout<<anser<<endl;
        }
        return 0;
}
View Code

tarjan做法:

沒有用到dfs序 只是dfs的時候記錄了一下當前節點的兒子節點QQQnxt[u]=v

極限可以做到2500ms

更新:莫名其妙他們的OJ變快(正常)了 跑了340ms 上面的倍增則跑了1000ms

/*Huyyt*/
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int dir[8][2] = {{0, 1}, {1, 0}, {0, -1}, { -1, 0}, {1, 1}, {1, -1}, { -1, -1}, { -1, 1}};
const int mod = 1e9 + 7, gakki = 5 + 2 + 1 + 19880611 + 1e9;
const int MAXN = 1e5 + 5, MAXM = 1e5 + 5, N = 1e5 + 5;
const int MAXQ = 100010;
int to[MAXM << 1], nxt[MAXM << 1], Head[MAXN], tot = 1;
inline void addedge(int u, int v)
{
        to[++tot] = v;
        nxt[tot] = Head[u];
        Head[u] = tot;
}
inline void read(int &v)
{
        v = 0;
        char c = 0;
        int p = 1;
        while (c < '0' || c > '9')
        {
                if (c == '-')
                {
                        p = -1;
                }
                c = getchar();
        }
        while (c >= '0' && c <= '9')
        {
                v = (v << 3) + (v << 1) + c - '0';
                c = getchar();
        }
        v *= p;
}
int QQQnxt[MAXN];
int QQQanser[MAXN];
pair<int, int> QQQ[MAXM];
int n, m, anser = 0;
int presum[MAXN], ans[MAXN];
bool vis[MAXN];//訪問標記
int ancestor[MAXN];//祖先
struct Query
{
        int q, next;
        int index;//查詢編號
} query[MAXQ * 2];
int tt, Q, h[MAXQ], answer[MAXQ];
int F[MAXN];//需要初始化為-1
int find(int x)
{
        if (F[x] == -1)
        {
                return x;
        }
        return F[x] = find(F[x]);
}
void bing(int u, int v)
{
        int t1 = find(u);
        int t2 = find(v);
        if (t1 != t2)
        {
                F[t1] = t2;
        }
}
inline void add_query(int u, int v, int index)
{
        query[tt].q = v;
        query[tt].next = h[u];
        query[tt].index = index;
        h[u] = tt++;
        query[tt].q = u;
        query[tt].next = h[v];
        query[tt].index = index;
        h[v] = tt++;
}
void LCA(int u)
{
        ancestor[u] = u;
        vis[u] = true;
        for (int i = Head[u]; i; i = nxt[i])
        {
                int v = to[i];
                QQQnxt[u] = v;
                if (vis[v])
                {
                        continue;
                }
                LCA(v);
                bing(u, v);
                ancestor[find(u)] = u;
        }
        for (int i = h[u]; i != -1; i = query[i].next)
        {
                int v = query[i].q;
                if (vis[v])
                {
                        answer[query[i].index] = ancestor[find(v)];
                        if (answer[query[i].index] == v)
                        {
                                QQQanser[query[i].index] = QQQnxt[v];
                        }
                }
        }
}
void init()
{
        tt = tot = 1;
        anser = 0;
        for (int i = 1; i <= n; i++)
        {
                ancestor[i] = presum[i] = Head[i] = 0;
                vis[i] = F[i] = h[i] = -1;
        }
}
void getadd(int x, int fa)
{
        presum[x] += presum[fa];
        for (int i = Head[x]; i; i = nxt[i])
        {
                int v = to[i];
                if (v != fa)
                {
                        getadd(v, x);
                }
        }
}
int main()
{
        ios_base::sync_with_stdio(0);
        cin.tie(0);
        int T;
        read(T);
        while (T--)
        {
                int u, v;
                read(n), read(m);
                init();
                for (int i = 1; i <= n - 1; i++)
                {
                        read(u), read(v);
                        addedge(u, v), addedge(v, u);
                }
                for (int i = 0; i < m; i++)
                {
                        read(u), read(v);
                        add_query(u, v, i);
                        QQQ[i] = make_pair(u, v);
                }
                LCA(1);
                for (int i = 0; i < m; i++)
                {
                        //cout << answer[i] << endl;
                        if (answer[i] == QQQ[i].first)
                        {
                                //cout<<QQQanser[i]<<endl;
                                presum[1]++;
                                presum[QQQanser[i]]--;
                                presum[QQQ[i].second]++;
                        }
                        else if (answer[i] == QQQ[i].second)
                        {
                                //cout<<QQQanser[i]<<endl;
                                presum[1]++;
                                presum[QQQanser[i]]--;
                                presum[QQQ[i].first]++;
                        }
                        else
                        {
                                presum[QQQ[i].first]++;
                                presum[QQQ[i].second]++;
                        }
                }
                getadd(1, 0);
                //                for (int i = 1; i <= n; i++)
                //                {
                //                        cout << "presum" << i << " " << presum[i] << endl;
                //                        cout << "ans" << i << " " << ans[i] << endl;
                //                }
                for (int i = 1; i <= n; i++)
                {
                        if (presum[i] == m)
                        {
                                //cout << i << endl;
                                anser++;
                        }
                }
                printf("%d\n", anser);
        }
        return 0;
}
View Code

E(比賽通過)

注意年=月=日的特殊合法情況即可

F(比賽通過)

如果知道中位數的話 我們就可以n2logn暴力地知道答案 剩下就是怎么找中位數的問題

找中位數可以用權值線段樹來做 注意單個點權值占一半以上的情況

總復雜度3*T*n2logn/2

G

無視

H(比賽通過)

水題

I(比賽通過)

樹分治中點分治的一個小部分 變成帶權的了 直接dfs一次即可

J(比賽通過)

結論題

很容易可以知道前三個我們肯定是可以確認是原數列的前三個

因為A0=0   A0+A1   A0+A2這三個肯定是最小的

比如樣例0 1 2 2 我們可以先確認前三個0 1 2

則這三個產生的數列是1 2 3 接下來我們看與目標數列1 2 2 3 3 4對比最小的缺什么

很容易發現缺了個2 所以我們必須補個2

因為數列是非遞減的Ai與之前數相加產生的數不大於Ai+1與之前數相加所產生的數

這樣繼續check直到數列被填滿

寫的話就是直接暴力找 看起來復雜度會爆炸 但其實中間很多就直接break相當於剪枝了 能過

K

題意:

你要玩一個猜數游戲 答案為X 你最多只能問N次 問的時間最多不能超過V

第i次詢問你可以選一個數Y猜 如果Y大於X的話 會花費Ai的時間 不然的話會花費Ai+Bi的時間

你只有每次猜完后才可以猜下一次 問你最后可以從1開始猜到的最大區間長度為多少

解:

dp dp[i][j]表示只使用詢問 i到n 而且時間不超過j所能知道的答案

dp[i][j] = dp[i+1][j-Ai] + dp[i+1][j-Ai-Bi]   但是邊界的時候需要考慮一些細節... 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM