您的位置:首页 > 大数据 > 人工智能

2015 Multi-University Training Contest 1

2016-07-26 19:13 381 查看
感觉题目很棒,贴上留作记录。

A

定义 f(L,R)为区间[L,R]里面合法数的个数。一个数a[i]合法意味着L <= i <= R且区间里面不存在j使得i!=j&& a[i]%a[j]==0。

题意:问你所有区间合法数的总数。

思路:一开始一直YYO(n)的神算法,发现实在不可搞,最后直接暴力找每个数的最近因子区间就过了。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 10;
const int MOD = 1e9 + 7;
int a[MAXN], cnt[MAXN];
vector<int> G[MAXN], P[MAXN];
void Solve(int n) {
for(int i = 1; i * i <= n; i++) {
if(n % i == 0) {
P
.push_back(i);
if(i * i != n) {
P
.push_back(n / i);
}
}
}
}
int main()
{
for(int i = 1; i <= 10000; i++) {
P[i].clear(); Solve(i);
}
int n;
while(scanf("%d", &n) != EOF) {
for(int i = 1; i <= 10000; i++) G[i].clear(), cnt[i] = 0;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
G[a[i]].push_back(i);
cnt[a[i]]++;
}
LL ans = 0;
for(int i = 1; i <= 10000; i++) {
for(int j = 0; j < cnt[i]; j++) {
int p1 = G[i][j]; int l = 0, r = n + 1;
for(int x = 0; x < P[i].size(); x++) {
int v = P[i][x];
for(int y = 0; y < cnt[v]; y++) {
int p2 = G[v][y];
if(p2 < p1) {
l = max(p2, l);
}
else if(p2 > p1) {
r = min(p2, r);
}
}
}
l++; r--;
ans = (ans + 1LL * (r - p1) * (p1 - l) + (r - p1) + (p1 - l)) % MOD;
//cout << ans << endl;
}
}
ans += n; ans %= MOD;
printf("%lld\n", ans);
}
return 0;
}


B

题意:问有多少个区间的最大值-最小值< k。

RMQ + 二分就好了。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
#define CLR(a, b) memset(a, (b), sizeof(a))
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 10;
const int MAXM = 1e6 + 10;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
int a[MAXN];
int dp[2][MAXN][20];
void RMQ_init(int n) {
for(int i = 1; i <= n; i++) {
dp[0][i][0] = dp[1][i][0] = a[i];
}
for(int j = 1; (1 << j) <= n; j++) {
for(int i = 1; i + (1 << j) - 1 <= n; i++) {
dp[0][i][j] = max(dp[0][i][j-1], dp[0][i + (1 << (j - 1))][j-1]);
dp[1][i][j] = min(dp[1][i][j-1], dp[1][i + (1 << (j - 1))][j-1]);
}
}
}
int Query(int L, int R, int op) {
int k = 0;
while((1 << (k + 1) <= R - L + 1)) k++;
return op == 0 ? max(dp[0][L][k], dp[0][R - (1 << k) + 1][k]) :
min(dp[1][L][k], dp[1][R - (1 << k) + 1][k]);
}
int main()
{
int t; scanf("%d", &t);
while(t--) {
int n, k; scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
RMQ_init(n); LL ans = 0;
for(int i = 1; i <= n; i++) {
int l = i, r = n, pos;
while(r >= l) {
int mid = l + r >> 1;
if(Query(i, mid, 0) - Query(i, mid, 1) < k) {
pos = mid;
l = mid + 1;
}
else {
r = mid - 1;
}
}
ans += pos - i + 1;
}
printf("%lld\n", ans);
}
return 0;
}


F

题意:给定一棵树和m条链以及每条链的价值,你需要选若干条点不相交的链使得这些链的价值总和最大。

思路:dp[i]表示以i节点为根的子树可以得到的最大价值,sum[i]=∑dp[j] 其中j是i的孩子。

有两种决策:

一、i节点不选链或者不在链上,那样直接就是dp[i]=sum[i];

