NOIP模拟测试3「序列·熟练剖分·建造游乐园(play)」
---恢复内容开始---
序列
刚调出来样例就A了,假装是水题。
因为是乱序,我们要求出来每两项之间最小公比,而不是直接比
求出来每两项之间最小公比,然后扫一遍就完了。(还要注意重复情况)
那么问题就转化成了怎么求最小公比。

完了
以下是本人丑陋的代码
#include<bits/stdc++.h>
#define ll long long
#define A 100000
using namespace std;
ll n,a[10*A],tot=0,maxlen[10*A],nowlen=0,dl[10*A],zuixiaogongbi,prime[10*A],mark[10*A],ges[1010],ml=0,big[A];
set <ll> s;
ll gcd(ll x,ll y)
{
return y==0?x:gcd(y,x%y);
}
bool pol(ll ooo){
ll x=ooo;
for(ll i=1;i<=tot;i++){
if(prime[i]>x) break;
while(x!=prime[i]){
if(x%prime[i]==0){
if(!ges[prime[i]])
dl[++dl[0]]=prime[i];
ges[prime[i]]++;
x=x/prime[i];
}
else break;
}
if(x==prime[i]){
if(!ges[x])dl[++dl[0]]=x;
ges[x]++;
break;
}
}
if(x)return 0;
return 1;
}
bool check(ll x,ll y)
{
memset(ges,0,sizeof(ges));
dl[0]=0;
if(x<=y) swap(x,y);
if(x%y) return 0;
ll z=x/y,gd=1;
if(pol(z))return 0;
if(dl[0]!=1)
{
gd=gcd(ges[dl[1]],ges[dl[2]]);
zuixiaogongbi=1;
for(ll i=3;i<=dl[0];i++)
gd=gcd(ges[dl[i]],gd);
}
else
gd=ges[dl[1]],zuixiaogongbi=1;
// for(ll i=1;i<=dl[0];i++){
// printf("ges=%lld dl=%lld \n",ges[dl[i]],dl[i]);
// }
// printf("pd=%lld\n",gd);
for(ll i=1;i<=dl[0];i++)
ges[dl[i]]/=gd,zuixiaogongbi*=ges[dl[i]]*dl[i]; return 1;
}
int main()
{
for(ll i=2;i<=1000;i++){
if(!mark[i]){
prime[++tot]=i;
}
for(ll j=1;j<=tot;j++){
if(i*prime[j]>1000) break;
mark[i*prime[j]]=1;
if(i%prime[j]==0){break;}
}
}
ll man=0;
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
ll len=1;
for(ll i=2;i<=n;i++)
if(a[i]==a[i-1]){len++;}
else{man=max(man,len);len=1;}
man=max(man,len);
for(ll i=2;i<=n;i++){
if(check(a[i],a[i-1]))
big[i]=zuixiaogongbi;
else big[i]=-1;
}
len=1;s.insert(a[1]);
for(ll i=2;i<=n;i++)//枚举开头
{
if((big[i]!=-1)&&(big[i]==big[i-1]||big[i-1]==0||s.size()==1))
{
ll x=a[i];
bool ok=0;
if((s.find(x))==s.end())
{
// printf("insert a[%lld]=%lld big[%lld]=%lld big[%lld]=%lld\n",i,a[i],i-1,big[i-1],i-2,big[i-2]);
s.insert(x);
ll sz=s.size();
man=max(man,sz);
}
else
{
// printf("因重复而清空 i=%lld\n",i);
ll sz=s.size();
man=max(man,sz);
s.clear();s.insert(a[i]);
ll ss=s.size();
// printf("目前size=%lld\n",ss);
}
}
else
{ ll sz=s.size();
// printf("因不相等而清空 i=%lld sz=%lld\n",i,sz);
man=max(man,sz);
s.clear();
s.insert(a[i]);
}
ll w=s.size();
// printf("w=%lld\n",w);
}
ll sz=s.size();
man=max(man,sz);
cout<<man<<endl;
}
熟练剖分
这个题还是挺好的。
这是学长的题解
时间复杂度为O(n^2)的树上dp,关键在如何设计状态以及如何合并dp数组
对于这个关键部分可以有很多种不同的设计,欢迎同学们积极设计自己的状态定义以及转移方式
我分享一下我的做法
f[i][j]表示对于点i,其子树内最大代价为j的方案数
转移方式为
- 以dfs为大框架进行
- 对于每个节点先处理所有的儿子节点,最后将已获得的所有子节点信息进行合并得到该节点信息
- 合并时,依次将每一个子节点的信息纳入暂时的动态的一个dp数组储存,该dp数组分为0/1两个数组,大概长成g[0/1][j]这个样子(可以使用滚动数组变成g[0/1][0/1][j])0数组表示之前的子节点中不含重边的情况,1表示之前的子节点中已含有重边,j表示对应状况下,最大代价为j(不是前缀和),数组内存储信息为该情况下的方案数
- 转移就简单了,枚举g数组的每一种情况以及正在合并的子节点dp数组的每一种情况,进行转移,转移时注意代价与重边的变化
- 这个时间复杂度的计算来自于点对总数,所以一定要把合并时的时间复杂度准确控制,不要错误写成O(n^3)的dp
#include<bits/stdc++.h>
#define ll long long
#define A 10000
using namespace std;
const ll mod=1e9+7;
ll g[2][2][A],f[A][A],son[A][A],size[A],n,sum;
bool flag[A];
inline ll meng(ll x,ll k){
ll ans=1;
for(;k;k>>=1,x=x*x%mod)
if(k&1)
ans=ans*x%mod;
return ans;
} void dfs(ll x){
if(!son[x][0]){f[x][0]=1;return ;}
for(ll i=1;i<=son[x][0];i++){
ll y=son[x][i];
dfs(y);size[x]=max(size[x],size[y]+1);
}
ll cur=0,maxn=size[son[x][1]]+1;
memset(g,0,sizeof(g));
for(ll i=1;i<=maxn;i++){
g[0][0][i]=f[son[x][1]][i-1];//因为g选择的是x的儿子,当前面没有选择重链时应该由i-1转移过来
g[0][1][i]=f[son[x][1]][i];//因为g选择了重链,所以i代价应该减1
}
g[0][1][0]=f[son[x][1]][0];
for(ll i=2;i<=son[x][0];i++){
memset(g[cur^1],0,sizeof(g[cur^1]));
for(ll j=0;j<=maxn;j++){
for(ll k=0;k<=size[son[x][i]];k++){//枚举儿子的深度
g[cur^1][0][max(j,k+1)]=(g[cur^1][0][max(j,k+1)]+f[son[x][i]][k]*g[cur][0][j])%mod;//还没有重链
g[cur^1][1][max(j,k+1)]=(g[cur^1][1][max(j,k+1)]+f[son[x][i]][k]*g[cur][1][j])%mod;//之前的点已经有了重链
g[cur^1][1][max(j,k)]=(g[cur^1][1][max(j,k)]+f[son[x][i]][k]*g[cur][0][j])%mod;//选择当前点为重链
}
}
cur^=1;
maxn=max(maxn,size[son[x][i]]+1);
}
memcpy(f[x],g[cur][1],sizeof(f[x]));
}
int main(){
scanf("%lld",&n);
sum=1;
for(ll i=1;i<=n;i++){
scanf("%lld",&son[i][0]);
if(son[i][0]){
for(ll j=1;j<=son[i][0];j++){
ll r;scanf("%lld",&r);
son[i][j]=r;
flag[r]=1;
}
}
(sum*=((son[i][0]==0)?1:meng(son[i][0],mod-2)))%=mod;
}
for(ll i=1;i<=n;i++){
if(!flag[i]){
dfs(i);
ll ans=0;
for(ll j=1;j<=size[i];j++)
{ans=(ans+f[i][j]*j)%mod;}
ans=sum*ans%mod;
printf("%lld\n",ans);
return 0;
}
}
}
建造游乐园
首先我们要知道无向图的一条性质,一个无向图一定有偶数个度数为奇数的点。
那么我们在i-1中随意连边(可连可以不连,共2^种情况)$C_{i-1}^{2}$是哪两个点之间连边的情况,而$2^{C_{i-1}^{2}}$则是每个边都可以选择或者不选,
那么即使有度数为奇数的点,我们让i与它相连就保证了i点入度仍为偶数,并且其他奇数入度点变成了偶数。
这样我们就保证了它一定是偶数条边
设g=$2^{C_{i-1}^{2}}$(为可能不连通的欧拉图) f为符合的连通的欧拉图
因为欧拉图必须所有点都入度为偶数
只要有入度为奇数的点就不符合
于是我们用一个类似于容斥的东西只要有不联通就不行。
g[j]中本身包含只有一个点连通,只有两个点连通一直到j个点连通我们让g[j]减去1--j-1的连通情况就构成了j个点连通。
我们让f包含i点,我们还要从剩余i-1个点中,选择j-1个点使它与i点组成连通图f[j]。
剩余i-j个点随意连(g图与f图完全分割,没有连边)
然后我们从i-1个点中选择j-1个让j-1个点与i相连。
于是我们得到了:
$f[i]=g[i]-\sum \limits_{j=1}^{i-1}f[j]*g[i-j]*{C_{i-1}^{j-1}}$
至于为什么不是i个里面选,会选重复
完了
以下是本人丑陋的代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 2100
const ll mod=1000000007;
ll n,sum[A],c=0,ans,cishu,C[A][A],g[A],f[A];
ll meng(ll x,ll k){
ll ans=1;
for(;k;k>>=1,x=x*x%mod)
if(k&1) ans=ans*x%mod;
return ans;
}
int main()
{
scanf("%lld",&n);
C[0][0]=1;
for(ll i=1;i<=n;i++) C[i][0]=1;
for(ll i=1;i<=n;i++)
for(ll j=1;j<=i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
for(ll i=1;i<=n;i++)
g[i]=meng(2,C[i-1][2]);
//首先我们要知道无向图的一条性质,一个无向图一定有偶数个度数为奇数的点
//那么我们在i-1中随意连边(可连可以不连,共2^种情况),即使有度数为奇数的点我们相连仍为偶数
for(ll i=1;i<=n;i++){
f[i]=g[i];
for(ll j=1;j<=i-1;j++){
f[i]-=f[j]*g[i-j]%mod*C[i-1][j-1]%mod;
//乘i-1,j-1原因。很简单,我们从i-1个数中选,让他们与i相连
}
f[i]=(f[i]%mod+mod)%mod;
}
cout<<(f[n]*C[n][2]%mod+mod)%mod<<endl;
}
---恢复内容结束---
序列
刚调出来样例就A了,假装是水题。
因为是乱序,我们要求出来每两项之间最小公比,而不是直接比
求出来每两项之间最小公比,然后扫一遍就完了。(还要注意重复情况)
那么问题就转化成了怎么求最小公比。

完了
以下是本人丑陋的代码
熟练剖分
这个题还是挺好的。
一切尽在代码之中
建造游乐园
首先我们要知道无向图的一条性质,一个无向图一定有偶数个度数为奇数的点。
那么我们在i-1中随意连边(可连可以不连,共2^种情况)$C_{i-1}^{2}$是哪两个点之间连边的情况,而$2^{C_{i-1}^{2}}$则是每个边都可以选择或者不选,
那么即使有度数为奇数的点,我们让i与它相连就保证了i点入度仍为偶数,并且其他奇数入度点变成了偶数。
这样我们就保证了它一定是偶数条边
设g=$2^{C_{i-1}^{2}}$(为可能不连通的欧拉图) f为符合的连通的欧拉图
因为欧拉图必须所有点都入度为偶数
只要有入度为奇数的点就不符合
于是我们用一个类似于容斥的东西只要有不联通就不行。
g[j]中本身包含只有一个点连通,只有两个点连通一直到j个点连通我们让g[j]减去1--j-1的连通情况就构成了j个点连通。
我们让f包含i点,我们还要从剩余i-1个点中,选择j-1个点使它与i点组成连通图f[j]。
剩余i-j个点随意连(g图与f图完全分割,没有连边)
然后我们从i-1个点中选择j-1个让j-1个点与i相连。
于是我们得到了:
$f[i]=g[i]-\sum \limits_{j=1}^{i-1}f[j]*g[i-j]*{C_{i-1}^{j-1}}$
至于为什么不是i个里面选,会选重复
完了
以下是本人丑陋的代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define A 2100
const ll mod=1000000007;
ll n,sum[A],c=0,ans,cishu,C[A][A],g[A],f[A];
ll meng(ll x,ll k){
ll ans=1;
for(;k;k>>=1,x=x*x%mod)
if(k&1) ans=ans*x%mod;
return ans;
}
int main()
{
scanf("%lld",&n);
C[0][0]=1;
for(ll i=1;i<=n;i++) C[i][0]=1;
for(ll i=1;i<=n;i++)
for(ll j=1;j<=i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
for(ll i=1;i<=n;i++)
g[i]=meng(2,C[i-1][2]);
//首先我们要知道无向图的一条性质,一个无向图一定有偶数个度数为奇数的点
//那么我们在i-1中随意连边(可连可以不连,共2^种情况),即使有度数为奇数的点我们相连仍为偶数
for(ll i=1;i<=n;i++){
f[i]=g[i];
for(ll j=1;j<=i-1;j++){
f[i]-=f[j]*g[i-j]%mod*C[i-1][j-1]%mod;
//乘i-1,j-1原因。很简单,我们从i-1个数中选,让他们与i相连
}
f[i]=(f[i]%mod+mod)%mod;
}
cout<<(f[n]*C[n][2]%mod+mod)%mod<<endl;
}
NOIP模拟测试3「序列·熟练剖分·建造游乐园(play)」的更多相关文章
- NOIP模拟测试2「排列 (搜索)·APIO划艇」
排序 内存限制:128 MiB 时间限制:1000 ms 标准输入输出 题目描述 输入格式 数据范围与提示 对于30%的数据,1<=N<=4: 对于全部的数据,1<=N< ...
- NOIP模拟测试19「count·dinner·chess」
反思: 我考得最炸的一次 怎么说呢?简单的两个题0分,稍难(我还不敢说难,肯定又有人喷我)42分 前10分钟看T1,不会,觉得不可做,完全不可做,把它跳了 最后10分钟看T1,发现一个有点用的性质,仍 ...
- NOIP模拟测试13「矩阵游戏·跳房子·优美序列」
矩阵游戏 考试时思路一度和正解一样,考试到最后还是打了80分思路,结果80分打炸了只得了40分暴力分 题解 算出来第一列的总值,每次通过加每两列之间的差值得出下一列的总值 算第一列我们只需要让当前点* ...
- NOIP模拟测试8「寿司」
考试时打的类似$n^2$暴力,然后炸了只有10分 后来验证我的算法伪了. 题解 显然你有一种解法,假设你要在一个B点断开将R分别移向最左 最右,这样只用分别计算B点右面蓝色数量左面蓝色数量就得到了一个 ...
- NOIP模拟测试30「return·one·magic」
magic 题解 首先原式指数肯定会爆$long$ $long$ 首先根据欧拉定理我们可以将原式换成$N^{\sum\limits_{i=1}^{i<=N} [gcd(i,N)==1] C_{G ...
- NOIP模拟测试26「嚎叫响彻在贪婪的机房·主仆见证了 Hobo 的离别·征途堆积出友情的永恒」
题目比较神仙,注意是题目神仙 贪婪暗示贪心,堆积暗示堆优化$\%\%\%\%\%\%\%$ 两个乱搞$+$一个堆优化$dp$ 嚎叫响彻在贪婪的机房 题解 对于一个序列来说只要他们差的$gcd$不为$1 ...
- NOIP模拟测试21「折纸·不等式」
折纸 题解 考试时无限接近正解,然而最终也只是接近而已了 考虑模拟会爆炸,拿手折纸条试一试,很简单 考你动手能力 代码 #include<bits/stdc++.h> using name ...
- NOIP模拟测试18「引子·可爱宝贝精灵·相互再归的鹅妈妈」
待补 引子 题解 大模拟,注意细节 代码1 #include<bits/stdc++.h> using namespace std; int n,m;char a[1005][1005]; ...
- NOIP模拟测试11「string·matrix·big」
打的big出了点小问题,maxx初值我设的0然后少了10分 第二题暴力打炸 第一题剪了一些没用的枝依然40分 总分70 这是一次失败的考试 string 想到和序列那个题很像,但我没做序列,考场回忆学 ...
随机推荐
- c++vs类图
安装visual studio扩展开发工具 一定要勾选右侧栏中的类设计器 安装完成后在菜单栏点击视图--类视图,会出现类视图框,在框中右键项目--查看类视图,就自动生成了.
- 有关80386cpu在保护模式下的虚拟地址,线性地址和实际物理地址的关系
80386cpu是8086cpu的升级版,其具有32位的寄存器.(32根地址线和32根数据线) 8086cpu其是16位的寄存器但是其地址线有20根,其寻址范围为2的20次方,但是有一个16位的寄存器 ...
- 2020中国大学生程序设计竞赛(CCPC) - 网络选拔赛总结
1003 Express Mail Taking 题意:有n个柜子(编号1-n),m封信,k号位置有钥匙,现在需要取信封,并且每取一次信封都要从k号位置进行领取一次钥匙,再去有信封的位置领取信封,问最 ...
- Unity shader 护盾shield的简单实现
先上效果图 shader所用的贴图资源 扰动 直接对uv进行变换就可以了,记得首先把六边形格子地图的Tilling调高点 先预先调成合适大小的六边形,然后repeat铺满整个护盾 // Tiles a ...
- margin (子元素远离父元素边框)
如果父盒子没有设置border框着,那么他的子元素无法利用margin-top 来远离父元素的上边框 如果使用了margin-top会使子元素和父元素一起往下移 (子想离,父不设置border边框 则 ...
- C++PRIMER第二章前半部分答案
C++PRIMER第二章前半部分答案 哈哈哈,为什么是前半部分呢,后半部分还在学习中,重新系统性的学习c++,共同进步嘛,不多说,跟我一起来看看吧,第三章开始才是新手收割的时候,慢慢来~~ 2.1&a ...
- [DB] mysql windows 安装
参考 mysql安装 https://www.cnblogs.com/zhangkanghui/p/9613844.html navicat for mysql 中文破解版(无需激活码) https: ...
- 二、Python流程控制练习题
一.分支结构-if等 练习题: 练习1:英制单位与公制单位互换 练习2:掷骰子决定做什么 练习3:百分制成绩转等级制 练习4:输入三条边长如果能构成三角形就计算周长和面积 练习5:个人所得税计算器 练 ...
- exit()和_exit()的区别
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/HAHAandHEHE/article/de ...
- lvresize 调整LVM逻辑卷的空间大小,可以增大空间和缩小空间
lvresize 相关命令:lvreduce,lvextend,lvdisplay,lvcreate,lvremove,lvscan lvresize指令:调整逻辑卷空间大小[语 法]lvr ...