今天怒垫一底。要好好总结。
题目:
第一题:
有M个初始题目。我们有另外的N个题目,每个题目都由两个题目Xi,Yi混合而成,我们要知道对于每个题目,他最终由多少个初始题目组成(重复的只记一个)
N <= 1000000,M <= 100000
特殊的地方,我们允许你的程序有一定的误差,如果其中你有95%以上的行的答案和正确答案的误差不超过25%,那么你就可以得到分数。所谓误差不超过25%,即,如果正确答案是X,那么你的答案在[0.8X,1.25X]这个闭区间内。
第二题:
我们有N个点,Q个操作。
对于A操作,读入x,y,表示x,y之间连一条边。(数据保证始终为一个树)
对于Q操作,读入x,y,询问有多少条简单路径经过x,y这条边(保证x,y这条边曾经出现)
N <= 100000,Q <= 100000
第三题:
我们给定一个图,每个顶点都有一个字符(0~9,(,),+,-,*,/),给定一个路径长度,在起点与终点不限定的情况下,让你求出有多少条路径,其顶点组成一个合法表达式。(注意,你要处理各种情况,比如数字不能有多余的前导0,减号只有前面没有运算符或数字的时候才可以当成负号,括号可以任意添加(但不能有空括号),0可以做除数(我们只考虑文法而不考虑语意),加号不能当正号。)
比赛时的情况:
第一题一眼看上去没有什么思路,发现60%分(N <= 100000,M <= 10000)的可以直接用压位碾过去,就直接不想了。
第二题,一看就是数据结构题。我一开始考虑离线,开线段树维护连了区间的边的联通情况。想了很久都没有解决并查集的区间加法(事实上这个做不了).
于是我就直接跳进了在线的大坑,一直在想LCT怎么做。
难点在于连边的时候我们需要旋根,儿子个数就会发生改变!!
我是这样想的,考虑旋根对答案的影响只有对他到根的节点。于是我们就可以分情况考虑(事实上非常的繁琐,我反正是没调出来)
第三题,一眼看上去觉得不会。然后就不去想了。。。没有看到各种各样的限制条件。-_- -_- -_-其实这道题水的不得鸟。。。。
讲一下题解吧。一样先按难度排序(3,2,1)
第三题,注意到我们需要注意的只是他各种各样的非法条件。而我们其实能列举出来。
我们用*代替运算符,1代替非0的数字。
非法条件其实就只是:
* * *) (*[且* 不为-], )1 1( 01 () )(
然后我们直接DP就好了。。。。
第二题:
难点其实就在于我们加边的时候树的形态不确定。
那就先把整棵树建出来!!!!!!
建好树后,我们再作操作。
对于Add(x,y)
我们可以找出靠下的那个点,设为y
我们可以倍增出当前连到的深度最小的点u
那么对于x->u的点,深度比他大的点的个数就+上y的值!树链剖分就可以暴力过。
第一题:
果然是涉及面广(偏)、考察深入(怪)、思维强度大(难)的阿米巴和小强出的题。
来看一下我们60分的做法。
压位。
我们考虑怎么来优化。
我们对于M个初始题目random一个0~rand_max的值。
设一个值T,表示我们对于每个题目只保留T个初始题目。
对于合并操作(X+Y->Z):
我们将X,Y保留的值归并排序(并且顺便去重)
我们Z要保留的值就是前T小的值。
设P为第T小的值。
然后对于Z的答案就是T * rand_max / P.
我们对于一个点随机多次取答案的平均值就能尽量减小误差了。
合(da)理(dan)证(cai)明(ce)
因为是random出来的值,所以初始题目的值是在0~rand_max随机分布的。
而我们在K个数中取到最小的T个数的概率为T/K
这个概率就等于P/rand_max(放大与缩小?)
暴露的问题:
今天暴露的问题还是十分严重的。
对于打不出来的程序要及时取舍。注意分析时间。
题目必须要读完。不能一眼过。要每一道题都分析,剖析,化繁为简。
贴代码。
第一题
#include
#include
#include
#include
第二题
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 40,Mo = 1000000007;
int link[MAXN][MAXN],N,M,K;
char s[MAXN];
long long f[MAXN][MAXN][MAXN][2];
bool Num(int i)
{
return (s[i] >= '0' && s[i] <= '9');
}
bool Br(int i)
{
return (s[i] == '*' || s[i] == '/' || s[i] == '+' || s[i] == '-');
}
int main()
{
scanf("%d %d %d", &N, &M, &K);
scanf("%s", s + 1);
for(int i = 1;i <= M;i ++)
{
int x,y;scanf("%d %d", &x, &y),link[x][y] = link[y][x] = 1;
}
for(int i = 1;i <= N;i ++)
{
if (Num(i)) f[1][i][0][s[i] == '0'] = 1;
if (s[i] == '(') f[1][i][1][0] = 1;
if (s[i] == '-') f[1][i][0][0] = 1;
}
for(int step = 1;step < K;step ++)
for(int i = 1;i <= N;i ++)
for(int br = 0;br <= N / 2;br ++)
for(int d = 0;d < 2;d ++)
if (f[step][i][br][d])
for(int j = 1;j <= N;j ++)
if (link[i][j])
{
if (Br(i) && s[j] == ')') continue;
if (s[i] == '(' && (Br(j) && s[j] != '-')) continue;
if (s[i] == ')' && Num(j)) continue;
if (Num(i) && s[j] == '(') continue;
if (d && Num(j)) continue;
if (s[i] == '(' && s[j] == ')') continue;
if (s[i] == ')' && s[j] == '(') continue;
if (!br && s[j] == ')') continue;
if (Br(i) && Br(j)) continue;
if (Num(j))
{
if (s[j] != '0' || Num(i)) (f[step + 1][j][br][0] += f[step][i][br][d]) %= Mo; else
(f[step + 1][j][br][1] += f[step][i][br][d]) %= Mo;
} else
if (s[j] == '(') (f[step + 1][j][br + 1][0] += f[step][i][br][d]) %= Mo; else
if (s[j] == ')') (f[step + 1][j][br - 1][0] += f[step][i][br][d]) %= Mo; else
if (Br(j))(f[step + 1][j][br][0] += f[step][i][br][d]) %= Mo;
}
long long Ans = 0;
for(int i = 1;i <= N;i ++)
if (Num(i) || s[i] == ')')
for(int d = 0;d < 2;d ++) (Ans += f[K][i][0][d]) %= Mo;
printf("%lld\n",Ans);
}