您的位置:首页 > 运维架构

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

2017-09-01 14:44 447 查看
有一个大家都会的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票投完,有人同为票数最高者,平分胜率。(n≤5,k≤50)

思路:相当于一个dp[a][c][d][e],每一维度皆为50,看上去是505个状态,然而由于k只有50。相当于是5个非负整数加和为50的状态数,易得C(54,5),也就几百万个状态,所以直接宽搜即可,然后通过51进制的哈希来表示每个状态。[b]哦对了,注意!!!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的。(0≤n≤1e6,x≤1e8)

思路:水题,小心一个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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