2016"百度之星" - 初赛(Astar Round2A)
Posted PoorLitt1eThin9
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2016"百度之星" - 初赛(Astar Round2A)相关的知识,希望对你有一定的参考价值。
All X[HDU5690]
找循环节,注意可能是混循环。
#include <stdio.h>
#include <string.h>
bool f[10005];
int a[10005], l, r;
int main() {
int n, x, k, c, t;
long long m;
scanf("%d", &n);
for (int q = 0; q < n; q++) {
scanf("%d%lld%d%d", &x, &m, &k, &c);
memset(f, false, sizeof(f));
l = r = 0;
a[r++] = x % k;
f[a[0]] = true;
for (int i = 0;; i++) {
t = (a[r - 1] * 10 + x) % k;
if (f[t]) {
for (int j = 0; j < r; j++) {
if (a[j] == t) {
l = j;
break;
}
}
break;
}
f[t] = true;
a[r++] = t;
}
m -= l;
printf("Case #%d:\n%s\n", q + 1, c == a[m % (r - l) == 0 ? r - 1 : l + (int)(m % (r - l)) - 1] ? "Yes" : "No");
}
return 0;
}
Sitting in Line[HDU5691]
状态压缩,状态i表示整数的选取状态,其二进制形式的10分别表示对应的数字是否选取。dp[i][j]表示在状态i下最后放置的数字是第j个数字能得到的最大值。
#include<bitset>
#include<map>
#include<vector>
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<stack>
#include<queue>
#include<set>
#define inf 0x3f3f3f3f
#define mem(a,x) memset(a,x,sizeof(a))
#define F first
#define S second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
inline int in() {
int res = 0;
char c;
int f = 1;
while ((c = getchar()) < ‘0‘ || c > ‘9‘)if (c == ‘-‘) {
f = -1;
}
while (c >= ‘0‘ && c <= ‘9‘) {
res = res * 10 + c - ‘0‘, c = getchar();
}
return res * f;
}
const int N = 100010, MOD = 1e9 + 7;
int a[16];
int dp[1 << 16][16], must[16];
int one[1 << 16];
int main() {
for (int i = 0; i < 1 << 16; i++) {
for (int j = i; j; j >>= 1) {
one[i] += j & 1;
}
}
int T = in(), ca = 1;
while (T--) {
int n = in();
mem(must, -1);
int x, y;
for (int i = 0; i < n; i++) {
scanf("%d%d", &x, &y);
a[i] = x;
if (y != -1) {
must[y] = i; //y位置必须是ai值
}
}
int all = 1 << n;
for (int i = 0; i < all; i++) {
for (int j = 0; j < n; j++) {
dp[i][j] = -inf;
}
}
//0没法转移, 特殊处理
if (must[0] == -1) {
for (int i = 0; i < n; i++) {
dp[1 << i][i] = 0;
}
} else {
dp[1 << must[0]][must[0]] = 0;
}
for (int i = 1; i < all; i++) {
int num = one[i];
if (must[num] == -1) {
for (int j = 0; j < n; j++) { //枚举结束位置
if (i & 1 << j && dp[i][j] != -inf) {
for (int k = 0; k < n; k++) { //枚举下一次加的值
if (!(i & 1 << k)) {
dp[i | 1 << k][k] = max(dp[i | 1 << k][k], dp[i][j] + a[k] * a[j]);
}
}
}
}
} else {
int k = must[num]; //下一个位置必须是a[k]
if (i & 1 << k) {
continue; //如果a[k]已经使用, 不更新
}
for (int j = 0; j < n; j++) {
if (i & 1 << j && dp[i][j] != -inf) {
dp[i | 1 << k][k] = max(dp[i | 1 << k][k], dp[i][j] + a[j] * a[k]);
}
}
}
}
int ans = -inf;
for (int j = 0; j < n; j++) {
ans = max(ans, dp[all - 1][j]);
}
printf("Case #%d:\n%d\n", ca++, ans);
}
return 0;
}
Snacks[HDU5692]
首先先弄成以0为根的一棵树,每个节点的权值为根节点到当前节点的价值之和。查询某个节点时,其价值之和的最大值应为该节点对应的子树中最大的权值。更新某个节点的权值时,则该节点对应的子树上每一个节点都会产生同样的变化。因此,可以通过dfs产生树的先根遍历,然后用线段树来进行更新和查询操作。
错的原因:线段树写错了,lazy处理的不对。
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <string.h>
template <class T> class SegmentTree {
public:
T dat, lazy;
int leftBorder, rightBorder, mid;
SegmentTree * leftSon, * rightSon;
T(* lazyfunc)(T, T);
T(* mergefunc)(T, T);
SegmentTree() {
leftBorder = rightBorder = -1;
leftSon = rightSon = NULL;
}
void pushdown();
void pushup();
void Build(T *, int, int, T(*)(T, T), T(*)(T, T));
void Modify(int, int, T);
T Query(int, int);
void Free();
};
template<class T> void SegmentTree<T>::pushdown() {
if (lazy && leftBorder != rightBorder) {
leftSon->dat = lazyfunc(leftSon->dat, lazy);
rightSon->dat = lazyfunc(rightSon->dat, lazy);
leftSon->lazy = lazyfunc(leftSon->lazy, lazy);
rightSon->lazy = lazyfunc(rightSon->lazy, lazy);
}
lazy = (T)0;
}
template<class T> void SegmentTree<T>::pushup() {
dat = mergefunc(leftSon->dat, rightSon->dat);
}
template<class T> void SegmentTree<T>::Build(T * S, int l, int r, T(* lfunc)(T, T), T(* mfunc)(T, T)) {
if (l > r) {
return;
}
lazy = (T)0;
leftBorder = l;
rightBorder = r;
mid = (leftBorder + rightBorder) >> 1;
lazyfunc = lfunc;
mergefunc = mfunc;
if (l == r) {
dat = S[l];
return;
}
leftSon = new SegmentTree;
leftSon->Build(S, l, mid, lfunc, mfunc);
rightSon = new SegmentTree;
rightSon->Build(S, mid + 1, r, lfunc, mfunc);
pushup();
}
template<class T> void SegmentTree<T>::Modify(int l, int r, T NewDat) {
if (l > r || l < leftBorder || rightBorder < r) {
return;
}
if (leftBorder == l && rightBorder == r) {
dat = lazyfunc(dat, NewDat);
lazy = lazyfunc(lazy, NewDat);
return;
}
pushdown();
if (r <= mid) {
leftSon->Modify(l, r, NewDat);
} else if (mid < l) {
rightSon->Modify(l, r, NewDat);
} else {
leftSon->Modify(l, mid, NewDat);
rightSon->Modify(mid + 1, r, NewDat);
}
pushup();
}
template<class T> T SegmentTree<T>::Query(int l, int r) {
if (l > r || l < leftBorder || rightBorder < r) {
return dat;
}
pushdown();
if (l == leftBorder && r == rightBorder) {
return dat;
}
if (r <= mid) {
return leftSon->Query(l, r);
} else if (mid < l) {
return rightSon->Query(l, r);
} else {
return mergefunc(leftSon->Query(l, mid), rightSon->Query(mid + 1, r));
}
}
template<class T> void SegmentTree<T>::Free() {
if (leftSon != NULL) {
leftSon->Free();
}
if (rightSon != NULL) {
rightSon->Free();
}
delete leftSon;
delete rightSon;
}
SegmentTree<long long> st;
long long a[100005], b[100005], w[100005];
int M, N;
int head[100005], l[100005], r[100005];
struct EDGE {
int v, nex;
} edge[200005];
void addEdge(int a, int b) {
edge[M].v = b;
edge[M].nex = head[a];
head[a] = M++;
edge[M].v = a;
edge[M].nex = head[b];
head[b] = M++;
}
long long lazyfunc(long long x, long long y) {
return x + y;
}
long long mergefunc(long long x, long long y) {
return x > y ? x : y;
}
void dfs1(int f, int x) {
int e = head[x];
while (e != -1) {
int v = edge[e].v;
if (f != v) {
a[v] = a[x] + w[v];
dfs1(x, v);
}
e = edge[e].nex;
}
}
void dfs2(int f, int x) {
l[x] = N;
b[N++] = a[x];
int e = head[x];
while (e != -1) {
int v = edge[e].v;
if (f != v) {
dfs2(x, v);
}
e = edge[e].nex;
}
r[x] = N - 1;
}
int main() {
int n, m, t, o, u, v;
scanf("%d", &t);
for (int q = 1; q <= t; q++) {
scanf("%d%d", &n, &m);
M = 0;
memset(head, -1, sizeof(head));
for (int i = 1; i < n; i++) {
scanf("%d%d", &u, &v);
addEdge(u, v);
}
for (int i = 0; i < n; i++) {
scanf("%lld", &w[i]);
}
a[0] = w[0];
dfs1(-1, 0);
N = 0;
dfs2(-1, 0);
st.Free();
st.Build(b, 0, N - 1, lazyfunc, mergefunc);
printf("Case #%d:\n", q);
for (int i = 0; i < m; i++) {
scanf("%d", &o);
if (o == 0) {
scanf("%d%d", &u, &v);
st.Modify(l[u], r[u], v - w[u]);
w[u] = v;
} else {
scanf("%d", &u);
printf("%lld\n", st.Query(l[u], r[u]));
}
}
}
return 0;
}
D game[HDU5693]
首先只用考虑长度为2和3的等差数列就可以了,因为更长的等差数列都可以拆成几个2和3的小等差数列。
用f[i][j]表示从i到j的字段能否全部消除,能全部消除的情况总共有3种模式
[#####][#####]
O[#####]O
O[#####]O[#####]O
其他都可以用这三种形式表示出来。
dp[i]表示从1到i最多消去的个数。dp[i] = max(dp[i],dp[j - 1] + i - j + 1)
错的原因:①在转移前,dp[i]的值应先设为dp[i-1],因为第i个数字可能并不在任何一个等差数列中。②二维数组f[i][j]写成了f[i,j],编译器不会报错,因为i,j也是个表达式,二维数组用一维的下标访问也没有问题,但算出来的结果肯定不对了。③使用位运算的时候要注意加括号,比如x&1==0,如果x<0,结果就会出错。感觉原因是这样的:-x&1==0,程序先判断了(1==0)得到一个false,再由这个false去&前面的-x。所以应该写成(x&1)==0。
#include <stdio.h>
#include <string.h>
#include <map>
std::map<long long, bool> mp;
long long a[305], d;
int dp[305];
bool f[305][305];
int main() {
int n, m, t;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
mp.clear();
for (int i = 1; i <= m; i++) {
scanf("%lld", &d);
mp[d] = true;
}
memset(f, false, sizeof(f));
for (int i = 1; i <= n; i++) {
f[i][i - 1] = true;
}
for (int j = 2; j <= n; j++) {
for (int i = 1; i + j - 1 <= n; i++) {
for (int k = i + 1; k < i + j - 1; k++) {
if (f[i][k] && f[k + 1][i + j - 1]) {
f[i][i + j - 1] = true;
}
}
if (mp[a[i + j - 1] - a[i]] && f[i + 1][i + j - 2]) {
f[i][i + j - 1] = true;
}
if (((a[i + j - 1] - a[i]) & 1) == 0 && mp[(a[i + j - 1] - a[i]) >> 1]) {
for (int k = i + 1; k < i + j - 1; k++) {
if ((a[k] << 1) == (a[i] + a[i + j - 1]) && f[i + 1][k - 1] && f[k + 1][i + j - 2]) {
f[i][i + j - 1] = true;
}
}
}
}
}
memset(dp, 0, sizeof(dp));
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1];
for (int j = 1; j < i; j++) {
if (f[j][i] && dp[j - 1] + i - j + 1 > dp[i]) {
dp[i] = dp[j - 1] + i - j + 1;
}
}
}
printf("%d\n", dp[n]);
}
return 0;
}
BD String[HDU5694]
解法肯定是b(r)-b(l-1),只要能写出来b(x)函数就行。观察他的字符串构造,中间有一个b,右边的串是左边翻转加回文构成的,回文不改变bd的数量,翻转使bd数量交换,所以左右两边b和d的数量加起来是相等的,中间多一个b。另外S(n)的长度为2*S(n-1)+1=2^n-1。所以当x=2^n-1时,直接得到b(x)=(x/2)+1。对于一般情况,找到x长度介于S(n)和S(n+1)之间的n。只要用S(n+1)里的b的数量减去从第x+1位到S(n+1)末尾中的b的数量即可,而这一部分与字符串的开头同样长度的部分的关系是db数量交换,可以递归求解。
#include <stdio.h>
#include <string.h>
long long b(long long x) {
if (x == 0) {
return 0;
}
long long l = 1;
while (l < x) {
l = l * 2 + 1;
}
if (l == x) {
return l / 2 + 1;
}
return (l / 2 + 1) - (l - x - b(l - x));
}
int main() {
int t;
long long l, r;
scanf("%d", &t);
while (t--) {
scanf("%lld%lld", &l, &r);
printf("%lld\n", b(r) - b(l - 1));
}
return 0;
}
Gym Class[HDU5695]
拓扑排序,每次输出可行的同学中id最高的那个。
错的原因:每个同学要等到所有嫌弃他的同学都过了之后才能变成可行的。
#include <stdio.h>
#include <string.h>
#include <queue>
std::priority_queue<int> q;
int f[100005];
int M;
int head[100005];
struct EDGE {
int v, nex;
} edge[100005];
void addEdge(int a, int b) {
edge[M].v = b;
edge[M].nex = head[a];
head[a] = M++;
}
int main() {
int n, m, t, a, b;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
memset(f, 0, sizeof(f));
M = 0;
memset(head, -1, sizeof(head));
for (int i = 1; i <= m; i++) {
scanf("%d%d", &a, &b);
addEdge(a, b);
f[b]++;
}
while (!q.empty()) {
q.pop();
}
for (int i = 1; i <= n; i++) {
if (f[i] == 0) {
q.push(i);
}
}
long long ans = 0;
int min = 0x7FFFFFFF;
while (!q.empty()) {
int x = q.top();
q.pop();
if (x < min) {
min = x;
}
ans += min;
for (int e = head[x]; e != -1; e = edge[e].nex) {
f[edge[e].v]--;
if (f[edge[e].v] == 0) {
q.push(edge[e].v);
}
}
}
printf("%lld\n", ans);
}
return 0;
}
以上是关于2016"百度之星" - 初赛(Astar Round2A)的主要内容,如果未能解决你的问题,请参考以下文章
2016"百度之星" - 初赛(Astar Round2B)
2016"百度之星" - 初赛(Astar Round2A)
2016"百度之星" - 初赛(Astar Round2B)
2016"百度之星" - 初赛(Astar Round2A)