2010年福州赛区regional 解题报告

仅有8个题的解题报告,如果大牛您看不到想看的题解,抱歉了……

模拟比赛的时候队伍做出了6个题,其中一个水题没看懂……囧……

A题,不会……现场赛只有sjtu3个队已经bupt的一个队伍过了……

B题 无向图最小割,论文题,模板题类型

代码如下

B
#include <cmath>
#include
<cstdio>
#include
<memory.h>
#include
<algorithm>
#include
<iomanip>
#include
<iostream>
#include
<vector>
#include
<string>
#include
<queue>
using namespace std;
const int N = 500 + 3;
int n, m;
int mat[N][N];
int dist[N];
int visited[N];
int del[N]; // true表示该点已经被删掉
// 结点~n
int Stoer_Wagner(int source)
{
int minCut = 1<<30; // 无向图最小割
int tmp;
int i, t, j, k, pre;
int s = source; // 源点
memset(del, 0, sizeof(del));
for (t = 1; t < n; t++) // n - 1次Maximum Adjacency Search
{
for (i = 1; i <= n; i++)
if (!del[i])
dist[i]
= mat[s][i];
memset(visited,
0, sizeof(visited));
visited[s]
= 1;
k
= s;
for (i = 1; i <= n - t; i++) // 每次剩下n - t + 1个结点
{
tmp
= -1e9;
pre
= k;
k
= 0;
for (j = 1; j <= n; j++)
{
if (!del[j] && !visited[j] && dist[j] > tmp)
{
k
= j;
tmp
= dist[j];
}
}
if (!k) return 0; // 不连通
visited[k] = 1;
for (j = 1; j <= n; j++)
if (!del[j] && !visited[j])
dist[j]
+= mat[k][j];
}
minCut
= min(minCut, dist[k]);
del[k]
= 1; // 删除k点
// 合并k点和源点
for (i = 1; i <= n; i++)
if (!del[i] && i != pre)
{
mat[pre][i]
+= mat[k][i];
mat[i][pre]
= mat[pre][i];
}

}
return minCut;
}
int main ()
{
int u, v, w, i,source;
while (scanf("%d%d%d", &n, &m,&source) == 3)
{
if(n + m + source == 0)
break;
memset(mat,
0, sizeof(mat));
while (m--)
{
scanf(
"%d%d%d", &u, &v, &w);
if (u == v) continue;
mat[u ][v ]
+= w;
mat[v ][u ]
+= w;
}
printf(
"%d\n", Stoer_Wagner(source));
}
return 0;
}

C题,几何题,不会。但是貌似先判断是否可以映射到水平面上,然后对映射的点求凸包即可?反正不是我等弱菜会的……

D题,DP,数拆成二进制从高到底一位一位DP。如果N非常小的话,我们可以考虑从高位到低位,以此状态压缩做,状态s的第i位为1表示第i个数字达到上限(TC某场的解法)。但是此题N为50不可状态压缩。重点是想到如果有一个数的该位为1更改一次且为第一个更改的数字后其他的数字无论怎么如何都能得到想要的k。如果第i为为1且更改了不止一次,那么有(1 << i)种方法,如果不改有 (mi & ((1 << i) - 1)) + 1种方法。

比赛的时候当成高斯消元了交给队友一直没有解决,my fault……

更详细的解题报告参考这里(我就是在这里学的)http://hi.baidu.com/%D2%BB%CE%BB%C1%E3/blog/item/603ee5b68bf283e630add144.html

代码如下

D
#include <iostream>
#include
<cstring>
#include
<cstdio>
using namespace std;
#define REP(i, l, n) for(int i = l;i < int(n);++i)
#define CC(i, v) memset(i, v, sizeof(i))
#define FOREACH(con, i) for(__typeof(con.begin()) i = con.begin();i != con.end();++i)
typedef
long long LL;

const int N = 60;
const int MAXBIT = 32;
const int MOD = 1000000003;

LL dp[N][N], m[N];
int bitK[MAXBIT], bit[N][MAXBIT];

void toBit(int x, int *bit)
{
REP(i,
0, MAXBIT)
bit[i]
= (x >> (MAXBIT - i - 1)) & 1;
}

