2012-2013 Northwestern European Regional Contest (NWERC 2012)【solved:6 / 11】

来源:互联网 发布:淘宝老店铺 编辑:程序博客网 时间:2024/06/05 14:33

有一个大家都会的D。 emmm。我不会 但是!队友会呀2333先放放
-update:2017年10月16日20:49:39

A - Admiral

题意:给一张带权图,让你求出从s到t两条完全不同的路径的权值加和最小为多少。

思路:网络流。拆点入门题。s点和t点内部流量为2,其余为1。跑最小费用最大流即可。

#include <bits/stdc++.h>using namespace std;typedef long long LL;const int MAXN = 2 * 1000 + 5;const int INF = 0x3f3f3f3f;typedef pair<int, int>pii;struct MCMF{    int n,m;    struct Edge{int from, to, cap, flow, cost;};    vector<Edge> edge;    vector<int> g[MAXN];    bool inq[MAXN];    int d[MAXN]/*spfa*/, pre[MAXN]/*上一条弧*/, a[MAXN]/*可改进量*/;    void init(int n){        this->n = n;        for(int i = 1; i <= n; i++) g[i].clear();        edge.clear();    }    void addedge(int from, int to, int cap, int cost)    {        edge.push_back({from,to,cap,0,cost});        edge.push_back({to,from,0,0,-cost});        m = edge.size();        g[from].push_back(m-2);        g[to].push_back(m-1);    }    bool spfa(int s, int t, int& flow, LL& cost)    {        for(int i = 1; i <= n; i++) inq[i] = 0, d[i] = INF;        d[s] = 0, inq[s] = true, pre[s] = 0, a[s] = INF;        queue<int> q;        q.push(s);        while(!q.empty())        {            int u = q.front();q.pop();            inq[u] = false;            for(int i = 0; i < g[u].size(); i++)            {                Edge& e = edge[g[u][i]];                if(e.cap > e.flow && d[e.to] > d[u]+e.cost)                {                    d[e.to] = d[u]+e.cost;                    pre[e.to] = g[u][i];                    a[e.to] = min(a[u], e.cap-e.flow);                    if(!inq[e.to]) q.push(e.to), inq[e.to] = true;                }            }        }        if(INF == d[t]) return false;        flow += a[t];        cost += (LL)d[t]*(LL)a[t];        for(int u = t; u != s; u = edge[pre[u]].from)        {            edge[pre[u]].flow += a[t];            edge[pre[u]^1].flow -= a[t];        }        return true;    }    //需要保证初始网络没有负圈,返回最大流量,cost才是最小花费    int mincostmaxflow(int s, int t, LL & cost)    {        int flow = 0;        cost = 0;        while(spfa(s,t,flow,cost));        return flow;    }}mcmf;int main(){    int vs, es;    while(~scanf("%d%d", &vs, &es))    {        mcmf.init(vs << 1);        for(int i = 2; i < vs; i++) mcmf.addedge(i, i + vs, 1, 0);        mcmf.addedge(1, vs + 1, 2, 0);        mcmf.addedge(vs, vs << 1, 2, 0);        for(int i = 0; i < es; i++)        {            int a, b, c;            scanf("%d%d%d", &a, &b, &c);            mcmf.addedge(a + vs, b, 1, c);        }        long long ans = 0;        mcmf.mincostmaxflow(1, vs << 1, ans);        printf("%lld\n", ans);    }    return 0;}

B - Beer Pressure(搜索+哈希状态)

