zoukankan      html  css  js  c++  java
  • BZOJ Lydsy5月月赛 ADG题解

    题目链接

    BZOJ5月月赛

    题解

    好弱啊QAQ只写出三题

    A

    判断多干个数乘积是否是某个数的倍数有很多方法,比较常用的是取模,但这里并不适用,因为模数不定
    会发现数都比较小,所以我们可以考虑分解质因子,查找一下区间各个质因子数是否符合要求
    用主席树维护即可
    由于(10^5)以内不同质因子数最多的也就是(6)个,预处理一下质因子,可以看做一个常数
    复杂度是(O(nsqrt{n} + nlogn))

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define mp(a,b) make_pair<int,int>(a,b)
    #define cls(s) memset(s,0,sizeof(s))
    #define cp pair<int,int>
    #define LL long long int
    using namespace std;
    const int maxn = 100005,maxm = 10000005,N = 100000,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    int fac[maxn][20],num[maxn][20],fi[maxn],M;
    int p[maxn],pi,isn[maxn];
    void init(){
    	for (int i = 2; i <= N; i++){
    		if (!isn[i]) p[++pi] = i;
    		for (int j = 1; j <= pi && i * p[j] <= N; j++){
    			isn[i * p[j]] = true;
    			if (i % p[j] == 0) break;
    		}
    	}
    	for (int i = 2; i <= N; i++){
    		int x = i,t;
    		for (int j = 1; j <= pi && p[j] * p[j] <= x; j++){
    			if (x % p[j] == 0){
    				t = ++fi[i];
    				fac[i][t] = j;
    				while (x % p[j] == 0) num[i][t]++,x /= p[j];
    			}
    		}
    		if (x - 1){
    			fac[i][++fi[i]] = lower_bound(p + 1,p + 1 + pi,x) - p;
    			num[i][fi[i]] = 1;
    		}
    	}
    	M = pi;
    }
    int sum[maxm],ls[maxm],rs[maxm],rt[maxn],A[maxn],n,m,cnt;
    void add(int& u,int v,int l,int r,int pos,int w){
    	sum[u = ++cnt] = sum[v] + w;
    	ls[u] = ls[v]; rs[u] = rs[v];
    	if (l == r) return;
    	int mid = l + r >> 1;
    	if (mid >= pos) add(ls[u],ls[v],l,mid,pos,w);
    	else add(rs[u],rs[v],mid + 1,r,pos,w);
    }
    int query(int u,int v,int l,int r,int pos){
    	if (!u) return 0;
    	if (l == r) return sum[u] - sum[v];
    	int mid = l + r >> 1;
    	if (mid >= pos) return query(ls[u],ls[v],l,mid,pos);
    	return query(rs[u],rs[v],mid + 1,r,pos);
    }
    int main(){
    	init();
    	int T = read(),x,l,r,flag;
    	while (T--){
    		cnt = 0;
    		n = read(); m = read();
    		REP(i,n){
    			x = A[i] = read(); rt[i] = rt[i - 1];
    			for (int j = 1; j <= fi[x]; j++)
    				add(rt[i],rt[i],1,M,fac[x][j],num[x][j]);
    		}
    		while (m--){
    			l = read(); r = read(); x = read(); flag = true;
    			for (int i = 1; i <= fi[x]; i++)
    				if (query(rt[r],rt[l - 1],1,M,fac[x][i]) < num[x][i]){
    					flag = false; break;
    				}
    			if (!flag) puts("No");
    			else puts("Yes");
    		}
    	}
    	return 0;
    }
    
    

    D

    D是树上两点间的询问,考虑使用树上权值主席树
    由于只考虑奇偶性,所以我们覆给每个位置(1)(2),修改就翻转一下
    如何查询?
    拿出(u)(v)所对应的两棵树,我们需要快速合并树上信息得出第一个偶数位置
    容易知道当两个位置奇偶性不同时相加起来才是奇数,所以我们只需要找到两棵树第一个相同的位置
    权值不同不好比较,那我们就再维护一个值,与原来的值相反,这样转化成了查找第一个不同的位置
    我们只需快速比较两个区间是否相同,使用(hash)即可
    两个树实际上对应两条到根的链,相交的部分除了(lca)处都应减去,由于只考虑奇偶性,所以对答案没有影响,所以只用把(lca)处的值修改一下即可进行比较
    复杂度(O(nlogn)),还要优化一下常数,比如只建一次树,(RMQ)(lca)

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define Redge(u) for (register int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (register int i = 1; i <= (n); i++)
    #define mp(a,b) make_pair<int,int>(a,b)
    #define cls(s) memset(s,0,sizeof(s))
    #define cp pair<int,int>
    #define LL long long int
    #define ULL unsigned long long int
    using namespace std;
    const int maxn = 200005,maxm = 13000005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    int h[maxn],ne = 1;
    struct EDGE{int to,nxt;}ed[maxn << 1];
    inline void build(int u,int v){
    	ed[++ne] = (EDGE){v,h[u]}; h[u] = ne;
    	ed[++ne] = (EDGE){u,h[v]}; h[v] = ne;
    }
    ULL pw[maxn],val[maxm],vv[maxm];
    int A[maxn],fa[maxn],dep[maxn],dfn[maxn],mn[maxn << 1][19],Log[maxn << 1],bin[50],tot;
    int rt[maxn],ls[maxm],rs[maxm],cnt,n,m,N = 200001,typ,pos,V;
    inline void upd(int u,int l,int r){
    	val[u] = val[ls[u]] * pw[r - (l + r >> 1)] + val[rs[u]];
    	vv[u] = vv[ls[u]] * pw[r - (l + r >> 1)] + vv[rs[u]];
    }
    void build(int& u,int l,int r){
    	u = ++cnt;
    	if (l == r){val[u] = 1; vv[u] = 2; return;}
    	int mid = l + r >> 1;
    	build(ls[u],l,mid);
    	build(rs[u],mid + 1,r);
    	upd(u,l,r);
    }
    void modify(int& u,int v,int l,int r){
    	u = ++cnt; ls[u] = ls[v]; rs[u] = rs[v];
    	if (l == r){val[u] = val[v] == 1 ? 2 : 1; vv[u] = vv[v] == 1 ? 2 : 1; return;}
    	int mid = l + r >> 1;
    	if (mid >= pos) modify(ls[u],ls[v],l,mid);
    	else modify(rs[u],rs[v],mid + 1,r);
    	upd(u,l,r);
    }
    int query(int u,int v,int l,int r){
    	if (l == r) return l;
    	int mid = l + r >> 1;
    	ULL t1 = val[ls[u]],t2 = vv[ls[v]];
    	if (pos >= l && pos <= mid){
    		if (V == 1) t1 += pw[mid - pos];
    		else t1 -= pw[mid - pos];
    	}
    	if (t1 != t2) return query(ls[u],ls[v],l,mid);
    	return query(rs[u],rs[v],mid + 1,r);
    }
    inline int ask(int u){
    	int l = 1,r = N,mid;
    	while (l < r){
    		mid = l + r >> 1;
    		if (mid >= pos) r = mid,u = ls[u];
    		else l = mid + 1,u = rs[u];
    	}
    	return val[u];
    }
    void dfs(int u){
    	pos = A[u]; mn[++tot][0] = u; dfn[u] = tot;
    	modify(rt[u],rt[fa[u]],1,N);
    	Redge(u) if ((to = ed[k].to) != fa[u]){
    		fa[to] = u; dep[to] = dep[u] + 1;
    		dfs(to);
    		mn[++tot][0] = u;
    	}
    }
    inline int lca(int u,int v){
    	int l = dfn[u],r = dfn[v];
    	if (l > r) swap(l,r);
    	int t = Log[r - l + 1];
    	return dep[mn[l][t]] < dep[mn[r - bin[t] + 1][t]] ? mn[l][t] : mn[r - bin[t] + 1][t];
    }
    /*void print(int u,int l,int r){
        if (l == r){
            printf("%lld ",val[u]);
            return;
        }
        int mid = l + r >> 1;
        print(ls[u],l,mid);
        if (6 > mid)print(rs[u],mid + 1,r);
    }*/
    int main(){
    	bin[0] = 1; for (int i = 1; i <= 25; i++) bin[i] = bin[i - 1] << 1;
    	Log[0] = -1; for (register int i = 1; i < (maxn << 1); i++) Log[i] = Log[i >> 1] + 1;
    	pw[0] = 1; for (register int i = 1; i < maxn; i++) pw[i] = pw[i - 1] * 5;
    	build(rt[0],1,N);
    	int tmp = cnt;
    	int T = read();
    	while (T--){
    		n = read(); m = read();
    		REP(i,n) h[i] = 0;
    		tot = 0; cnt = tmp; ne = 1;
    		REP(i,n) A[i] = read();
    		for (register int i = 1; i < n; i++) build(read(),read());
    		dfs(1);
    		for (register int j = 1; j <= 18; j++)
    			for (register int i = 1; i <= tot; i++){
    				if (i + bin[j] - 1 > tot) break;
    				mn[i][j] = dep[mn[i][j - 1]] < dep[mn[i + bin[j - 1]][j - 1]] ? mn[i][j - 1] : mn[i + bin[j - 1]][j - 1];
    			}
    		register int u,v,o;
    		while (m--){
    			u = read(); v = read(); o = lca(u,v);
    			//printf("lca(%d,%d) = %d
    ",u,v,o);
    			//print(rt[u],1,N); puts("");
    			//print(rt[v],1,N); puts("");
    			pos = A[o];
    			V = ask(rt[u]);
    			printf("%d
    ",query(rt[u],rt[v],1,N));
    		}
    	}
    	return 0;
    }
    
    

    G

    一眼看过去以为是裸的后缀数组,仔细一看原来串那么长
    但是思想是相通的
    先讲讲如果是一般的串怎么做
    我们只需要写出一个(O(1))(cmp)函数即可进行排序
    我们知道比较两个字符串实质是比较第一个不同的字符【串长不够特殊考虑】
    所以我们只需要(O(1))求出两个位置的(lcp)即可
    这是后缀数组拿手的地方

    然而换到了这道题,我们同样可以通过求(lcp)来进行比较,但不是用后缀数组
    我们先压缩一下,保证任意相邻两段字符不同
    我们发现(m)很小,可以(O(m^2))dp求出任意两段开头位置开始的(lcp),记为(f[i][j])
    我们对于任意两个串,对于段开头前面的部分我们可以直接判断是否相同,如果它们字符都不一样当然不同,如果字符一样就看到该段末尾的距离,由于相邻段字符是不一样的,如果距离不同,那么到段的结尾后自然就不同了,如果距离还相同,那么就可以加上(f[i][j]),就是(lcp)
    综上可以(O(nlogn))预处理串的位置,(O(m^2))dp,(O(nlog^2n))排序
    总复杂度(O(nlog^2n + m^2))

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define mp(a,b) make_pair<int,int>(a,b)
    #define cls(s) memset(s,0,sizeof(s))
    #define cp pair<int,int>
    #define LL long long int
    using namespace std;
    const int maxn = 300005,maxm = 2005,INF = 1000000000;
    inline LL read(){
    	LL out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    	return out * flag;
    }
    char s[maxm];
    int n,m,id[maxn];
    LL num[maxm],ll[maxn],len[maxn],l[maxn],lcp[maxm][maxm];
    bool cmp(const int& x,const int& y){
    	LL u = l[x],v = l[y],L;
    	if (s[u] != s[v]) return s[u] < s[v];
    	else {
    		LL l1 = num[u] - ll[x] + 1,l2 = num[v] - ll[y] + 1;
    		if (l1 != l2) L = min(l1,l2);
    		else L = l1 + lcp[u + 1][v + 1];
    	}
    	L = min(L,min(len[x],len[y]));
    	//printf("%d-%d  lcp = %lld
    ",x,y,L);
    	if (L == len[x] && L == len[y]) return x < y;
    	if (L == len[x]) return true;
    	if (L == len[y]) return false;
    	int p1 = lower_bound(num + 1,num + 1 + m,ll[x] + L) - num;
    	int p2 = lower_bound(num + 1,num + 1 + m,ll[y] + L) - num;
    	return s[p1] < s[p2];
    }
    int main(){
    	int T = read(); char c;
    	LL L,R;
    	while (T--){
    		n = read(); m = read();
    		for (int i = 1; i <= m; i++){
    			c = getchar(); while (!isalpha(c)) c = getchar();
    			num[i] = read();
    			if (i > 1 && c == s[i - 1]){
    				num[i - 1] += num[i];
    				i--; m--;
    			}
    			else s[i] = c;
    		}
    		REP(i,m) lcp[i][m + 1] = lcp[m + 1][i] = 0;
    		for (int i = m; i; i--)
    			for (int j = m; j; j--){
    				if (s[i] != s[j]) lcp[i][j] = 0;
    				else if (num[i] == num[j]) lcp[i][j] = num[i] + lcp[i + 1][j + 1];
    				else lcp[i][j] = min(num[i],num[j]);
    				//printf("lcp(%d,%d) = %lld
    ",i,j,lcp[i][j]);
    			}
    		for (int i = 1; i <= m; i++) num[i] += num[i - 1];
    		for (int i = 1; i <= n; i++){
    			L = read(); R = read(); id[i] = i;
    			len[i] = R - L + 1;
    			l[i] = lower_bound(num + 1,num + 1 + m,L) - num;
    			ll[i] = L;
    		}
    		sort(id + 1,id + 1 + n,cmp);
    		for (int i = 1; i <= n; i++){
    			printf("%d",id[i]);
    			if (i < n) putchar(' ');
    		}
    		if (T) puts("");
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    如何让spring mvc web应用启动时就执行特定处理
    关联查询left join中on 和where 的区别
    c语言中'.'与'>'的区别
    申请博客园的计划
    java学习环境安装与配置
    ACM之旅之坎坷编译器
    2013年小结
    申请博客园的计划
    生命在于运动
    委托和事件
  • 原文地址:https://www.cnblogs.com/Mychael/p/9096872.html
Copyright © 2011-2022 走看看