void mod(LL& x, LL y)
{
x
= (x + y % MOD) % MOD;
}
LL method(
int x, int i, bool chg)
{
i
= MAXBIT - 1 - i;
if(chg) return 1 << i;
return (x & ((1 << i) - 1)) + 1;
}
LL DP(
int n)
{
LL ans
= 0;
REP(i,
0, MAXBIT)
{
CC(dp,
0);
dp[
0][0] = 1;
int now = 0;
REP(j,
0, n)
{
now
^= bit[j + 1][i];
for(int t = 0; t <= j; t++)
{
mod(dp[j
+ 1][t], dp[j][t] * method(m[j + 1], i, 0));
if(!bit[j + 1][i]) continue;
if(t == 0)
mod(dp[j
+ 1][t + 1], dp[j][t]);
else
mod(dp[j
+ 1][t + 1], dp[j][t] * method(m[j + 1], i, 1));
}
}
REP(j,
1, n + 1)
if((now ^ bitK[i] ^ (j & 1)) == 0)
mod(ans, dp[n][j]);
if(now ^ bitK[i]) return ans;
}
mod(ans,
1);
return ans;
}
int main()
{
int n, k;
while(scanf("%d %d",&n, &k) == 2 && (n || k))
{
toBit(k, bitK);
for(int i = 1; i <= n; i++)
{
scanf(
"%lld", &m[i]);
toBit(m[i], bit[i]);
}
printf(
"%lld\n", DP(n));
}
return 0;
}

E题 费马点,模拟退火精度要搞的非常离谱才行……要YY结论……讨厌这样的题目

队友做的,没有代码

F题,赤裸自动机,解压后500W,开始看小了,囧……构建自动机,正串反串各走一次

写麻烦了,其实拿原来的传得正反串构建一次自动机貌似就可以了……

F
  1 /*
2 * =====================================================================================
3 *
4 * Filename: sim.cpp
5 *
6 * Description:
7 *
8 * Version: 1.0
9 * Created: 08/22/2011 12:48:32 PM
10 * Revision: none
11 * Compiler: gcc
12 *
13 * Author: ronaflx
14 * Company: hit-ACM-Group
15 *
16 * =====================================================================================
17 */
18
19 #include <cstdlib>
20 #include <cctype>
21 #include <cstring>
22 #include <iterator>
23 #include <limits>
24 #include <cstdio>
25 #include <cmath>
26 #include <ctime>
27 #include <climits>
28 #include <algorithm>
29 #include <functional>
30 #include <numeric>
31 #include <vector>
32 #include <map>
33 #include <set>
34 #include <queue>
35 #include <stack>
36 #include <bitset>
37 #include <list>
38 #include <string>
39 #include <iostream>
40 #include <sstream>
41 #include <fstream>
42 #include <iomanip>
43 #include <stdexcept>
44 #include <utility>
45 #include <cassert>
46 #include <complex>
47 using namespace std;
48
49 #define LEFT(i) ((i) << 1)
50 #define RIGHT(i) (((i) << 1) | 1)
51 #define MID(i) ((l[i] + r[i]) >> 1)
52 #define CC(i, v) memset(i, v, sizeof(i))
53 #define REP(i, l, n) for(int i = l;i < int(n);++i)
54 #define FOREACH(con, i) for(__typeof(con.begin()) i = con.begin();i != con.end();++i)
55 const int N = 301;
56 bool chk[N];
57 class ACAutoMachine
58 {
59 public:
60 void initialize()
61 {
62 pp = pool;
63 root = newNode();
64 }
65 void insert(char *str, int id)
66 {
67 node *now = root;
68 while (*str)
69 {
70 int idx = (*str) - 'A';
71 if (now->next[idx] == NULL)
72 now->next[idx] = newNode();
73 now = now->next[idx];
74 str++;
75 }
76 now->id = id;
77 now->count++;
78 }
79 void build()
80 {
81 head = tail = 0;
82 q[tail++] = root;
83 while (head != tail)
84 {
85 node *beg = q[head++], *tmp = NULL;
86 for (int i = 0; i < KIND; i++)
87 {
88 if (beg->next[i] != NULL)
89 {
90 if (beg == root)
91 beg->next[i]->fail = root;
92 else
93 {
94 tmp = beg->fail;
95 while (tmp)
96 {
97 if (tmp->next[i] != NULL)
98 {
99 beg->next[i]->fail = tmp->next[i];
100 break;
101 }
102 tmp = tmp->fail;
103 }
104 if (tmp == NULL)
105 beg->next[i]->fail = root;
106 }
107 q[tail++] = beg->next[i];
108 }
109 }
110 }
111 }
112 int query(char * str)
113 {
114 int cnt = 0, idx;
115 node * p = root;
116 while (*str)
117 {
118 idx = (*str) - 'A';
119 while (p->next[idx] == NULL && p != root)
120 p = p->fail;
121 p = p->next[idx];
122 p = (p == NULL) ? root : p;
123 node * tmp = p;
124 while (tmp != root && tmp->count != -1)
125 {
126 if(tmp->id != -1) chk[tmp->id] = true;
127 cnt += tmp->count;
128 tmp->count = -1;
129 tmp = tmp->fail;
130 }
131 str++;
132 }
133 return cnt;
134 }
135 private:
136 const static int KIND = 26;
137 const static int MAXNODE = 500000;
138
139 struct node
140 {
141 node *fail;
142 node * next[KIND];
143 int count, id;
144 } pool[MAXNODE], *pp, *root;
145 node *q[MAXNODE];
146 int head,tail;
147 node *newNode()
148 {
149 pp->fail = NULL;
150 pp->count = 0;
151 pp->id = -1;
152 memset(pp->next, 0, sizeof (pp->next));
153 return pp++;
154 }
155 };
156 ACAutoMachine AC;
157 const int LEN = 6100000;
158 char keywords[N][2000];
159 char text[LEN], Stext[LEN];
160
161 int main()
162 {
163 int n, t;
164 scanf("%d", &t);
165 while(t--)
166 {
167 scanf("%d", &n);
168 for(int i = 0;i < n;i++)
169 scanf("%s", keywords[i]);
170 memset(chk, 0, sizeof(chk));
171 scanf("%s", text);
172 int len = strlen(text), k = 0;
173 for(int i = 0;i < len;i++)
174 {
175 if(text[i] == '[')
176 {
177 i++;
178 int now = 0;
179 for(;isdigit(text[i]);i++)
180 now = now * 10 + text[i] - '0';
181 memset(Stext + k, text[i++], sizeof(char) * now);
182 k += now;
183 }
184 else
185 Stext[k++] = text[i];
186 }
187 Stext[k] = 0;
188 AC.initialize();
189 for(int i = 0;i < n;i++) AC.insert(keywords[i], i);
190 AC.build();
191 AC.query(Stext);
192 reverse(Stext, Stext + strlen(Stext));
193 AC.initialize();
194 for(int i = 0;i < n;i++) AC.insert(keywords[i], i);
195 AC.build();
196 AC.query(Stext);
197 int cnt = 0;
198 for(int i = 0;i < n;i++)
199 cnt += chk[i];
200 printf("%d\n", cnt);
201 }
202 return 0;
203 }