题意:给你n个人已获得的票数,告诉你参与投票的人总人数为K,问你每个人赢得最后获选的概率是多少。每个人投票时,投票概率不等价,投第i个人的概率为,第i个人当前获得票数/当前已投票人数。如果最后k票投完,有人同为票数最高者,平分胜率。(n5k50

思路:相当于一个dp[a][b][c][d][e],每一维度皆为50,看上去是505个状态,然而由于k只有50。相当于是5个非负整数加和为50的状态数,易得C(54,5),也就几百万个状态,所以直接宽搜即可,然后通过51进制的哈希来表示每个状态。哦对了,注意!!!printf里面输出”%”是通过%%而不是%不然你会wa的不知所措orz。

#include <bits/stdc++.h>using namespace std;const int base = 51;int n, k, pub[10], temp[10];double ans[10];inline long long calc(int a[]){    long long ret = 0;    for(int i = 0; i < n; i++)  ret = ret * base + a[i];    return ret;}int main(){    while(~scanf("%d%d", &n, &k))    {        for(int i = 0; i < n; i++)  ans[i] = 0;        for(int i = 0; i < n; i++)  scanf("%d", &pub[i]);        unordered_map<long long, double>pro;        queue<long long>que;        que.push(calc(pub));        pro[calc(pub)] = 1.0;        while(que.size())        {            long long cur = que.front();que.pop();            long long tot = 0;            for(int i = 0; i < n; i++)                temp[n - 1 - i] = cur % base, cur /= base, tot += temp[n - 1 - i];            long long st = calc(temp);            if(tot == k)            {                int mx = *max_element(temp, temp + n);                int cnt = count(temp, temp + n, mx);                for(int i = 0; i < n; i++)                    if(temp[i] == mx)   ans[i] += 1.0 / cnt * pro[st];                continue;            }            for(int i = 0; i < n; i++)            {                double p = 1.0 * temp[i] / tot;                temp[i]++;                long long fst = calc(temp);                if(pro[fst] == 0)    que.push(fst);                pro[fst] += pro[st] * p;                temp[i]--;            }        }        for(int i = 0; i < n; i++)  printf("pub %d: %.2f %%\n", i + 1, ans[i] * 100);    }    return 0;}

E - Edge Case

思路:写了几个发现感觉是斐波那契数列,直接加了个大数类A了。

I - Idol

2-SAT板子题。

#include <bits/stdc++.h>using namespace std;const int maxn  = 2000 + 5;vector<int>G[maxn];int pre[maxn];int lowlink[maxn];//lowlink[u]:u及其后代能追溯到的最早祖先点v的pre[v]的值。int sccno[maxn], dfs_clock, scc_cnt;stack<int>S;void Tarjan(int u){    pre[u] = lowlink[u] = ++dfs_clock;    S.push(u);    for(int i = 0; i < G[u].size(); i++)    {        int v = G[u][i];        if(pre[v] == 0)        {            Tarjan(v);            lowlink[u] = min(lowlink[u], lowlink[v]);        }        else if(!sccno[v])        {            lowlink[u] = min(lowlink[u], pre[v]);        }    }    if(lowlink[u] == pre[u])    {        scc_cnt++;        for(;;)        {            int x = S.top();S.pop();            sccno[x] = scc_cnt;            if(x == u)  break;        }    }}void find_scc(int n){    dfs_clock = scc_cnt = 0;    memset(sccno, 0, sizeof(sccno));    memset(pre, 0, sizeof(pre));    for(int i = 1; i <= n; i++)    {        if(pre[i] == 0) Tarjan(i);    }}int n, m;int ans[maxn];pair<int, int>a[maxn];bool solve(){    memset(ans, 0, sizeof(ans));    for(int i = 1; i <= n; i++)    {        if(sccno[i] == sccno[i + n])    return false;    }    int flag = 0;    if(sccno[1] > sccno[1 + n]) flag = 1;    for(int i = 1; i <= n; i++)    {        if(flag)    ans[i] = (sccno[i] > sccno[i + n]);        else ans[i] = (sccno[i] < sccno[i + n]);    }    for(int i = 0; i < m; i++)    {        int x = a[i].first, y = a[i].second;        int jx = (x > 0), jy = (y > 0);        x = abs(x), y = abs(y);        if((ans[x] != jx) && (ans[y] != jy))    return false;    }    return true;}int main(){    while(~scanf("%d%d", &n, &m))    {        for(int i = 1; i <= 2 * n; i++) G[i].clear();        for(int i = 0; i < m; i++)        {            int x, y;            scanf("%d%d", &x, &y);            a[i] = {x, y};            if(x > 0 && y > 0)//x && y                G[x + n].push_back(y), G[y + n].push_back(x);            else if(x < 0 && y < 0)            {                x = abs(x), y = abs(y);                G[x].push_back(y + n), G[y].push_back(x + n);            }            else if(x > 0 && y < 0)            {                y = abs(y);                G[x + n].push_back(y + n);                G[y].push_back(x);            }            else if(x < 0 && y > 0)            {                swap(x, y);                y = abs(y);                G[x + n].push_back(y + n);                G[y].push_back(x);            }        }        find_scc(2 * n);        if(solve()) puts("yes");        else puts("no");    }    return 0;}

J - Joint Venture

题意:给你n个数,问你里头有没有两个加和为x的。(0n1e6x1e8)

思路:水题,小心一个a[i] == x - a[i]的特判即可。

K - Key Insight

题意:给你两串字符,然后告诉你长度k为一段,串一串二在每一段中一一对应,得到若干种对应方案,然后问你整个串中有多少种方案使得,所有段均满足。

思路:先按题意连边,同一段中,相同的两个字母连边。又易得,两点之间只有边数==段数,才应该在新图中连边。得到新图以后,直接搜联通块,联通块为偶数即可。(易证,今年多校也有一个这种题)然后每个联通块的贡献度即为其大小siz/2的阶乘。

#include <bits/stdc++.h>using namespace std;char s1[105], s2[105];int pre[205][205], in[205], vis[205];vector<int>G[205];int siz = 0;int dfs(int u){    vis[u] = 1;    int ret = 1;    for(auto o : G[u])  if(vis[o] == 0) ret += dfs(o);    return ret;}long long fac(int siz){long long ret = 1;for(int i = 1; i <= siz; i++) ret *= i; return ret;};int main(){    int block;    while(~scanf("%d", &block))    {        memset(vis, 0, sizeof(vis));        memset(pre, 0, sizeof(pre));        for(int i = 0; i <= 200; i++)   G[i].clear();        scanf("%s%s", s1 + 1, s2 + 1);        int len = strlen(s1 + 1);        int need = len / block;        for(int i = 1; i <= len; i += block)        {            for(int j = 0; j < block; j++)            {                for(int k = 0; k < block; k++)                {                    if(s1[j + i] == s2[k + i])                    {                        pre[j + 1][k + 1 + block]++, pre[k + 1 + block][j + 1]++;                    }                }            }        }        for(int i = 1; i <= block * 2; i++)        {            for(int j = block + 1; j <= block * 2; j++)            {                if(pre[i][j] >= need)                {                    G[i].push_back(j);                    G[j].push_back(i);                    in[i]++, in[j]++;                }            }        }        long long ans = 1;        for(int i = 1; i <= block; i++)        {            if(vis[i])  continue;            int siz = dfs(i);            if(siz & 1) ans = 0;            else ans *= fac(siz/2);        }        printf("%I64d\n", ans);    }    return 0;}
阅读全文
0 0
原创粉丝点击