二、i节点为某条链k的lca,那样dp[i]=v[k]+∑sum[j]−∑dp[j] 其中j是k链上的点。

预处理LCA,用树链剖分维护就可以啦。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#define CLR(a, b) memset(a, (b), sizeof(a))
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 10;
const int MAXM = 1e6 + 10;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
struct Edge {
int from, to, next;
};
Edge edge[MAXN<<1];
int head[MAXN], edgenum;
int vs[MAXN<<1];
int depth[MAXN<<1];
int id[MAXN];
int dfs_clock;
void init() { CLR(head, -1); edgenum = 0; }
void addEdge(int u, int v) {
Edge E = {u, v, head[u]};
edge[edgenum] = E;
head[u] = edgenum++;
}
void DFS(int u, int fa, int d) {
id[u] = dfs_clock; vs[dfs_clock] = u; depth[dfs_clock++] = d;
for(int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if(v == fa) continue;
DFS(v, u, d + 1);
vs[dfs_clock] = u;
depth[dfs_clock++] = d;
}
}
void find_depth() {
dfs_clock = 1; CLR(vs, 0); CLR(id, 0); CLR(depth, 0);
DFS(1, -1, 0);
}
int dp[MAXN<<1][30];
void RMQ_init(int NN) {
for(int i = 1; i <= NN; i++) dp[i][0] = i;
for(int j = 1; (1<<j) <= NN; j++) {
for(int i = 1; i + (1<<j) - 1 <= NN; i++) {
int a = dp[i][j-1];
int b = dp[i + (1<<(j-1))][j-1];
if(depth[a] < depth[b]) dp[i][j] = a;
else dp[i][j] = b;
}
}
}
int query(int L, int R) {
int k = 0;
while((1<<(k+1)) <= R - L + 1) k++;
int a = dp[L][k], b = dp[R - (1 << k) + 1][k];
if(depth[a] < depth[b]) return a;
else return b;
}
int LCA(int u, int v) {
int x = id[u], y = id[v];
if(x > y) return vs[query(y, x)];
else return vs[query(x, y)];
}
int chain[MAXN][4];
vector<int> G[MAXN];
struct Tree {
int l, r, sum1, sum2;
};
Tree tree[MAXN<<2];
void PushUp(int o, int op) {
if(op == 1) {
tree[o].sum1 = tree[ll].sum1 + tree[rr].sum1;
}
else {
tree[o].sum2 = tree[ll].sum2 + tree[rr].sum2;
}
}
void Build(int o, int l, int r) {
tree[o].l = l; tree[o].r = r;
tree[o].sum1 = tree[o].sum2 = 0;
if(l == r) {
return ;
}
int mid = (l + r) >> 1;
Build(ll, l, mid); Build(rr, mid+1, r);
}
void Update(int o, int pos, int v, int op) {
if(tree[o].l == tree[o].r) {
if(op == 1) {
tree[o].sum1 = v;
}
else {
tree[o].sum2 = v;
}
return ;
}
int mid = (tree[o].l + tree[o].r) >> 1;
if(pos <= mid) Update(ll, pos, v, op);
else Update(rr, pos, v, op);
PushUp(o, op);
}
int Query(int o, int L, int R, int op) {
if(tree[o].l == L && tree[o].r == R) {
return op == 1 ? tree[o].sum1 : tree[o].sum2;
}
int mid = (tree[o].l + tree[o].r) >> 1;
if(R <= mid) return Query(ll, L, R, op);
else if(L > mid) return Query(rr, L, R, op);
else {
return Query(ll, L, mid, op) + Query(rr, mid+1, R, op);
}
}
int son[MAXN], num[MAXN];
int top[MAXN], pos[MAXN], Id;
int dep[MAXN], pre[MAXN];
void DFS1(int u, int fa, int d) {
dep[u] = d; pre[u] = fa; num[u] = 1; son[u] = -1;
for(int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if(v == fa) continue;
DFS1(v, u, d+1);
num[u] += num[v];
if(son[u] == -1 || num[son[u]] < num[v])
son[u] = v;
}
}
void DFS2(int u, int T) {
top[u] = T; pos[u] = ++Id;
if(son[u] == -1) return ;
DFS2(son[u], T);
for(int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if(v == pre[u] || v == son[u]) continue;
DFS2(v, v);
}
}
int GetSum(int u, int v, int op) {
int f1 = top[u], f2 = top[v];
int ans = 0;
while(f1 != f2) {
if(dep[f1] < dep[f2]) {
swap(u, v); swap(f1, f2);
}
ans += Query(1, pos[f1], pos[u], op);
u = pre[f1], f1 = top[u];
}
if(dep[u] > dep[v]) swap(u, v);
ans += Query(1, pos[u], pos[v], op);
return ans;
}
int d[MAXN], s[MAXN];
void Solve(int u, int fa) {
s[u] = 0; d[u] = 0;
for(int i = head[u]; i != -1; i = edge[i].next) {
int v = edge[i].to;
if(v == fa) continue;
Solve(v, u);
s[u] += d[v];
}
Update(1, pos[u], s[u], 1);
d[u] = s[u];
for(int j = 0; j < G[u].size(); j++) {
int k = G[u][j];
d[u] = max(d[u], chain[k][2] + GetSum(chain[k][0], chain[k][1], 1) -
GetSum(chain[k][0], chain[k][1], 2));
}
Update(1, pos[u], d[u], 2);
}
int main()
{
int t; scanf("%d", &t);
while(t--) {
int n, m; scanf("%d%d", &n, &m);
init();
for(int i = 0; i < n-1; i++) {
int u, v; scanf("%d%d", &u, &v);
addEdge(u, v); addEdge(v, u);
}
DFS1(1, -1, 1); Id = 0; DFS2(1, 1); Build(1, 1, Id);
for(int i = 1; i <= n; i++) G[i].clear();
find_depth(); RMQ_init(dfs_clock - 1);
for(int i = 0; i < m; i++) {
scanf("%d%d%d", &chain[i][0], &chain[i][1], &chain[i][2]);
chain[i][3] = LCA(chain[i][0], chain[i][1]);
G[chain[i][3]].push_back(i);
}
Solve(1, -1);
printf("%d\n", d[1]);
}
return 0;
}