G题,是个水的拓扑序DP,比赛的时候都没读懂题。

状态DP,第一个物品1pound 最多买多少钱,按照原图的反图建模,拓扑DP即可

ans = sum(dp[i] * weight[i]);

G
/*
* =====================================================================================
*
* Filename: dp.cpp
*
* Description:
*
* Version: 1.0
* Created: 2011/8/13 17:48:08
* Revision: none
* Compiler: gcc
*
* Author: ronaflx
* Company: hit-acm-group
*
* =====================================================================================
*/
#include
<iostream>
#include
<cstring>
#include
<cstdio>
#include
<cmath>
#include
<algorithm>
using namespace std;
#define REP(i, l, n) for(int i = l;i < n;i++)
#define FOREACH(con, i) for(__typeof(con.begin()) i = con.begin();i != con.end();++i)
#define CC(con, i) memset(con, i, sizeof(con))
const int E = 600000;
const int V = 10100;
struct edge
{
int v;
edge
*nxt;
double val;
}pool[E],
*g[V], *pp;
double price[V], weight[V], dp[V];
int topo[V], chk[V];
void initialize()
{
CC(g,
0);
pp
= pool;
CC(price,
0);
CC(weight,
0);
CC(chk,
0);
CC(dp,
0);
}
void addedge(int u, int v, double val)
{
pp
->v = v;
pp
->nxt = g[u];
pp
->val = val;
g[u]
= pp++;
}
int a[V], cnt;
double b[V];

