2021牛客多校第三场

来源

C - Minimum grid(最大匹配)

想要代价最小,填的数当然是越少越好。对于一个位置,如果它所在的行和列的最大值为x,那么这个位置填x是最优的。如果填的数比x大,矛盾;如果比x小,相当于没填,白白浪费一个位置。

将数组b、c的数称为限定数,那么对于每个x,它有n'个行限定数,m'个列限定数。那么x要满足n'+m'个行和列。设置n'个结点和m‘个结点,如果存在合法位置(i,j)行列最大值均为x,就连一条边(i,j),求最大匹配t。由于必定存在合法方案,剩余需要取的行列就剩下n'+m'-2*t,x所需填的个数为n'+m'-2*t+t=n'+m'-t。x的贡献为x(n'+m'-t)。

代码用的是逆十字的写法,大佬的代码写的就是好。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;

const int N = 1e4 + 10;
const int M = 1e6 + 10;
const double eps = 1e5;
typedef long long ll;

struct edge {
    int ne, np, f;
};
edge ed[M];
int head[N];
int cur[N];
int si = 2;
int dis[N];
int arr[N];

void init(int n) {
    si = 2;
    for(int i = 0; i <= n; i++) {
        head[i] = 0;
        cur[i] = 0;
    }
}

void add(int u, int v, int f) {
    ed[si] = edge{head[u], v, f};
    head[u] = si;
    cur[u] = head[u];
    si++;

    ed[si] = edge{head[v], u, 0};
    head[v] = si;
    cur[v] = head[v];
    si++;
}

bool bfs(int s, int t) {
    memset(dis, 0, sizeof dis);
    for(int i = 1; i <= t; i++) cur[i] = head[i]; // 当前弧优化,注意这里初始化是所有的点初始化
    queue<int> q;
    q.push(s);
    dis[s] = 1;
    while(!q.empty()) {
        int cur = q.front();
        q.pop();
        for(int i = head[cur]; i; i = ed[i].ne) {
            int nt = ed[i].np;
            if(dis[nt] || (!ed[i].f)) continue;
            dis[nt] = dis[cur] + 1;
            q.push(nt);
        }
    }
    return dis[t];
}

int dfs(int p, int t, int flo) {
    if(p == t) return flo;
    int delta = flo;
    
    for(int &i = cur[p]; i; i = ed[i].ne) {
        int nt = ed[i].np;
        if(dis[nt] == dis[p] + 1 && ed[i].f) {
            int d = dfs(nt, t, min(delta, ed[i].f));
            delta -= d;
            ed[i].f -= d; ed[i^1].f += d;
            if(delta == 0) break;
        }
    }
    return flo - delta;
}

ll dini(int s, int t) { // 网络流求最大匹配
    ll ans = 0;
    while(bfs(s, t)) {
        ans += dfs(s, t, INF);
    }
    return ans; 
}
typedef pair<int, int> PII;
int brr[N], crr[N];
int sr[M], sc[M];
vector<PII> pos[M];

unordered_map<int, int> mar, mac;

int main() {
    int n, m, k;
    cin >> n >> m >> k;
    for(int i = 1; i <= n; i++) cin >> brr[i], sr[brr[i]]++;
    for(int i = 1; i <= n; i++) cin >> crr[i], sc[crr[i]]++;
    for(int i = 1; i <= m; i++) {
        int x, y;
        cin >> x >> y;
        if(brr[x] == crr[y]) pos[brr[x]].push_back({x, y});
    }
    ll ans = 0;
    for(int i = 1000000; i; i--) {
        if(!(sr[i] + sc[i])) continue;
        init(sr[i] + sc[i] + 2);
        mar.clear(), mac.clear();
        int s = 1, t = sr[i] + sc[i] + 2;
        for(int j = 2; j <= sr[i] + 1; j++) add(s, j, 1);
        for(int j = sr[i] + 2; j < t; j++) add(j, t, 1);
        int p1 = 2, p2 = sr[i] + 2;
        for(auto p : pos[i]) {
            if(!mar[p.first]) mar[p.first] = p1++;
            if(!mac[p.second]) mac[p.second] = p2++;
            add(mar[p.first], mac[p.second], 1);
        } 
        int res = dini(s, t);
        ans += 1ll * (sr[i] + sc[i] - res) * i;
    }
    cout << ans << endl;
}

E - Math(数学,打表,OEIS)

\((xy+1)|(x^2+y^2) \to k(xy+1)=x^2+y^2\)

将y看作未知量,由韦达定理

\(y^2 -kxy + (x^2-k) = 0 \to\)

