NOIP模拟题 2016.11.9 [动态规划] [数论] [二分答案] [启发式合并] [线段树] [树链剖分]
2016-11-09 14:43
645 查看
子序列
描述
给定3 个字符串,求它们的最长公共子序列。
输入
第一行一个整数n,表示三个字符串的长度
接下来三行,每行是一个长度为n 只包含小写字母的字符串。
输出
输出最长公共子序列的长度。
输入样例
4
abac
abbc
cbca
输出样例
2
提示
30% n<=10
100% n<=120
T1:
三个串的LCS。。直接DP。。
dun
【问题描述】
定义两个素数是连续的当且仅当这两个素数之间不存在其他的素数(如 7,11 ,(23,29)。给定N,k,在不超过N的正整数中求能够分解为k个连续的素数的和的最大的那个是多少。
【输入格式】
第一行一个正整数T代表数据组数。
接下来T行每行两个正整数N,k代表一组询问。
【输出格式】
输出共T行,每行一个整数代表答案;如果找不到这样的数,输出−1。
【样例输入】
3
20 2
20 3
20 4
【样例输出】
18
15
17
【样例解释】
╭︿︿︿╮
{/ o o /}
( (oo) )
︶︶︶
【数据规模与约定】
对于20%的数据,1≤N≤100。
对于40%的数据,T=1。
对于60%的数据,所有的询问的N相等。
对于100%的数据,1≤T<2000,1≤N≤1e6。
T2:
打质数表,然后直接前缀和。
二分最大的位置使得连续k个质数适合小于等于N
发放粮食
描述
有一个村庄在闹饥荒,善良而土豪的YGH决定给他们发放救济粮,该村庄有 n 户人家,每两户人家之间只有一条路可以互相到达,即这些人家之间形成一棵树。现在 YGH 会以这样的形式给他们发放粮食,选择两户人家,然后对这两个户人家路径上的所有人家都发放一袋种类为w的救济粮。在完成一系列发放任务后,YGH 想知道每一户人家收到的粮食中数量最多的是哪一种。
输入
第一行两个数 n,q,其中 n 表示村庄共有几户人家,q 表示 YGH 一共发放了几次粮食。接下来 n-1 行,每行两个数 x y,表示编号为 x 和 y 的两户人家之间连有边。接下来 q 行,每行三个数 x y w,表示 YGH 选择了 x 到 y 的路径,对每户人家发放 1 袋种类为 w 的粮食。
输出
输出 n 行,第 i 行输出编号为 i 的人家收到的粮食中数量最多的种类号,如果有多个数量相同的粮食,输出其中最小的种类号,如果没有收到粮食,输出0
样例输入
[1]
2 4
1 2
1 1 1
1 2 2
2 2 2
2 2 1
[2]
5 3
1 2
3 1
3 4
5 3
2 3 3
1 5 2
3 3 3
样例输出
[1]
1
2
[2]
2
3
3
0
2
提示
对于 40% 的数据 n<=1000,q<=1000,1<=w<=1000
对于 100% 的数据 n<=100000,q<=100000,1<=w<=100000 1<=x,y<=n
T3:
解法一:
首先应该想到树上差分,对于u,v之间加上一个种类为c的物品,那么 应该在u,v分别+1,到达LCA时先-1以保证LCA的正确性,最后返回上层时LCA再-1以完全消除u,v的影响。
但是有很多种类不能直接倍增,空间时间都无法承受。
所以每一个节点都是一棵线段树,直接在线段树上修改。
由于涉及到两棵树的合并,应采用启发式合并,把小的线段树合并到大的线段树上,然后把小的那棵树删掉,注意内存回收,不回收您懂的QWQ,并且要记得push!!!
实际上只用开两倍线段树的空间就可以了,因为每次只有两个有节点的线段树在占用内存。。
想到这里差不多就是就可以过了吧。。
然而。。GG
倍增的时候少打了个=。。。应该是>=好的吧。。
GGGG。。。。。。。。
数组开多大这个我是真的不知道。。
32倍GG3个点,100倍才过。。。
解法二:
树链剖分,只不过不用线段树维护树链,而是用维护当前颜色最大值。
按照特定的dfs序,把所有的区间更新变成差分序列,然后在dfs序列上求前缀和。
在top+1,然后在u的下一个节点-1
描述
给定3 个字符串,求它们的最长公共子序列。
输入
第一行一个整数n,表示三个字符串的长度
接下来三行,每行是一个长度为n 只包含小写字母的字符串。
输出
输出最长公共子序列的长度。
输入样例
4
abac
abbc
cbca
输出样例
2
提示
30% n<=10
100% n<=120
T1:
三个串的LCS。。直接DP。。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smax(x,tmp) x=max((x),(tmp)) #define smin(x,tmp) x=min((x),(tmp)) #define maxx(x1,x2,x3) max(max(x1,x2),x3) #define minn(x1,x2,x3) min(min(x1,x2),x3) typedef long long LL; template <class T> inline void read(T &x) { x = 0; T flag = 1; char ch = (char)getchar(); while(ch<'0' || ch>'9') { if(ch == '-') flag = -1; ch = (char)getchar(); } while(ch>='0' && ch<='9') { x = (x<<1) + (x<<3) + ch - '0'; ch = (char)getchar(); } x *= flag; } template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); } const int INF=0x3f3f3f3f; const int maxn = 125; char X[maxn],Y[maxn],Z[maxn]; int dp[maxn][maxn][maxn]; int n; int main() { freopen("subq.in","r",stdin); freopen("subq.out","w",stdout); read(n); scanf("%s%s%s",X+1,Y+1,Z+1); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) for(int k=1;k<=n;k++) { smax(dp[i][j][k],maxx(dp[i-1][j][k],dp[i][j-1][k],dp[i][j][k-1])); if(X[i]==Y[j] && Y[j]==Z[k]) smax(dp[i][j][k],dp[i-1][j-1][k-1]+1); } printf("%d",dp ); return 0; }
dun
【问题描述】
定义两个素数是连续的当且仅当这两个素数之间不存在其他的素数(如 7,11 ,(23,29)。给定N,k,在不超过N的正整数中求能够分解为k个连续的素数的和的最大的那个是多少。
【输入格式】
第一行一个正整数T代表数据组数。
接下来T行每行两个正整数N,k代表一组询问。
【输出格式】
输出共T行,每行一个整数代表答案;如果找不到这样的数,输出−1。
【样例输入】
3
20 2
20 3
20 4
【样例输出】
18
15
17
【样例解释】
╭︿︿︿╮
{/ o o /}
( (oo) )
︶︶︶
【数据规模与约定】
对于20%的数据,1≤N≤100。
对于40%的数据,T=1。
对于60%的数据,所有的询问的N相等。
对于100%的数据,1≤T<2000,1≤N≤1e6。
T2:
打质数表,然后直接前缀和。
二分最大的位置使得连续k个质数适合小于等于N
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smax(x,tmp) x=max((x),(tmp)) #define smin(x,tmp) x=min((x),(tmp)) #define maxx(x1,x2,x3) max(max(x1,x2),x3) #define minn(x1,x2,x3) min(min(x1,x2),x3) typedef long long LL; template <class T> inline void read(T &x) { x = 0; T flag = 1; char ch = (char)getchar(); while(ch<'0' || ch>'9') { if(ch == '-') flag = -1; ch = (char)getchar(); } while(ch>='0' && ch<='9') { x = (x<<1) + (x<<3) + ch - '0'; ch = (char)getchar(); } x *= flag; } template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); } const int INF=0x3f3f3f3f; const int maxn = 1200005; const int N = 1200000; bool no[maxn]; int prime[maxn],cnt; LL sum[maxn]; void Euler() { no[1] = true; for(int i=2;i<=N;i++) { if(!no[i]) prime[++cnt] = i; int j = 1; LL to = (LL) i * prime[j]; while(j<=cnt && to<=N) { no[to] = true; if(i%prime[j] == 0) break; to = (LL) i * prime[++j]; } } for(int i=1;i<=cnt;i++) sum[i] = sum[i-1] + prime[i]; } int search(int l,int r,int n,int k) { while(l < r) { int mid = (l+r+1)>>1; if(sum[mid+k] - sum[mid] <= n) l = mid; else r = mid - 1; } return l; } int main() { freopen("dun.in","r",stdin); freopen("dun.out","w",stdout); Euler(); int T; read(T); while(T--) { int n,k; read(n); read(k); if(!k) { printf("0\n");continue; } if(k > cnt) { printf("-1"); continue; } int pos = search(0,cnt-k,n,k); if(sum[pos+k]-sum[pos]<=n) printf(AUTO,sum[pos+k]-sum[pos]); else printf("-1"); putchar('\n'); } return 0; }
发放粮食
描述
有一个村庄在闹饥荒,善良而土豪的YGH决定给他们发放救济粮,该村庄有 n 户人家,每两户人家之间只有一条路可以互相到达,即这些人家之间形成一棵树。现在 YGH 会以这样的形式给他们发放粮食,选择两户人家,然后对这两个户人家路径上的所有人家都发放一袋种类为w的救济粮。在完成一系列发放任务后,YGH 想知道每一户人家收到的粮食中数量最多的是哪一种。
输入
第一行两个数 n,q,其中 n 表示村庄共有几户人家,q 表示 YGH 一共发放了几次粮食。接下来 n-1 行,每行两个数 x y,表示编号为 x 和 y 的两户人家之间连有边。接下来 q 行,每行三个数 x y w,表示 YGH 选择了 x 到 y 的路径,对每户人家发放 1 袋种类为 w 的粮食。
输出
输出 n 行,第 i 行输出编号为 i 的人家收到的粮食中数量最多的种类号,如果有多个数量相同的粮食,输出其中最小的种类号,如果没有收到粮食,输出0
样例输入
[1]
2 4
1 2
1 1 1
1 2 2
2 2 2
2 2 1
[2]
5 3
1 2
3 1
3 4
5 3
2 3 3
1 5 2
3 3 3
样例输出
[1]
1
2
[2]
2
3
3
0
2
提示
对于 40% 的数据 n<=1000,q<=1000,1<=w<=1000
对于 100% 的数据 n<=100000,q<=100000,1<=w<=100000 1<=x,y<=n
T3:
解法一:
首先应该想到树上差分,对于u,v之间加上一个种类为c的物品,那么 应该在u,v分别+1,到达LCA时先-1以保证LCA的正确性,最后返回上层时LCA再-1以完全消除u,v的影响。
但是有很多种类不能直接倍增,空间时间都无法承受。
所以每一个节点都是一棵线段树,直接在线段树上修改。
由于涉及到两棵树的合并,应采用启发式合并,把小的线段树合并到大的线段树上,然后把小的那棵树删掉,注意内存回收,不回收您懂的QWQ,并且要记得push!!!
实际上只用开两倍线段树的空间就可以了,因为每次只有两个有节点的线段树在占用内存。。
想到这里差不多就是就可以过了吧。。
然而。。GG
倍增的时候少打了个=。。。应该是>=好的吧。。
GGGG。。。。。。。。
数组开多大这个我是真的不知道。。
32倍GG3个点,100倍才过。。。
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smax(x,tmp) x=max((x),(tmp)) #define smin(x,tmp) x=min((x),(tmp)) #define maxx(x1,x2,x3) max(max(x1,x2),x3) #define minn(x1,x2,x3) min(min(x1,x2),x3) typedef long long LL; template <class T> inline void read(T &x) { x = 0; T flag = 1; char ch = (char)getchar(); while(ch<'0' || ch>'9') { if(ch == '-') flag = -1; ch = (char)getchar(); } while(ch>='0' && ch<='9') { x = (x<<1) + (x<<3) + ch - '0'; ch = (char)getchar(); } x *= flag; } template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); } // end template const int INF=0x3f3f3f3f; const int maxn = 100005; int n,q,Root,maxw; int ans[maxn]; // original tree related struct Edge { int to,next; }edge[maxn<<1]; int head[maxn]; int maxedge; inline void addedge(int u,int v) { edge[++maxedge] = (Edge) { v,head[u] }; head[u] = maxedge; edge[++maxedge] = (Edge) { u,head[v] }; head[v] = maxedge; } // end original tree // LCA related const int maxd = 18; const int D = 17; int fa[maxn][maxd]; int depth[maxn]; void lca(int u,int father,int deep) { depth[u] = deep; for(int k=1;k<=D;k++) fa[u][k] = fa[fa[u][k-1]][k-1]; for(int i=head[u];~i;i=edge[i].next) { int v = edge[i].to; if(v == father) continue; fa[v][0] = u; lca(v,u,deep+1); } } int LCA(int u,int v) { if(u == v) return u; if(depth[u] < depth[v]) swap(u,v); for(int k=D;k>=0;k--) if(depth[fa[u][k]] >= depth[v]) u = fa[u][k]; if(u == v) return u; for(int k=D;k>=0;k--) if(fa[u][k] ^ fa[v][k]) u = fa[u][k] , v = fa[v][k]; return fa[u][0]; } // end LCA // segment tree related struct Node { int ch[2]; int pos,val; // the maxnode under this one and its value int size; // set for faster merge }node[maxn<<5]; #define ch(x,d) node[x].ch[d] #define size(x) node[x].size #define pos(x) node[x].pos #define val(x) node[x].val int maxnode; int sta[maxn<<5],top; inline int require() { int root; if(top) root = sta[top--]; else root = ++maxnode; return root; } void recycle(int &root) { if(!root) return; recycle(ch(root,0)); recycle(ch(root,1)); pos(root) = val(root) = 0; size(root) = 0; sta[++top] = root; root = 0; } inline void update(int root) { size(root) = size(ch(root,0)) + size(ch(root,1)); int lson = ch(root,0); int rson = ch(root,1); int rt = val(lson)>val(rson) || (val(lson)==val(rson)&&pos(lson)<pos(rson)) ? lson : rson; // the minimum type is required val(root) = val(rt); pos(root) = pos(rt); } void modify(int &root,int l,int r,int pos,int val) { if(!root) root = require(); if(l == r) { val(root) += val; pos(root) = l; size(root) = !!val(root); return; } int mid = (l+r)>>1; if(pos<=mid) modify(ch(root,0),l,mid,pos,val); else modify(ch(root,1),mid+1,r,pos,val); update(root); } void merge(int rt,int root,int l,int r) { if(!size(root)) return; if(l == r) { modify(rt,1,maxw,l,val(root)); return; } int mid = (l+r)>>1; merge(rt,ch(root,0),l,mid); merge(rt,ch(root,1),mid+1,r); } int ultra(int root1,int root2) // root2 to root1 { if(size(root1) < size(root2)) swap(root1,root2); merge(root1,root2,1,maxw); recycle(root2); return root1; } // end segment tree // delta related struct Delta { int next; int pos; int val; }delta[maxn<<2]; int d_pre[maxn],d_post[maxn]; int maxdelta; inline void addpre(int u,int pos,int val) { delta[++maxdelta] = (Delta) { d_pre[u],pos,val }; d_pre[u] = maxdelta; } inline void addpost(int u,int pos,int val) { delta[++maxdelta] = (Delta) { d_post[u],pos,val }; d_post[u] = maxdelta; } // end delta // main inline void init() { memset(head,-1,sizeof(head)); maxedge=-1; memset(d_pre,-1,sizeof(d_pre)); memset(d_post,-1,sizeof(d_post)); maxdelta=-1; read(n); read(q); Root = (n+1)>>1; for(int i=1;i<n;i++) { int x,y; read(x); read(y); addedge(x,y); } lca(Root,-1,1); } void delta_composition() { for(int i=1;i<=q;i++) { int x,y,w; read(x),read(y),read(w); smax(maxw,w); int the_lca = LCA(x,y); addpre(x,w,1); addpre(y,w,1); addpre(the_lca,w,-1); addpost(the_lca,w,-1); } } int dfs(int u,int father) { int root = require(); for(int i=head[u];~i;i=edge[i].next) { int v = edge[i].to; if(v == father) continue; root = ultra(dfs(v,u),root); } for(int i=d_pre[u];~i;i=delta[i].next) modify(root,1,maxw,delta[i].pos,delta[i].val); ans[u] = pos(root); for(int i=d_post[u];~i;i=delta[i].next) modify(root,1,maxw,delta[i].pos,delta[i].val); return root; } void work() { dfs(Root,-1); for(int i=1;i<=n;i++) printf("%d\n",ans[i]); } int main() { freopen("rice.in","r",stdin); freopen("rice.out","w",stdout); init(); delta_composition(); work(); return 0; }
解法二:
树链剖分,只不过不用线段树维护树链,而是用维护当前颜色最大值。
按照特定的dfs序,把所有的区间更新变成差分序列,然后在dfs序列上求前缀和。
在top+1,然后在u的下一个节点-1
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<vector> #include<queue> #include<stack> #include<map> #include<set> #include<string> #include<iomanip> #include<ctime> #include<cctype> #include<algorithm> #ifdef WIN32 #define AUTO "%I64d" #else #define AUTO "%lld" #endif using namespace std; #define smax(x,tmp) x=max((x),(tmp)) #define smin(x,tmp) x=min((x),(tmp)) #define maxx(x1,x2,x3) max(max(x1,x2),x3) #define minn(x1,x2,x3) min(min(x1,x2),x3) typedef long long LL; template <class T> inline void read(T &x) { x = 0; T flag = 1; char ch = (char)getchar(); while(ch<'0' || ch>'9') { if(ch == '-') flag = -1; ch = (char)getchar(); } while(ch>='0' && ch<='9') { x = (x<<1) + (x<<3) + ch - '0'; ch = (char)getchar(); } x *= flag; } template <class T> T gcd(T a,T b) { return !b?a:gcd(b,a%b); } const int INF=0x3f3f3f3f; const int maxn = 100005; struct Edge { int to,next; }edge[maxn<<1]; int head[maxn]; int maxedge; inline void addedge(int u,int v) { edge[++maxedge] = (Edge) { v,head[u] }; head[u] = maxedge; edge[++maxedge] = (Edge) { u,head[v] }; head[v] = maxedge; } struct Delta { int next; int pos,val; }delta[maxn<<5]; int delta_head[maxn]; int maxdelta; inline void adddelta(int u,int pos,int val) { delta[++maxdelta] = (Delta) { delta_head[u],pos,val }; delta_head[u] = maxdelta; } struct Node { int val,pos; }node[maxn<<2]; #define val(x) node[x].val #define pos(x) node[x].pos int rid[maxn]; int maxnode; int fa[maxn],son[maxn],depth[maxn],size[maxn]; int top[maxn],id[maxn]; void dfs1(int u,int father,int deep) { fa[u] = father; depth[u] = deep; size[u] = 1; for(int i=head[u];~i;i=edge[i].next) { int v = edge[i].to; if(v == father) continue; dfs1(v,u,deep+1); size[u] += size[v]; if(!son[u] || size[son[u]]<size[v]) son[u] = v; } } void dfs2(int u,int tp) { top[u] = tp; id[u] = ++maxnode; rid[maxnode] = u; if(son[u]) dfs2(son[u],tp); for(int i=head[u];~i;i=edge[i].next) { int v = edge[i].to; if(v == fa[u] || v == son[u]) continue; dfs2(v,v); } } inline void update(int root) { int x = val(root<<1) >= val(root<<1|1) ? (root<<1) : (root<<1|1); val(root) = val(x); pos(root) = pos(x); } void modify(int root,int l,int r,int pos,int val) { if(l == r) { val(root) += val; pos(root) = val(root)? l : 0; return; } int mid = (l+r)>>1; if(pos <= mid) modify(root<<1,l,mid,pos,val); else modify(root<<1|1,mid+1,r,pos,val); update(root); } int n,q,Root,maxw; int ans[maxn]; void Delta_composition(int u,int v,int pos) { int tp1 = top[u] , tp2 = top[v]; while(tp1 ^ tp2) { if(depth[tp1] < depth[tp2]) swap(u,v) , swap(tp1,tp2); adddelta(rid[id[tp1]],pos,1); adddelta(rid[id[u]+1],pos,-1); u = fa[tp1]; tp1 = top[u]; } if(depth[u] < depth[v]) swap(u,v); adddelta(rid[id[v]],pos,1); adddelta(rid[id[u]+1],pos,-1); } void dfs(int u) { for(int i=delta_head[u];~i;i=delta[i].next) modify(1,1,maxw,delta[i].pos,delta[i].val); ans[u] = pos(1); if(son[u]) dfs(son[u]); for(int i=head[u];~i;i=edge[i].next) { int v = edge[i].to; if(v == fa[u] || v == son[u]) continue; dfs(v); } } inline bool init() { if(!~scanf("%d%d",&n,&q) || (!n&&!q)) return false; Root = (n+1)>>1; memset(head,-1,sizeof(head)); maxedge=-1; memset(delta_head,-1,sizeof(delta_head)); maxdelta=-1; memset(node,0,sizeof(node)); memset(son,0,sizeof(son)); maxnode = 0; for(int i=1;i<n;i++) { int x,y; read(x); read(y); addedge(x,y); } dfs1(Root,-1,1); dfs2(Root,Root); for(int i=1;i<=q;i++) { int u,v,w; read(u); read(v); read(w); smax(maxw,w); Delta_composition(u,v,w); } return true; } void work() { dfs(Root); for(int i=1;i<=n;i++) printf("%d\n",ans[i]); } int main() { #ifndef ONLINE_JUDGE freopen("rice.in","r",stdin); freopen("rice.out","w",stdout); #endif while(init()) work(); return 0; }
相关文章推荐
- 【noip模拟题】[dp][二分][树链剖分][hdu5029][线段树]
- NOIP模拟题 2016.10.5 [Trie] [数学] [二分答案] [杂题] [复杂状态DP]
- 【NOIP 模拟题】[T2] 王者荣耀(二分答案+dp)
- NOIP模拟题 2016.11.17 [数论] [数位DP] [扫描线] [线段树]
- NOIP 2015 Day 2 transport 运输计划 (树链剖分 序列差分 二分答案)
- bzoj 4326: NOIP2015 运输计划 (二分答案+树链剖分+树状数组)
- 【NOIP模拟题】【线段树】【树链剖分】发放粮食题解
- NOIP模拟题 2016.11.8 (2) [线段树] [动态逆序对] [矩阵快速幂] [数论] [欧拉函数]
- [二分答案 树链剖分] BZOJ 4326 NOIP2015 运输计划
- NOIP模拟题 2016.10.18 [二分答案] [从上到下的树形DP] [链表翻转]
- 【NOIP 模拟题】刺杀大使(二分答案+并查集)
- hzwer2015.9.13 NOIP模拟题 explo seq earth[DP][数论][二分][SPFA]
- spoj GSS7(树链剖分 + 线段树区间合并)
- 【NOIP2015】运输计划(树上差分,二分答案)
- AOJ 2450 Do use segment tree (树链剖分 + 线段树区间合并)
- [NOIP模拟题][树状数组][线段树]
- [NOIP模拟][LCS][二分][树链剖分][hdu5029]
- NOIP模拟题 [线段树][矩阵快速幂]
- BZOJ 1146 CTSC 2008 网络管理 Network 树链剖分+二分答案+平衡树
- LuoguP2680/UOJ150[NOIP2015] 运输计划 解题报告【二分答案+树上操作(LCA)+树上差分】