BJOI2014 2014.8.13

今天怒垫一底。要好好总结。


题目:

第一题:

有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
      
      

using namespace std;

const int MAXN = 1000005;

long double Total[MAXN];
int T,N,M,X[MAXN],Inc[MAXN][25],Y[MAXN];

bool cmp(double a,double b) {return a > b;}

void Merge(int *a,int *b,int *c)
{
	int l = 1,r = 1,tot = 0;
	while ((l <= b[0]) && (r <= c[0]) && (tot + 1 <= T))
	if (b[l] < c[r]) a[++ tot] = b[l ++]; else if (b[l] > c[r]) a[++ tot] = c[r ++]; else
	a[++ tot] = b[l ++],r ++;
	while (l <= b[0] && (tot + 1 <= T)) a[++ tot] = b[l ++];
	while (r <= c[0] && (tot + 1 <= T)) a[++ tot] = c[r ++];
	a[0] = tot;
}

int main()
{
	srand((unsigned)time(0));
	scanf("%d %d", &N, &M);
	for(int i = M + 1;i <= N;i ++) scanf("%d %d",&X[i],&Y[i]);
	T = 20; 
	int C = 10000000;
	int Time = C / N;
	for(int i = 1;i <= Time;i ++)
	{
		for(int j = 1;j <= M;j ++) Inc[j][1] = rand() + 1,Inc[j][0] = 1;
		for(int j = M + 1;j <= N;j ++)
		{
			Merge(Inc[j],Inc[X[j]],Inc[Y[j]]);
			int val = (Inc[j][0] < T) ? Inc[j][0] : (Inc[j][0] * ((double)RAND_MAX / (Inc[j][Inc[j][0]] + 1)));
			Total[j] += val;
		}
	}
	for(int i = M + 1;i <= N;i ++) printf("%d\n", (int)(Total[i] / Time + 0.001));
}

     
     
    
    
   
   

第二题

#include
   
   
    
    
#include
    
    
     
     
#include
     
     
      
      
#include
      
      
       
       
#include
       
        using namespace std; typedef pair 
        
          P; const int MAXN = 200005; struct Node { int add,value; }T[MAXN * 4]; bool bz[MAXN]; int Next[MAXN],To[MAXN],tot; int Link[MAXN][3],Final[MAXN],Pre[MAXN],Ord[MAXN],H[MAXN],Fa[MAXN][20],Son[MAXN],N,M,Q,cnt; int Stack[MAXN]; void read(int &x) { char c; while (c = getchar(),c < '0' || c > '9'); x = c - 48; while (c = getchar(),c >= '0' && c <= '9') x = x * 10 + c - 48; } void link(int x,int y) {To[++ tot] = y,Next[tot] = Final[x],Final[x] = tot;} void Get_Son(int Now) { Son[Now] = 1;bz[Now] = 1; for(int i = Final[Now];i;i = Next[i]) if (!bz[To[i]]) Fa[To[i]][0] = Now,Get_Son(To[i]),Son[Now] += Son[To[i]]; } void Get_Height(int Now,int Fa,int Pre) { Ord[Now] = ++ cnt,H[Now] = Fa; int h = -1; for(int i = Final[Now];i;i = Next[i]) if ((To[i] != Pre) && ((h == -1) || (Son[To[i]] > Son[h]))) h = To[i]; if (h == -1) return; Get_Height(h,Fa,Now); for(int i = Final[Now];i;i = Next[i]) if ((To[i] != Pre) && To[i] != h) Get_Height(To[i],To[i],Now); } int get(int a) {return Stack[a] == a ? a : Stack[a] = get(Stack[a]);} int To_Search(int y) { int v = get(y); for(int j = 17;j + 1;j --) if (v == get(Fa[y][j])) y = Fa[y][j]; return y; } void Build(int l,int r,int jd) { if (l == r) T[jd].value = 1; else { int mid = (l + r) / 2; Build(l,mid,jd * 2),Build(mid + 1,r,jd * 2 + 1); } } void Label(int jd,int ad) {T[jd].value += ad,T[jd].add += ad;} int Find(int l,int r,int p,int jd) { if (l == r) return T[jd].value; else { int mid = (l + r) / 2; if (T[jd].add) Label(jd * 2,T[jd].add),Label(jd * 2 + 1,T[jd].add),T[jd].add = 0; if (p <= mid) return Find(l,mid,p,jd * 2); else return Find(mid + 1,r,p,jd * 2 + 1); } } void Change(int l,int r,int x,int y,int v,int jd) { if (l == x && r == y) T[jd].value += v,T[jd].add += v; else { int mid = (l + r) / 2; if (T[jd].add) Label(jd * 2,T[jd].add),Label(jd * 2 + 1,T[jd].add),T[jd].add = 0; if (y <= mid) Change(l,mid,x,y,v,jd * 2); else if (x > mid) Change(mid + 1,r,x,y,v,jd * 2 + 1); else Change(l,mid,x,mid,v,jd * 2),Change(mid + 1,r,mid + 1,y,v,jd * 2 + 1); } } int main() { read(N),read(M); for(int i = 1;i <= N;i ++) Stack[i] = i; for(int i = 1;i <= M;i ++) { char c;while (c = getchar(),c < 'A' || c > 'Z'); int x,y;read(x),read(y); Link[i][1] = x,Link[i][2] = y; if (c == 'A') Link[i][0] = 1,link(x,y),link(y,x); } for(int i = 1;i <= N;i ++) if (!bz[i]) { Get_Son(i); Get_Height(i,i,0); } for(int i = 1;i <= 17;i ++) for(int j = 1;j <= N;j ++) Fa[j][i] = Fa[Fa[j][i - 1]][i - 1]; Build(1,N,1); for(int i = 1;i <= M;i ++) { int x = Link[i][1],y = Link[i][2]; if (Fa[x][0] == y) swap(x,y); if (Link[i][0]) { Stack[get(y)] = get(x); int Up = Ord[To_Search(y)],Vnow = Find(1,N,Ord[y],1); y = Fa[y][0]; while (Ord[y] >= Up) { if (Ord[H[y]] >= Up) Change(1,N,Ord[H[y]],Ord[y],Vnow,1); else Change(1,N,Up,Ord[y],Vnow,1); y = Fa[H[y]][0]; } } else { int Up = Ord[To_Search(y)],Tmp = Find(1,N,Up,1); int Tmp1 = Find(1,N,Ord[y],1); printf("%lld\n",(long long)(Tmp - Tmp1) * Tmp1); } } return 0; } 
        
      
      
     
     
    
    
   
   

第三题
#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);
}

    
    
   
   


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值