\(yy'=x^2-k\)

\(y+y'=kx\)

不妨设\(x\le y\),有\(y'\le x\le y\)\((x, y')\)也是一组解

利用\(y'\)\(x\)可以求出\(y\),然后由于\(x\)\(y\)地位相同可以互换,于是又可以用\(x\)\(y\)求出\(x'\)......

可以一直递增下去,存在递推关系(通过互换\(x\), \(y\)\(y+y'=kx\)

\((y', x)\to(x, kx-y')\to...\)

初始时\(y'=0\),有

\(y=kx\)

\(k=x^2\)

即初始解为\((0, x)\)\(k=x^2\)。通过枚举不同的\(x\)取值,可得所有解。由于这个递推式递增得很快,直接暴力求即可。将结果储存,二分查找答案。时间复杂度\(O(n^{\frac{1}{3}})\)

当然也可以打表找规律,我太菜,看不出规律。于是我直接去OEIS让它帮我找了(滑稽)。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const ll M = 1e18;
vector<ll> ans;
ll dp[N];
 
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
 
int main() {
    IOS;
    for(int k = 2; 1ll * k * k * k <= M; k++) {
        dp[0] = 0;
        dp[1] = k;
        for(int i = 2; ; i++) {
            __int128 tmp = 1ll * k * k;
            tmp = tmp * dp[i - 1] - dp[i - 2];
            if(tmp > M) break;
            dp[i] = tmp;
            ans.push_back(dp[i]);
        }
    }
    sort(ans.begin(), ans.end());
    // cout << ans.size() << endl;
    int t;
    cin >> t;
    while(t--) {
        ll n;
        cin >> n;
        cout << upper_bound(ans.begin(), ans.end(), n) - ans.begin() + 1 << endl;
    }
}

I - Kuriyama Mirai and Exclusive Or(异或,思维)

这个大佬的题解写得通俗易懂,看了他的题解终于搞明白了。

主要讲第二个操作,设\({\rm lowbit(x)}=2^k\),对于\(i-l<2^k\)\(a\bigoplus(x + i-l)=a\bigoplus x \bigoplus (i-l)\),相当于给区间\([l,l+2^k)\)异或上\(x\),在依次异或\(0,1,2,...,2^k-1\),用标记\(b_{l,k}\)代表后一个操作,随后\(l\to l+2^k,x \to x+2^k\),直到\(l>r\)。最后还要\(2^k\)从大到小填充最后一段未处理的区间。

对于标记\(b_{l,k}\)​,异或\(0,1,2,...,2^k-1\)​等价于异或\(0,1,2,...,2^{k-1}-1,2^{k-1}|0, 2^{k-1}|1, ..., 2^{k-1}|(2^{k-1}-1)\)​,即给区间\([l+2^{k-1},l+2^{k})\)​异或上\(2^{k-1}\)​,然后标记分裂成\(b_{l,k-1}\)​和\(b_{l+2^{k-1},k-1}\)​​。

\(k\)最大只要开到\(\log(n)\),与异或的值域无关。

注意使用bitset,这题卡空间。

#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 6e5 + 10;
const double eps = 1e-5;

int arr[N];
bitset<25> tag[N];
int pw[N];

void upd(int l, int r, int x) {
    for(int i = 0; i <= 20; i++) {
        if((x & pw[i]) && (l + pw[i] - 1 <= r)) {
            arr[l] ^= x;
            arr[l + pw[i]] ^= x;
            tag[l][i].flip();
            l += pw[i];
            x += pw[i];
        }
    }
    for(int i = 20; i >= 0; i--) {
        if(l + pw[i] - 1 <= r) {
            arr[l] ^= x;
            arr[l + pw[i]] ^= x;
            tag[l][i].flip();
            l += pw[i];
            x += pw[i];
        }
    }
}

void solve(int n) {
    for(int i = 20; i > 0; i--) {
        for(int j = 1; j <= n; j++) {
            if(tag[j][i]) {
                tag[j][i - 1].flip();
                if(j + pw[i - 1] <= n) {
                    arr[j + pw[i - 1]] ^= pw[i - 1];
                    tag[j + pw[i - 1]][i - 1].flip();
                }
                if(j + pw[i] <= n) arr[j + pw[i]] ^= pw[i - 1];
            }
        }
    }
}

int main() {
    IOS;
    pw[0] = 1;
    for(int i = 1; i <= 30; i++) pw[i] = (pw[i - 1] << 1);
    int n, q;
    cin >> n >> q;
    for(int i = 1; i <= n; i++) {
        cin >> arr[i];
    }
    for(int i = n; i >= 1; i--) arr[i] ^= arr[i - 1];
    while(q--) {
        int ty, l, r, x;
        cin >> ty >> l >> r >> x;
        if(ty == 0) {
            arr[l] ^= x;
            arr[r + 1] ^= x;
        } else {
            upd(l, r, x);
        }
    }
    solve(n);
    for(int i = 1; i <= n; i++) {
        arr[i] ^= arr[i - 1];
        cout << arr[i] << " \n"[i == n];
    }
}
posted @ 2021-08-01 14:07  limil  阅读(96)  评论(0编辑  收藏  举报