void dfs(int v)
{
chk[v]
= true;
for(edge *i = g[v];i != NULL; i = i->nxt)
if(!chk[i->v]) dfs(i->v);
topo[cnt
++] = v;
}
int main()
{
int n, m;
while(scanf("%d", &n) == 1 && n)
{
initialize();
REP(i,
0, n) scanf("%lf %lf", &price[i], &weight[i]);
scanf(
"%d", &m);
REP(i,
0, m)
{
int k;
scanf(
"%d", &k);
for(int j = 0;j < k;j++)
{
scanf(
"%d", &a[j]);
a[j]
--;
if(j == k - 1) break;
scanf(
"%lf", &b[j + 1]);
}
for(int j = 1;j < k;j++)
addedge(a[j], a[j
- 1], b[j]);
}
cnt
= 0;
REP(i,
0, n) if(!chk[i]) dfs(i);
reverse(topo, topo
+ n);
double ans = 0;
for(int i = 0;i < n;i++)
{
int v = topo[i];
dp[v]
= max(dp[v], price[v]);
ans
+= dp[v] * weight[v];
for(edge *j = g[v];j != NULL;j = j->nxt)
dp[j
->v] = max(dp[j->v], dp[v] * j->val);
}
printf(
"%.2lf\n", ans);
}
return 0;
}

H题,贪心就可以,注意区间是开区间,所以要枚举0.5的情况,我是网络流做的,反正是过了……感谢出题人……

代码就不献丑了

I题很好的一个线段树DP,容易想到100 * 5000 * 5000的解法

主要思路就是把绝对值展开,然后对于mat[i][j]的所有左边按-f[i- 1][j] - j插入到线段树中,j++的扫描。

右边同理按绝对值展开的做。j--的扫描复杂度100 * 5000 * log(200000);建树的时候不要偷懒……把区间的大小先算出来,不然TLE的

代码如下:

I
#include <iostream>
#include
<cstring>
#include
<cstdio>
#include
<cstring>
#include
<cmath>
#include
<cstdlib>
#include
<string>
#include
<algorithm>
#include
<vector>
#include
<map>
#include
<set>
#define cc(x,y) memset((x),(y),sizeof((x)))
#define rep(x,y) for(int (x) = 0;(x) < (y) ; (x) ++)
using namespace std;
int my_scanf()
{
char a;
int num;
while(a=getchar(),!(a>='0'&& a<='9'));
num
= a-'0';
while ((a = getchar())!=' ' && a!='\n')
num
= num*10 + a-'0';
return num;
}
const int N = 110;
const int M = 5010;
const int D = 110000;
const int inf = 0x3f3f3f3f;
int cost[N][M];
int f[N][M];
int dp[N][M];
const int Len = 240000;
struct Seg
{
int left,right,minv;
} seg[Len
* 4];
void build(int num,int left,int right)
{
int mid = (left + right) / 2;
seg[num].left
= left;
seg[num].right
= right;
if(left == right)
{
seg[num].minv
= inf;
return;
}
build(num
*2,left,mid);
build(num
*2 + 1,mid + 1,right);
seg[num].minv
= min(seg[2*num].minv,seg[2*num + 1].minv);
}
void insert(int num,int pos,int val)
{
int mid = (seg[num].left + seg[num].right) /2;
if(seg[num].left == seg[num].right)
{
seg[num].minv
= min(val,seg[num].minv);
return;
}
if(pos <=mid)
insert(num
*2,pos,val);
else
insert(num
*2 + 1,pos,val);
seg[num].minv
= min(seg[2*num].minv,seg[2*num + 1].minv);
}
int query(int num,int left,int right)
{
int mid = (seg[num].left + seg[num].right) /2;
if(seg[num].left == left && seg[num].right == right)
{
return seg[num].minv;
}
if(right <=mid)
return query(num*2,left,right);
else if(left > mid)
return query(num*2 + 1,left,right);
else
return min(query(num*2,left,mid),query(num*2+1,mid + 1,right));
}
int main()
{
int n,m;
while(scanf("%d %d",&n,&m) == 2)
{
if( n + m == 0)
break;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
cost[i][j]
= my_scanf();
//scanf("%d",&cost[i][j]);
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
f[i][j]
= my_scanf();
//scanf("%d",&f[i][j]);
memset(dp,0x3f,sizeof(dp));
for(int i=1; i<=m; i++)
dp[
1][i] = cost[1][i];
for(int i=2; i<=n; i++)
{
int minn = inf, maxn = -inf;
for(int j = 1;j <= m;j++)
{
minn
= min(minn, -f[i - 1][j] - j + D);
minn
= min(minn, f[i][j] - j + D);
maxn
= max(maxn, -f[i - 1][j] - j + D);
maxn
= max(maxn, f[i][j] - j + D);
}
build(
1, minn, maxn);
for(int j=1; j<=m; j++)
{
insert(
1,-f[i-1][j] - j + D,dp[i-1][j]);
int k = query(1,minn,f[i][j] - j + D);
dp[i][j]
= min(dp[i][j] ,k + cost[i][j]);
}
minn
= inf, maxn = -inf;
for(int j = m;j >= 1;j--)
{
minn
= min(minn, j - f[i - 1][j] + D);
minn
= min(minn, f[i][j] + j + D);
maxn
= max(maxn, j - f[i - 1][j] + D);
maxn
= max(maxn, f[i][j] + j + D);
}
build(
1, minn, maxn);
for(int j=m; j>=1; j--)
{
insert(
1,j - f[i-1][j] + D, dp[i-1][j]);

int k = query(1, minn,f[i][j] + j + D);
dp[i][j]
= min(dp[i][j] , k + cost[i][j]);
}
}
printf(
"%d\n",*min_element(dp[n] + 1,dp[n] + m + 1));
}
return 0;
}