G

题意:问你最少需要去掉多少条边可以截断1 - n的最短路以及最多去掉多少条边最短路依然存在。

思路:就是一个最小割 + 边数最少的最短路。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#define CLR(a, b) memset(a, (b), sizeof(a))
using namespace std;
typedef long long LL;
const int MAXN = 2 * 1e3 + 10;
const int MAXM = 1e6 + 10;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
struct Edge{
int from, to, cap, flow, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
void init() {CLR(head, -1); edgenum = 0;}
void addEdge(int u, int v, int w) {
Edge E1 = {u, v, w, 0, head[u]};
edge[edgenum] = E1;
head[u] = edgenum++;
Edge E2 = {v, u, 0, 0, head[v]};
edge[edgenum] = E2;
head[v] = edgenum++;
}
bool vis[MAXN];
int dist[MAXN], cur[MAXN];
bool Bfs(int s, int t) {
queue<int> Q; Q.push(s); CLR(vis, false); CLR(dist, -1);
vis[s] = true; dist[0] = 0;
while(!Q.empty()) {
int u = Q.front(); Q.pop();
for(int i = head[u]; i != -1; i = edge[i].next) {
Edge E = edge[i];
if(!vis[E.to] && E.cap > E.flow) {
dist[E.to] = dist[u] + 1;
if(E.to == t) return true;
vis[E.to] = true;
Q.push(E.to);
}
}
}
return false;
}
int DFS(int x, int a, int t) {
if(x == t || a == 0) return a;
int flow = 0, f;
for(int &i = cur[x]; i != -1; i = edge[i].next) {
Edge &E = edge[i];
if(dist[E.to] == dist[x] + 1 && (f = DFS(E.to, min(E.cap-E.flow, a), t)) > 0) {
edge[i].flow += f;
edge[i^1].flow -= f;
flow += f;
a -= f;
if(a == 0) break;
}
}
return flow;
}
int Maxflow(int s, int t) {
int flow = 0;
while(Bfs(s, t)) {
memcpy(cur, head, sizeof(head));
flow += DFS(s, INF, t);
}
return flow;
}
struct Edge2 {
int from, to, val, next;
};
Edge2 edge2[MAXM];
int head2[MAXN], edgenum2;
void addEdge2(int u, int v, int w) {
Edge2 E1 = {u, v, w, head2[u]};
edge2[edgenum2] = E1;
head2[u] = edgenum2++;
}
int num[MAXN];
void SPFA(int s, int t) {
CLR(vis, false); CLR(dist, INF); CLR(num, INF);
queue<int> Q; Q.push(s); vis[s] = true; dist[s] = 0; num[s] = 0;
while(!Q.empty()) {
int u = Q.front(); Q.pop(); vis[u] = false;
for(int i = head2[u]; i != -1; i = edge2[i].next) {
Edge2 E = edge2[i];
if(dist[E.to] > dist[u] + E.val) {
dist[E.to] = dist[u] + E.val;
num[E.to] = num[u] + 1;
if(!vis[E.to]) {
vis[E.to] = true;
Q.push(E.to);
}
}
else if(dist[E.to] == dist[u] + E.val) {
num[E.to] = min(num[E.to], num[u] + 1);
}
}
}
}
int main()
{
int n, m;
while(scanf("%d%d", &n, &m) != EOF) {
CLR(head2, -1); edgenum2 = 0;
for(int i = 0; i < m; i++) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addEdge2(u, v, w);
addEdge2(v, u, w);
}
SPFA(1, n); init();
for(int i = 1; i <= n; i++) {
for(int j = head2[i]; j != -1; j = edge2[j].next) {
int v = edge2[j].to;
if(dist[v] == dist[i] + edge2[j].val) {
addEdge(i, v, 1);
}
}
}
printf("%d %d\n", Maxflow(1, n), m - num
);
}
return 0;
}


