您的位置:首页 > 其它

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。。

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息