给定了一个正整数 N N N。
有多少种方法将 N N N分解成为四个质数 a , b , c , d a,b,c,d a,b,c,d的和。例如: 9 = 2 + 2 + 2 + 3 = 2 + 2 + 3 + 2 = 2 + 3 + 2 + 2 = 3 + 2 + 2 + 2 9 = 2 + 2 + 2 + 3 = 2 + 2 + 3 + 2 = 2 + 3 + 2 + 2 = 3 + 2 + 2 + 2 9=2+2+2+3=2+2+3+2=2+3+2+2=3+2+2+2,故共有 4 4 4种方法将 9 9 9分解成为四个整数。
本题多组数据测试:
第一行读入一个整数 T T T表示数据组数。
接下来共 T T T行,每行包含一个正整数 N N N。
共 T T T行,每行一个整数表示答案。
2
9
10
4
6
对于 10 % 10% 10%的数据, N ≤ 10 N\le10 N≤10。
对于 40 % 40% 40%的数据, N ≤ 100 N\le100 N≤100。
对于 70 % 70% 70%的数据, N ≤ 1000 N\le 1000 N≤1000。
对于 100 % 100% 100%的数据, T ≤ 10 , N ≤ 100000 T\le 10,N\le 100000 T≤10,N≤100000。
打表发现, 100000 100000 100000以内的质数有9500多个,显然直接枚举是过不了的。
考虑中途相遇法。
对于每一个数 n n n,设 f ( i ) , i ≤ n f(i),i\le n f(i),i≤n为用两个质数凑出 i i i的方案数,显然这个函数可以快速地打表,然后再枚举剩下的两个质数。
但这样时间复杂度是过高的,多打几次 f ( i ) f(i) f(i)就发现每次 f ( i ) f(i) f(i)都是相同的,那么我们就预先打出这个表即可。
#include
typedef long long ll;
const int Maxn=100000;
int N;
bool ispri[Maxn+5];
ll f[Maxn+5];
int pri[10000+5],tot;
inline void Init() {
for(int i=2;i<=Maxn;i++) {
if(!ispri[i])
pri[++tot]=i;
for(int j=1;j<=tot&&i*pri[j]<=Maxn;j++) {
ispri[i*pri[j]]=true;
if(i%pri[j]==0)break;
}
}
for(int i=1;i<=tot;i++)
for(int j=1;j<=tot&&pri[i]+pri[j]<=Maxn;j++)
f[pri[i]+pri[j]]++;
}
int main() {
freopen("plus.in","r",stdin);
freopen("plus.out","w",stdout);
int T;
scanf("%d",&T);
Init();
while(T--) {
scanf("%d",&N);
ll ans=0;
for(int i=4;i<=N-4;i++)
ans+=(f[i]*f[N-i]);
printf("%lld\n",ans);
}
return 0;
}
假设给定了两个整数m,n。有n个互不相同的整数 x 1 , x 2 , … , x n ( 0 ≤ x i ≤ 2 m − 1 ) x_1,x_2,\ldots,x_n(0\le xi\le 2^m-1) x1,x2,…,xn(0≤xi≤2m−1)。对于每一个属于 0 0 0到 2 m − 1 2^m-1 2m−1的 y y y,我们找到 p y p_y py使得 x p y x_{p_y} xpy异或 y y y有最大值。即对于任意的 i = ̸ p y i =\not py i≠py,有 y ⊕ x p y > y ⊕ x i y\oplus x_{p_y}>y\oplus x_i y⊕xpy>y⊕xi。(其中 ⊕ \oplus ⊕表示二进制异或)。
现在我们把这个问题反过来。给定 m m m和 n n n,以及序列 p 0 , p 1 , … , p 2 m − 1 p_0,p_1,\ldots,p_{2^m-1} p0,p1,…,p2m−1,计算有多少个不同序列 x 1 , x 2 , … , x n x_1,x_2,\ldots,x_n x1,x2,…,xn可以通过上文描述的问题生成出序列p。两个序列是不同的当且仅当存在一个i使得两个序列中 x i x_i xi是不同的。
答案对 1000000007 ( 1 0 9 + 7 ) 1000000007(10^9+7) 1000000007(109+7)取模。
第一行两个用空格隔开的整数 m , n m,n m,n。其中 2 m 2^m 2m是 p p p序列的长度, n n n是 x x x序列的长度。
之后 2 m 2^m 2m行,每行一个整数,表示 p p p序列。保证 1 1 1到 n n n中的每一个数在输入中都至少出现一次。
输出一行一个整数表示答案。
样例1:
3 6
1
1
2
2
3
4
5
6
样例2:
2 3
1
2
1
3
样例3:
3 8
1
2
3
4
5
6
7
8
样例1:
4
样例2:
0
样例3:
1
对于 30 % 30% 30%的数据: m ≤ 3 , n ≤ 4 m\le3,n\le4 m≤3,n≤4。
另外 10 % 10% 10%的数据: m = 0 m=0 m=0。
另外 10 % 10% 10%的数据: n = 1 n=1 n=1。
另外 10 % 10% 10%的数据: p i = i p_i=i pi=i。
对于 100 % 100% 100%的数据: 0 ≤ m ≤ 16 , 1 ≤ n ≤ 2 m 0\le m\le16,1\le n\le2^m 0≤m≤16,1≤n≤2m。
考虑由高位向低位一位一位的考虑。
然后就。。。 我不想写了QAQ。。。 真的脑洞。。。
#include
#include
using namespace std;
typedef long long ll;
const ll Mod=1000000007;
const int Maxn=(1<<16);
int N,M,P[Maxn+5];
ll DFS(int l,int r) {
if(l==r)return 1LL;
int mid=(l+r)>>1;
bool flag=true;
for(int i=l,j=mid+1;i<=mid;i++,j++)
if(P[i]!=P[j]) {
flag=false;
break;
}
if(flag)return DFS(l,mid)*2LL%Mod;
for(int i=l,j=mid+1;i<=mid;i++,j++)
if(P[i]==P[j])return 0;
return DFS(l,mid)*DFS(mid+1,r)%Mod;
}
int main() {
freopen("match.in","r",stdin);
freopen("match.out","w",stdout);
scanf("%d %d",&M,&N);
for(int i=0;i<(1<<M);i++)
scanf("%d",&P[i]);
printf("%lld\n",DFS(0,(1<<M)-1));
return 0;
}
给定一个 N N N个点的树,点的标号从 1 1 1到 N N N。
一条树上点 a a a到点 b b b的简单路径 P P P是一个 k k k个点的序列( a = P 1 , P 2 , … , P k = b a=P_1,P_2,\ldots,P_k=b a=P1,P2,…,Pk=b),相邻的两个点之间有边连接且任何一个点至多在序列中出现一次。注意 a a a可能和 b b b是相等的。简单路径上的边就是连接序列上相邻两个点的边。
一条简单路径的邻边是只有一个端点在简单路径上的边。树上的每条边是黑色的或者白色的。最开始所有的边都是黑色的。
有 Q Q Q次操作,有两种操作类型。
0
:计算 a a a到 b b b的简单路径上有多少条边是黑色的。1
:将简单路径 a a a到 b b b上的边全部设置成白色的。将简单路径a到b上的邻边设置成黑色的。第一行一个整数 N ( 1 ≤ N ≤ 200000 ) N(1\le N\le200000) N(1≤N≤200000)。
接下来 N − 1 N-1 N−1行,每行两个整数 a i , b i a_i,b_i ai,bi,表示一条树边。保证读入的是一棵树。
接下来一行一个整数 Q ( 1 ≤ Q ≤ 300000 ) Q(1\le Q\le300000) Q(1≤Q≤300000)。
接下来 Q Q Q行,每行三个整数 t i , a i , b i t_i, a_i, b_i ti,ai,bi。其中 t i t_i ti表示操作类型。
对于每个0
操作,输出一行一个整数表示答案。
样例1:
8
1 7
7 8
1 5
1 3
5 2
5 6
6 4
5
1 2 7
0 2 8
1 6 7
0 1 2
0 3 8
样例2:
19
1 2
2 3
1 5
5 4
5 6
6 7
6 8
1 11
11 12
11 13
11 10
10 9
13 14
13 15
15 16
15 17
15 18
15 19
6
1 19 8
0 16 2
0 16 3
1 12 9
0 19 8
0 16 9
样例1:
1
1
2
样例2:
2
3
2
2
对于 5 % 5% 5%的数据: N = 1 N=1 N=1。
对于 20 % 20% 20%的数据: N ≤ 200 N\le 200 N≤200。
对于 30 % 30% 30%的数据: N ≤ 2000 N\le2000 N≤2000。
另外 20 % 20% 20%的数据:树的形态是一条链。
另外 30 % 30% 30%的数据:操作1
中 a i = b i a_i=b_i ai=bi,且 a i a_i ai是随机生成的。
对于 100 % 100% 100%的数据: 1 ≤ N ≤ 200000 , 1 ≤ Q ≤ 300000 1\le N\le 200000,1\le Q\le 300000 1≤N≤200000,1≤Q≤300000
如果你在Windows环境下,请在编译选项中添加如下的内容:
-Wl,--stack=200000000
看到这个题我的第一反应是树链剖分 (然而我考场上没有搞出来)
考虑修改操作。对于每一条链,我们用线段树维护链上的修改时间即可。
考虑查询操作。对于每一条链,我们可以用链上的修改时间戳来判定,即对于两个点,若两个点的时间戳不同则该边为黑色,否则为白色。两条重链的连接处用两个单点查询即可。
真的板子。。。
#include
#include
#include
using namespace std;
const int Maxn=200000;
struct Edge {
int to;
Edge *nxt;
};
Edge pool[Maxn*2+5];
Edge *ecnt=&pool[0],*G[Maxn+5];
inline void addedge(int u,int v) {
Edge *p=++ecnt;
p->to=v;
p->nxt=G[u],G[u]=p;
}
int N;
struct SegmentTree {
struct Segment {
int tag,cnt;
int lef_col,rig_col;
};
Segment t[Maxn*4+5];
void pushup(int rt) {
t[rt].lef_col=t[rt<<1].lef_col;
t[rt].rig_col=t[rt<<1|1].rig_col;
t[rt].cnt=t[rt<<1].cnt+t[rt<<1|1].cnt+(t[rt<<1].rig_col!=t[rt<<1|1].lef_col);
}
void pushdown(int rt) {
if(t[rt].tag!=-1) {
t[rt<<1].tag=t[rt<<1].lef_col=t[rt<<1].rig_col=t[rt].tag;
t[rt<<1|1].tag=t[rt<<1|1].lef_col=t[rt<<1|1].rig_col=t[rt].tag;
t[rt<<1].cnt=t[rt<<1|1].cnt=0;
t[rt].tag=-1;
}
}
void build(int rt,int l,int r) {
t[rt].tag=-1;
if(l==r) {
t[rt].lef_col=t[rt].rig_col=l,t[rt].cnt=0;
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid),build(rt<<1|1,mid+1,r);
pushup(rt);
}
void modify(int rt,int l,int r,int ml,int mr,int val) {
if(l>mr||r<ml)return;
if(ml<=l&&r<=mr) {
t[rt].tag=t[rt].lef_col=t[rt].rig_col=val;
t[rt].cnt=0;
return;
}
pushdown(rt);
int mid=(l+r)>>1;
modify(rt<<1,l,mid,ml,mr,val);
modify(rt<<1|1,mid+1,r,ml,mr,val);
pushup(rt);
}
int query(int rt,int l,int r,int ql,int qr) {
if(l>qr||r<ql)return 0;
if(ql<=l&&r<=qr)return t[rt].cnt;
pushdown(rt);
int mid=(l+r)>>1;
int ret=query(rt<<1,l,mid,ql,qr)+query(rt<<1|1,mid+1,r,ql,qr);
if(ql<=mid&&mid+1<=qr)
ret+=(t[rt<<1].rig_col!=t[rt<<1|1].lef_col);
return ret;
}
int query(int rt,int l,int r,int pos) {
if(l==r)return t[rt].lef_col;
pushdown(rt);
int mid=(l+r)>>1;
if(pos<=mid)return query(rt<<1,l,mid,pos);
else return query(rt<<1|1,mid+1,r,pos);
}
};
int siz[Maxn+5],dep[Maxn+5],son[Maxn+5],fa[Maxn+5];
int top[Maxn+5],tid[Maxn+5],rnk[Maxn+5],dcnt;
void dfs(int u,int pre,int depth) {
dep[u]=depth,fa[u]=pre,siz[u]=1;
for(Edge *p=G[u];p!=NULL;p=p->nxt) {
int v=p->to;
if(v==pre)continue;
dfs(v,u,depth+1);
siz[u]+=siz[v];
if(son[u]==-1||siz[v]>siz[son[u]])
son[u]=v;
}
}
void dfs(int u,int tp) {
top[u]=tp,tid[u]=++dcnt,rnk[dcnt]=u;
if(son[u]==-1)return;
dfs(son[u],tp);
for(Edge *p=G[u];p!=NULL;p=p->nxt) {
int v=p->to;
if(v==fa[u]||v==son[u])
continue;
dfs(v,v);
}
}
SegmentTree tree;
void Modify(int u,int v,int col) {
while(top[u]!=top[v]) {
if(dep[top[u]]<dep[top[v]])
swap(u,v);
tree.modify(1,1,N,tid[top[u]],tid[u],col);
u=fa[top[u]];
}
if(dep[u]<dep[v])swap(u,v);
tree.modify(1,1,N,tid[v],tid[u],col);
}
int Query(int u,int v) {
int ret=0;
while(top[u]!=top[v]) {
if(dep[top[u]]<dep[top[v]])
swap(u,v);
ret+=tree.query(1,1,N,tid[top[u]],tid[u]);
u=top[u];
ret+=(tree.query(1,1,N,tid[u])!=tree.query(1,1,N,tid[fa[u]]));
u=fa[u];
}
if(dep[u]<dep[v])swap(u,v);
ret+=tree.query(1,1,N,tid[v],tid[u]);
return ret;
}
int main() {
freopen("colour.in","r",stdin);
freopen("colour.out","w",stdout);
scanf("%d",&N);
for(int i=1;i<N;i++) {
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v),addedge(v,u);
}
memset(son,-1,sizeof son);
dfs(1,0,1);
dfs(1,1);
tree.build(1,1,N);
int Q;
scanf("%d",&Q);
for(int i=1;i<=Q;i++) {
int op,u,v;
scanf("%d %d %d",&op,&u,&v);
if(op==1)Modify(u,v,i+N);
else printf("%d\n",Query(u,v));
}
return 0;
}