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互质且1≤bi≤n。
【思路】
通过打表可以发现an=n∗(n+1),所以先可以求出所有的小于n的数,通过推导1∗2+2∗3+3∗4+⋯+n∗(n+1)=
1∗1+2∗2+3∗3+⋯+n∗n+1+2+3+⋯+n=
n∗(n+1)∗(2∗n+1)/6+n∗(n+1)/2=
n∗(n+1)∗(n+2)/3
可算出所有小于n的数的和。
然后要去除所有与m不互质的数,那么可以筛出所有m的不同质因子,然后通过减掉某个因子的倍数,通过容斥,奇加偶减,即奇数个质因子就减,偶数个就加,因为对于一个因子为x,即求所有小于m的x倍数的项,求法如下
x∗(x+1)+⋯假设有len项,即可化为
x∗x∗len∗(len+1)∗(2∗len+2)/6+x∗len∗(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;
}