Test 2018-09-20
【jzoj 4743 NOIP2016 提高 A 组模拟 9.2】积木
Description
Input
第一行包含一个整数 $ n $ 。
接下来 $ n $ 行,每行包含三个整数 $ a,b,c $ ,表示该块积木是一个 $ a \times b \times c $ 的长方体。
Output
第一行包含一个整数,表示答案。
Sample Input
3
8 7 6
3 9 4
1 10 5
Sample Output
18
Explanation
选择第 $ 1 $ 块积木和第 $ 3 $ 块积木。
Data Constraint
对于 $ 10 $ % 的数据, $ n=1 $ 。
对于 $ 40 $ % 的数据, $ n \le 6 $
对于 $ 100 $ % 的数据, $ 1 \le n \le 15, 1 \le a,b,c \le 10^8 $ 。
题解
-
10 分做法
输出 $ max(a,b,c) $ 。 -
40 分做法
⽣成全排列,然后枚举每个积⽊哪个⾯朝上,时间复杂度 $ O(n!·3n) $ 。 -
100 分做法
显然是状态压缩 $ DP $ 。设计状态 $ f[S][i][0/1/2] $ 表示已经⽤了集合 $ S $ 内的积⽊,
最顶上是编 号为 $ i $ 的积⽊,它的哪个⾯朝上。转移时枚举不在 $ S $ 内的积⽊,以及朝上的⾯判断即可。时间 复杂度 $ O(2^n ·(3n)^2) $ 。 -
我的做法: 暴搜(过了)w(゚Д゚)w
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,a[20],b[20],c[20],ans;
#define INF 100000005
bool vis[20];
void dfs(int res,int A,int B){
ans=max(ans,res);
for(int i=1;i<=n;++i)
if(!vis[i]){
vis[i]=1;
if((A>=a[i]&&B>=b[i])||(A>=b[i]&&B>=a[i])) dfs(res+c[i],a[i],b[i]);
if((A>=a[i]&&B>=c[i])||(A>=c[i]&&B>=a[i])) dfs(res+b[i],a[i],c[i]);
if((A>=b[i]&&B>=c[i])||(A>=c[i]&&B>=b[i])) dfs(res+a[i],b[i],c[i]);
vis[i]=0;
}
return;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d %d %d",&a[i],&b[i],&c[i]);
dfs(0,INF,INF);
printf("%d",ans);
return 0;
}
【jzoj 4755 NOIP2016 提高 A 组模拟 9.4】快速荷叶叶变换
Description
Input
一行,包含两个整数 $ N,M $ 。
Output
$ 1 $ 个整数,$ FHT(N,M) \quad mod \quad 1000000007 $ 的值。
Sample Input
3 4
Sample Output
1
Data Constraint
对于 $ 40 $ % 的数据,$ 1 ≤ N,M ≤ 1000 $
对于 $ 60 $ % 的数据,$ 1 ≤ N,M ≤ 10^6 $
对于 $ 100 $ % 的数据,$ 1 ≤ N,M ≤ 10^9 $
题解
- 直接模拟,期望得分 $ 40 $ 分
-
这样分别计算,可以拿到 $ 60 $ 分
-
考虑对 $ \sum_{i=1}^N ( N \quad mod \quad i) $ 继续化简:
- 因为 $ \lfloor \frac{N}{i} \rfloor $ 只有 $ \sqrt{N} $ 种取值,所以可以分段计数,时间复杂度为 $ O( sqrt{N} ) $
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
#define Mod 1000000007
int n,m,ans1,ans2;
int work(int N){
int res=N*N%Mod,r;
for(int l=1;l<=N;l=r+1){
r=N/(N/l);
res=(res-(l+r)*(r-l+1)/2%Mod*(N/l)%Mod+Mod)%Mod;
}
return (res+Mod)%Mod;
}
signed main(){
scanf("%lld %lld",&n,&m);
ans1=work(n); ans2=work(m);
printf("%lld",ans1*ans2%Mod);
return 0;
}
【jzoj 4757 NOIP2016 提高 A 组模拟 9.4】树上 摩托
Description
Sherco 是一位经验丰富的魔♂法师。 Sherco 在第零次圣杯战争中取得了胜利,并取得了王之宝藏——王の树。
他想把这棵树砍去任意条边,拆成若干棵新树,并装饰在他的摩托上,让他的摩托更加酷炫。
但 Sherco 认为,这样生成的树不具有美感,于是 Sherco 想让每棵新树的节点数相同。 他想知道有多少种方法分割这棵树。
Input
第一行一个正整数 $ N $ ,表示这棵树的结点总数。
接下来 $ N-1 $ 行,每行两个数字 $ X,Y $ 表示编号为 $ X $ 的结点与编号为 $ Y $ 的结点相连。
结点编号的范围为 $ [1,N] $ 。
Output
一个整数,表示方案数。注意,不砍去任何一条边也算作一种方案。
Sample Input
6
1 2
2 3
2 4
4 5
5 6
Sample Output
3
Data Constraint
对于 $ 40 $ %的数据,$ N ≤ 15 $
对于 $ 60 $ %的数据,$ N ≤ 10^5 $
对于 $ 100 $ %的数据,$ N ≤ 10^6 $ 数据规模非常大,请使用高效的读入方式。
题解
-
暴力枚举删哪些边,期望得分40分。
-
观察到一些性质:
-
- 1.树的大小只可能是 $ N $ 的约数
2.树的大小确定的话,方案最多只有一种
- 1.树的大小只可能是 $ N $ 的约数
-
我们可以枚举树的大小,并 $ DFS $ 判定是否可行。
时间复杂度 $ O(𝑵 sqrt{𝑵})$ ,期望得分 $ 60 $ 分。 -
继续观察到一些性质:
将原树看做一个有根树,一个节点可以作一个块的”根”,
当且仅当该节点的size能被块的大小整除 -
预处理出每个节点的 $ size $ ,枚举树的大小 $ k $ ,判断 $ size $ 为 $ k $ 的 倍数的节点数量是否为 $ \frac{𝑵}{ 𝒌} $ 。
-
时间复杂度$ O(Nln_N) $
代码
- 虽然我 T 了,只要把dfs改成bfs就好啦!( ̄y▽, ̄)╭
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
void read(int &x){
char ch;x=0;
while(ch=getchar(),ch<'0'||ch>'9');x=ch-48;
while(ch=getchar(),ch>='0'&&ch<='9')x=10*x+ch-48;
}
int siz[1000005],num[1000005],n,ans;
int to[2000005],nxt[2000005],head[1000005],tot;
inline void add(int u,int v){ to[++tot]=v; nxt[tot]=head[u]; head[u]=tot; }
void dfs(int u,int fa){
siz[u]=1;
for(int i=head[u];i;i=nxt[i])
if(to[i]!=fa){
dfs(to[i],u);
siz[u]+=siz[to[i]];
}
++num[siz[u]];
}
int main(){
read(n);
for(int i=1;i<n;++i){
int u,v;
read(u); read(v);
add(u,v); add(v,u);
}
dfs(1,0);
for(int k=1;k<=n;++k)
if(n%k==0){
int p=n/k;
for(int j=k;j<=n;j+=k)
p-=num[j];
if(p==0) ++ans;
}
printf("%d",ans);
return 0;
}