J

题意:去掉2,3,4,5,6,7,…的r次方其中2 <= r <= 63得到Y序列。问你Y序列里面第n个数是多少。

思路:先容斥,发现可以很快得到前n个数里面去掉多少个数。这样我们可以直接二分,但是二分会T,那就用迭代了。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
#define CLR(a, b) memset(a, (b), sizeof(a))
#define ll o<<1
#define rr o<<1|1
using namespace std;
typedef long long LL;
const int MAXN = 1e5 + 10;
const int MAXM = 1e6 + 10;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
bool vis[80];
void getp() {
for(int i = 2; i <= 70; i++) {
if(vis[i]) continue;
for(int j = 2 * i; j <= 70; j += i) {
vis[j] = true;
}
}
}
int p[30], k;
int q[1000000], top;
LL Count(LL n) {
LL ans = n;
for(int i = 1; i < top; i++) {
LL res = (LL)pow(n + 0.5, 1.0 / abs(q[i])) - 1;
ans += q[i] / abs(q[i]) * res;
}
return ans - 1;
}
int main()
{
getp();
int t; scanf("%d", &t);
while(t--) {
LL n; int m; scanf("%lld%d", &n, &m);
k = 0;
for(int i = 2; i <= m; i++) {
if(!vis[i]) {
p[k++] = i;
}
}
top = 0; q[top++] = 1;
for(int i = 0; i < k; i++) {
int T = top;
for(int j = 0; j < T; j++) {
q[top++] = q[j] * p[i] * (-1);
if(abs(q[top-1]) > 62) {
top--;
}
}
}
LL ans = n;
while(1) {
LL num = Count(ans);
if (num == n)   break;
ans += n - num;
}
printf("%lld\n", ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: