2018沈阳网络赛

2018沈阳网络赛

#ACM-ICPC 2018 沈阳赛区网络预赛

D - k短路(A* + dijkstra)

A*算法可看此博客

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 1000;
const int INF = 1000000000;
typedef pair<int, int> PII;
struct Anode {
	int id, h, f;
	Anode(int _id, int _h, int _f){id = _id, h = _h, f = _f;}
	friend bool operator < (Anode a, Anode b) {
		if(a.f != b.f)return a.f > b.f;
		return a.h > b.h;
	}
};
vector<PII>G[maxn + 5], rG[maxn + 5];
int dis[maxn + 5];
int N, M, S, E, K, T;
void Init() {
	for(int i = 1; i <= N; i++) {
		G[i].clear();
		rG[i].clear(); 
		dis[i] = INF;
	}
	scanf("%d%d%d%d", &S, &E, &K, &T);
	for(int i = 1; i <= M; i++) {
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w);
		G[u].push_back(PII(w, v));
		rG[v].push_back(PII(w, u));
	}
}

void dijkstra(int s) {
	priority_queue<PII, vector<PII>, greater<PII> >que;
	dis[s] = 0;
	que.push(PII(0, s));
	while(!que.empty()) {
		PII p = que.top(); que.pop();
		int v = p.second;
		if(dis[v] < p.first)continue;
		for(int i = 0; i < rG[v].size(); i++) {
			int u = rG[v][i].second;
			int w = rG[v][i].first;
			if(dis[u] > dis[v] + w) {
				dis[u] = dis[v] + w;
				que.push(PII(dis[u], u));
			}
		}
	}
}
priority_queue<Anode>que;
int Astar(int s, int t) {
    if(dis[s] == INF)return -1;
	while(!que.empty())que.pop();
	que.push(Anode(s, 0, dis[s]));
	int cur = 0;
	while(!que.empty()) {
		Anode p = que.top(); que.pop();
		if(p.id == t)cur++;
		if(cur == K)return p.h;
		if(p.h > T)return -1;
		for(int i = 0; i < G[p.id].size(); i++) {
			int v = G[p.id][i].second;
			int w = G[p.id][i].first;
			que.push(Anode(v, p.h + w, p.h + w + dis[v]));
		}
	}
	return -1;
}
int main() {
	while(scanf("%d%d", &N, &M) != EOF) {
		Init();
		dijkstra(E);
		int w = Astar(S, E);
		if(w <= T && w != -1)printf("yareyaredawa\n");
		else printf("Whitesnake!\n");
	}
	return 0;
}

F - 网络流上下界 (预处理 + 网络流最大流 ) 判断是否满流

【题目大意】
给你一个二分图,问你是否能删掉一些边使得所有点的度都在[L,R]区间内。

【思路】

首先可以处理出每个点的度为多少,然后分三种情况:
Min_ind, Max_ind 分别代表所有点中最小的度和最大的度。

  • 如果Min_ind < L的话就一定不会满足。

  • 如果Min_ind >= L && Max_ind <= R 的话就已经满足了。

  • 如果Max_ind > R的话就是我们需要考虑的问题了。

首先对于所有的边,假如它连接的两个点ind[u]>R&&ind[v]>R的话就可以给他删掉,因为这样不影响满足条件的点,如果ind[u]<=R&&ind[v]<=R那么这条边就不用管,因为已经是满足条件的边了。现在设度大于R的记为集合A,其余的记为集合B,那么对于ind[u],ind[v]一个在A集合,一个在B集合的这种我们给他建上边,权值为1。然后对所有A集合中的点建立一个源点,边的权值就为ind[]-R,可看为需要减少的量,然后对于B集合全部指向一个汇点,边权即为ind[]-L,可看为最多能够减少的量。现在要求的就是看所有的从源点出发的流的和是否全部能流向汇点,即该图是否为满流。

【代码】

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 40000;
const int INF = 1000000000;
struct edge {
	int to, cap, rev;
	edge(int _to, int _cap, int _rev) {
		to = _to, cap = _cap, rev = _rev;
	}
};
int N, M, K, L, R;
vector<edge>G[maxn + 5];
int u[maxn + 5], v[maxn + 5];
int dep[maxn + 5], ind[maxn + 5], iter[maxn + 5];
bool vis[maxn + 5];
void add_edge(int from, int to, int cap) {
	G[from].push_back(edge(to, cap, G[to].size()));
	G[to].push_back(edge(from, 0, G[from].size() - 1));
}
void Init() {
	memset(ind, 0, sizeof(ind));
	scanf("%d%d", &L, &R);
	for(int i = 1; i <= K; i++) {
		scanf("%d%d", &u[i], &v[i]);
		v[i] += N;
		ind[u[i]]++;
		ind[v[i]]++;
	}

}
void bfs(int s) {
	memset(dep, -1, sizeof(dep));
	queue<int>que;
	dep[s] = 0;
	que.push(s);
	while(!que.empty()) {
		int v = que.front(); que.pop();
		for(int i = 0; i < G[v].size(); i++) {
			edge &e = G[v][i];
			if(e.cap > 0 && dep[e.to] < 0) {
				dep[e.to] = dep[v] + 1;
				que.push(e.to);
			}
		}
	}
}

int dfs(int v, int t, int f) {
	if(v == t)return f;
	for(int &i = iter[v]; i < G[v].size(); i++) {
		edge &e = G[v][i];
		if(e.cap > 0 && dep[v] < dep[e.to]) {
			int d = dfs(e.to, t, min(f, e.cap));
			if(d > 0) {
				e.cap -= d;
				G[e.to][e.rev].cap += d;
				return d;
			}
		}
	}
	return 0;
}
int Dinic(int s, int t) {
	int flow = 0;
	for(;;) {
		bfs(s);
		if(dep[t] < 0)return flow;
		memset(iter, 0, sizeof(iter));
		int f;
		while((f = dfs(s, t, INF)) > 0)flow += f;
	}
}
void solve() {
	for(int i = 0; i <= N + M + 1; i++)G[i].clear();
	int Max_ind = 0, Min_ind = N;
	for(int i = 1; i <= N + M; i++) {
		Max_ind = max(Max_ind, ind[i]);
		Min_ind = min(Min_ind, ind[i]);
	}
	if(Min_ind >= L) {
		if(Max_ind <= R)printf("Yes\n");
		else {
			int s = 0, t = N + M + 1, sum = 0;
			memset(vis, 0, sizeof(vis));
			for(int i = 1; i <= K; i++) {
				if(ind[u[i]] > R && ind[v[i]] > R) {
					ind[u[i]]--;
					ind[v[i]]--;
					vis[i] = 1;
				}
			}
			for(int i = 1; i <= K; i++) {
				if(vis[i] == 0) {
					if(ind[u[i]] > R && ind[v[i]] > L)add_edge(u[i], v[i], 1);
					else if(ind[v[i]] > R && ind[u[i]] > L)add_edge(v[i], u[i], 1);
				}
			}
			for(int i = 1; i <= N + M; i++) {
				if(ind[i] > R) {
					add_edge(s, i, ind[i] - R);
					sum += ind[i] - R;
				}
				else if(ind[i] > L)add_edge(i, t, ind[i] - L);
			}
			int res = Dinic(0, N + M + 1);
			if(res == sum)printf("Yes\n");
			else printf("No\n");
		}
	}
	else printf("No\n");
}
int main() {
	int cas = 0;
	while(~scanf("%d%d%d", &N, &M, &K)) {
		Init();
		printf("Case %d: ", ++cas);
		solve();
	}
}

G - 数学 、拆分质数、奇偶容斥

【题目大意】
已知\begin{equation}a_n=
\left{\begin{aligned}
0,\quad n=0 \
1,\quad n=1\
\dfrac{3a_{n-1}-a_{n-2}}{2}+n+1,n>1
\end{aligned}
\right.
\end{equation}
i=1pabi,其中bi与m互质且1bin

【思路】
通过打表可以发现an=n(n+1),所以先可以求出所有的小于n的数,通过推导12+23+34++n(n+1)=
11+22+33++nn+1+2+3++n=
n(n+1)(2n+1)/6+n(n+1)/2=
n(n+1)(n+2)/3
可算出所有小于n的数的和。
然后要去除所有与m不互质的数,那么可以筛出所有m的不同质因子,然后通过减掉某个因子的倍数,通过容斥,奇加偶减,即奇数个质因子就减,偶数个就加,因为对于一个因子为x,即求所有小于m的x倍数的项,求法如下
x(x+1)+假设有len项,即可化为
xxlen(len+1)(2len+2)/6+xlen(len+1)/2.
具体见代码
【代码】

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL Pt = 1e9 + 7;
const int maxn = 10000;
LL P, P2, P3;
int prime[maxn + 5], vis[maxn];
LL n;
int m, tot;
vector<int>vec;
LL qwe(LL x, LL y) {
    LL t = 1;
    while(y > 0) {
        if(y & 1)t = t * x % Pt;
        x = x * x % Pt;
        y >>= 1;
    }
    return t;
}
void Init() {
    for(int i = 2; i <= maxn; i++) {
        if(!vis[i]) {
            prime[++tot] = i;
            for(int j = i * 2; j <= maxn; j += i)vis[i] = 1;
        }
    }
}
void Getvec() {
    int x = m;
    for(int i = 1; prime[i] <= sqrt(x); i++) {
        if(x % prime[i] == 0) {
            vec.push_back(prime[i]);
            while(x % prime[i] == 0)x /= prime[i];
        }
    }
    if(x > 1)vec.push_back(x);
}
void solve() {
	LL res = n * (n + 1) % Pt * (n + 2) % Pt * P % Pt;
	int t = vec.size();
	for(int j = 1; j < (1 << t); j++) {
		LL cur = 1;
		int cnt = 0;
		for(int k = 0; k < t; k++) {
			if(j & (1 << k)) {
				cur *= vec[k];
				cnt++;
			}
		}
		LL l = n / cur;
		LL tmp = l * (l + 1) % Pt * (2 * l + 1) % Pt * P2 % Pt;
		tmp = tmp * cur % Pt * cur % Pt;
		tmp = (tmp + cur * l % Pt * (l + 1) % Pt * P3 % Pt) % Pt;
		if(cnt & 1)res = ((res - tmp) % Pt + Pt) % Pt;
		else res = (res + tmp) % Pt;
	}
	printf("%lld\n", res);
}
int main() {
	Init();
	P = qwe(3, Pt - 2);
	P2 = qwe(6, Pt - 2);
	P3 = qwe(2, Pt - 2);
	while(scanf("%lld%d", &n, &m) != EOF) {
		vec.clear();
		Getvec();
		solve();
	}
	return 0;
}

I - 模拟

【题目大意】
题意大概就是给你一串密文然你通过它给你的规则解码。
具体规则就是给你的字符串每一位都是一个十六进制,首先然你将起变为01串,之后每九位看做一串,这九位中第九位为奇偶校验位,就是前八位如果有奇数个1那么第九位应该得为0,反之得为1,如果满足这个条件该九位01串的前八位就加入到真正解密的串中。然后给你一些前缀不同的01串且每个01串对应一个字符,最后你要输出明文。

直接上代码了:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<string>
#include<algorithm>
#include<map>
using namespace std;
const int maxn = 200000;
map<string, int>vis;
char s[maxn + 5];
int m, n, tot, ch;
string f[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"};
int main() {
	int T; scanf("%d", &T);
	while(T--) {
		vis.clear();
		tot = 0;
		scanf("%d%d", &m, &n);
		for(int i = 1; i <= n; i++) {
			scanf("%d%s", &ch, s);
			vis[s] = ch;
		}
		scanf("%s", s);
		int len = strlen(s);
		string str = "";
		for(int i = 0; i < len; i++) {
			if(s[i] >= '0' && s[i] <= '9')str += f[s[i] - '0'];
			else if(s[i] >= 'A' && s[i] <= 'Z')str += f[s[i] - 'A' + 10];
			else str += f[s[i] - 'a' + 10];
		}
		string sttr = "";
		string tmp;
		len = str.size();
		for(int i = 0; i + 8 < len; i += 9) {
			int cnt = 0; tmp = "";
			for(int j = 0; j <= 7; j++) {
				if(str[i + j] == '1')cnt++;
				tmp += str[i + j];
			}
			cnt %= 2;
			if(cnt ^ (str[i + 8] - '0') == 0)continue;
			sttr += tmp;
		}
		//cout << sttr << endl;
		len = sttr.size();
		tmp = "";
		for(int i = 0; i < len; i++) {
			tmp += sttr[i];
			if(vis[tmp])s[++tot] = vis[tmp], tmp = "";
			if(tot == m)break;
		}
		for(int i = 1; i <= m; i++)printf("%c", s[i]); printf("\n");
	}
	return 0;
}

K - 打表,找规律

【题目大意】
给你一个n要你找到一个最大的x满足x的每一个字串(不要求连续)都是质数或者是1。
通过打表可发现最大的一个满足条件的为317,且满足条件的数不多。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <stack>
using namespace std;
#define debug(x) std::cerr << #x << " = " << (x) << std::endl
typedef pair<int, int> PII;
typedef long long LL;
typedef unsigned long long ULL;

char s[105];
int vis[1005];

int main(){
	int len,pos,T;
	for(int i = 1;i <= 999;i++) vis[i] = 0;
	vis[2] = 2;vis[3] = 3;vis[5] = 5;vis[7] = 7;
	vis[11] = 11;vis[13] = 13;vis[17] = 17;vis[23] = 23;vis[31] = 31;
	vis[37] = 37;vis[53] = 53;vis[71] = 71;vis[73] = 73;
	vis[113] = 113;vis[131] = 131;vis[137] = 137;vis[173] = 173;
	vis[311] = 311;vis[317] = 317;
	for(int i = 2;i <= 999;i++) vis[i] = max(vis[i],vis[i - 1]);
	while(~scanf("%d",&T)){
		for(int cas = 1;cas <= T;cas++){
			scanf("%s",s + 1);
			len = strlen(s + 1);
			if(len >= 4) pos = 317;
			else{
				pos = 0;
				for(int i = 1;i <= len;i++){
					pos *= 10;
					pos += (int)(s[i] - '0');
				}
			}
			printf("Case #%d: ",cas);
			printf("%d\n",vis[pos]);
		}
	}
    return 0;
}
posted @ 2018-09-09 19:58 呵呵!!! 阅读( ...) 评论( ...) 编辑 收藏
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值