J题暴力枚举,ABCDE分别为0-9的数字,注意事项不能又前导0,不能数字对应的字母唯一,除法不能又余数,这里WA了一次……

代码如下

J
/*
* =====================================================================================
*
* Filename: sim.cpp
*
* Description:
*
* Version: 1.0
* Created: 08/22/2011 12:48:32 PM
* Revision: none
* Compiler: gcc
*
* Author: ronaflx
* Company: hit-ACM-Group
*
* =====================================================================================
*/

#include
<cstdlib>
#include
<cctype>
#include
<cstring>
#include
<iterator>
#include
<limits>
#include
<cstdio>
#include
<cmath>
#include
<ctime>
#include
<climits>
#include
<algorithm>
#include
<functional>
#include
<numeric>
#include
<vector>
#include
<map>
#include
<set>
#include
<queue>
#include
<stack>
#include
<bitset>
#include
<list>
#include
<string>
#include
<iostream>
#include
<sstream>
#include
<fstream>
#include
<iomanip>
#include
<stdexcept>
#include
<utility>
#include
<cassert>
#include
<complex>
using namespace std;

#define LEFT(i) ((i) << 1)
#define RIGHT(i) (((i) << 1) | 1)
#define MID(i) ((l[i] + r[i]) >> 1)
#define CC(i, v) memset(i, v, sizeof(i))
#define REP(i, l, n) for(int i = l;i < int(n);++i)
#define FOREACH(con, i) for(__typeof(con.begin()) i = con.begin();i != con.end();++i)

typedef
long long LL;
const int N = 5;
int chk[N], hash[10], tonu[N];
LL tonumber(
const string& str)
{
if(str.size() > 1 && tonu[str[0] - 'A'] == 0) return -1;
LL ans
= 0;
FOREACH(str, i)
ans
= ans * 10 + tonu[*i - 'A'];
return ans;
}

LL cnt;
string str1, str2, str3;
void dfs(int n)
{
if(n == 6)
{
LL a
= tonumber(str1);
LL b
= tonumber(str2);
LL c
= tonumber(str3);
if(a == -1 || b == -1 || c == -1) return;
if(a + b == c) cnt++;
if(a * b == c) cnt++;
if(a - b == c) cnt++;
if(b != 0 && a % b == 0 && a / b == c) cnt++;
return;
}
if(!chk[n])
{
dfs(n
+ 1);
return;
}
for(int i = 0;i < 10;i++)
{
if(!hash[i])
{
hash[i]
= true;
tonu[n]
= i;
dfs(n
+ 1);
hash[i]
= false;
tonu[n]
= -1;
}
}
}

int main()
{
int t;
scanf(
"%d", &t);
while(t--)
{
cin
>> str1 >> str2 >> str3;
CC(hash,
0);CC(chk, 0);CC(tonu, -1);
FOREACH(str1, i) chk[
*i - 'A'] = true;
FOREACH(str2, i) chk[
*i - 'A'] = true;
FOREACH(str3, i) chk[
*i - 'A'] = true;
cnt
= 0;
dfs(
0);
cout
<< cnt << endl;
}
return 0;
}

  小节:做难题的能力还不够呀……金牌梦是越来越遥远了,1A率低迷,本场比赛1A的题目1道……

这套题比的相对很好,但是现场赛我们也就是4-5题的水平……

无向图最小割,如果没想到怎么办……费马点实现不知道结论怎么办……也许G题再仔细读一下题能搞出来。如果最后线段树都没调出来怎么办?

梦想还是很遥远……还有整整1个月,加油!!

你可能感兴趣的:(reg)