NOIP模拟测试19
T1:
题目大意:将一颗有N个节点的树分割,使得每个联通块大小相等,问一共有多少方案。(N<=1000000)
首先,一条很显然的性质,每个联通块的大小一定是N的因子。
然后,我们可以对于每个因子d,DFS一遍,维护一个si值,代表该子树中有多少节点是连通的,一旦这个值等于d,将这颗子树切掉,若这个值大于d,则判定不合法。
这个方法每次DFS只验证一个因子,效率较低。
我们可以只进行一遍DFS将每颗子树的大小存进一个桶里。一颗子树被切掉,当且仅当该子树中剩下的点共有d个,若d合法,则当前子树原有的节点数应为d的倍数,我们可以枚举每个因子的每个倍数,在桶里查询,若总数为N/d,则合法。
时间复杂度为$\sum_{d|N}d=O(Nlog_2N)$
Code:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=;
int n,nm=,m=,ans=;
int fi[N],d[],s[N];
struct edge{
int v,ne;
}e[N<<];
void add(int x,int y)
{
e[++nm].v=y;
e[nm].ne=fi[x];fi[x]=nm;
}
int read()
{
int s=;char c=getchar();
while(c<''||c>'') c=getchar();
while(c>=''&&c<=''){
s=(s<<)+(s<<)+c-'';
c=getchar();
}
return s;
}
void divide()
{
for(int i=;i<=sqrt(n);i++){
if(n%i==){
d[++m]=i;
if(i*i!=n) d[++m]=n/i;
}
}
}
bool dfs(int x,int p)
{
s[x]=;
for(int i=fi[x];i!=;i=e[i].ne){
int y=e[i].v;
if(y==p) continue;
dfs(y,x);
s[x]+=s[y];
}
}
bool check(int x)
{
int tot=;
for(int i=x;i<=n;i+=x){
int l=lower_bound(s+,s+n+,i)-s;
int r=upper_bound(s+,s+n+,i)-s;
tot+=r-l;
}
if(x*tot==n) return true;
else return false;
}
int main()
{
n=read();
if(n!=) ans++;
for(int i=;i<n;i++){
int x=read(),y=read();
add(x,y);add(y,x);
}
divide();
dfs(,);
sort(s+,s+n+);
for(int i=;i<=m;i++){
if(check(d[i])) ans++;
}
printf("%d\n",ans);
return ;
}
T1
T2:
题目大意:一个圆环上有N个数,将这N个数分为M个块,使得最大的块的和最小,输出这个最小值。
显然的单调性,二分答案。
对于圆环,短环成链并复制一倍。
关键点在于起始点的选择,$O(N)$枚举起始点再$O(N)$判断是绝对不可行的,我们需要优化。
我们可以优化判断,一般的判断方式是一个一个累加,直到大于二分的值位置,这个过程可以用倍增优化,设st[i][j]为以i为起点,长度为2j的区间和,从大到小枚举j,若加和小于二分的值,则累加。
时间复杂度$O(NM*log_2NM)$,M较大时有可能被卡掉。
还有一种思路是优化枚举起点。
我们先假设以0处为起始点,用$O(N)$的方法先找到此时的第一个块,也就是最靠左的块,并设这个块的左端点为L,右端点为R。
那么枚举的起始点只用在区间[L,R]内移动。
证明:
由于是圆环,所以从一个点向左右扩展都是可以的,如果以R+1为起始点,它可以向左扩展,其向左的第一个块必定为[L,R],与枚举起始点在L的情况重复了,所以不需要再枚举,其它点同理。
所以我们就在复杂度上除了一个M。
但是这个方法在一开始二分的值较大时会被卡成$O(N)$,所以在一开始要强化剪枝,一旦发现可行情况或发现不可行时要立即中止,才能保证算法的高效
时间复杂度$O((N^2log_2N)/M)$,M较小时会十分低效。
将两种思路综合起来就很难被卡掉了,或者可以测试点分治。我用的是第二种思路。
Code:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=;
int n,m,nn,L=,R=,ans;
int t[N<<];
bool judge(int y,int x)
{
int ans=,tot=;
for(int i=y;i<=y+n-;i++){
if(tot+t[i]>x){
ans++;tot=t[i];
}
else tot+=t[i];
if(ans>m) return false;
}
return true;
}
bool check(int x)
{
int l=,r=,tot=;
for(int i=;i<=n;i++){
if(tot+t[i]>x) break;
r=i;tot+=t[i];
}
for(int i=l;i<=r;i++){
if(judge(i,x)) return true;
}
return false;
}
int main()
{
scanf("%d%d",&n,&m);
nn=*n-;
for(int i=;i<=n;i++){
scanf("%d",&t[i]);
R+=t[i];L=max(L,t[i]);
}
for(int i=n+;i<=nn;i++)
t[i]=t[i-n];
ans=R;
while(L<=R){
int mid=(L+R)>>;
if(check(mid)){
ans=mid;
R=mid-;
}
else
L=mid+;
}
printf("%d\n",ans);
return ;
}
T2
T3:
详见洛谷1606白银莲花池。
我打的是BFS和DFS,用spfa也可以。
首先要求起点到终点的最短路径。我们发现移动一步的贡献不大于1,用不着最短路算法,一般的BFS可以解决。
但是此题有两种边权,0和1,并且0在使用一次后会变成1。
性质一:起点到终点的最短路中没有环。
证明一:环的权值不为负,所以走环之后步数不会少。
性质二:同一个点至多被经过一次。
证明二:若同一个点被经过多次,则一定存在环。
我们可以记录每个点是否被走过,然后就可以BFS了。
到现在这道题和算法竞赛进阶指南0x26中电路维修一样。可以用双端队列BFS解决,若到一个点的贡献为0,则从队头入队,反之从队尾入队。
问题一解决,现在求路径条数。
数据有问题,不过在作者和某NC的竭力hack,以及tdcp提供错误AC标程,该问题已被解决。
我们发现如果一个点被重复经过,会导致结果的错误,我们需要一个强有力的剪枝。
我们把从起点开始的求最短路BFS改为从终点开始,求出终点到所有点的最短距离,作为估价函数,搜索中一旦发现当前代价加估价大于最短路时,立即终止搜索,这样就可以防止重复了。
题目要求经过空格不同才算方案不同。
这个用BFS不好解决,我们可以嵌套DFS。
性质三:所有连通的敌人可以缩为一个点。
我们发现敌人就是一个中介点,由于敌人和方案数无关,它不能被入队,但是我们可以经过敌人到达其他的空地。
我没建边,所以用了鬼畜的DFS。
大框架还是BFS,每次对出队元素进行DFS,如果遇到敌人,则继续进行DFS,遇到空格就将其入队。
注意在同一次DFS中,更新节点的信息来源于同一个节点,所以一个点只能被更新一次。多次DFS之间,为防止重复,每个点只能入队一次。
BFS到最后,即为正确答案。
由于DFS的存在,时间复杂度$O(N^2M^2)$,但远远达不到这个上界。
Code:
#include<iostream>
#include<cstdio>
#include<queue>
#define LL long long
using namespace std;
const int N=;
int n,m,sx,sy,ex,ey;
int ans1=;
LL ans2=;
int a[N][N],v[N][N];
LL dp[N][N][N*N];
bool vis[N][N],in[N][N],out[N][N][N*N];
int dex[]={,,,,,-,-,-,-};
int dey[]={,,-,,-,,-,,-};
deque<int> q1,q2,q3;
void bfs1()
{
v[ex][ey]=;
q1.push_back(ex);q2.push_back(ey);
while(!q1.empty()){
int x=q1.front(),y=q2.front();
q1.pop_front();q2.pop_front();
for(int i=;i<=;i++){
int xx=x+dex[i],yy=y+dey[i];
if(a[xx][yy]==||xx<||xx>n||yy<||yy>m) continue;
if(v[xx][yy]!=) continue;
if(a[xx][yy]==){
v[xx][yy]=v[x][y];
q1.push_front(xx);q2.push_front(yy);
}
else{
v[xx][yy]=v[x][y]+;
q1.push_back(xx);q2.push_back(yy);
}
}
}
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
v[i][j]-=;
if(a[i][j]==) ans1=v[i][j];
}
}
}
void dfs1(int xx,int yy,int x,int y,int z)
{
vis[xx][yy]=true;
for(int i=;i<=;i++){
int nex=xx+dex[i],ney=yy+dey[i];
if(a[nex][ney]==||nex<||nex>n||ney<||ney>m) continue;
if(a[nex][ney]==){
if(v[nex][ney]+z>ans1) continue;
if(vis[nex][ney]) continue;
dfs1(nex,ney,x,y,z);
}
else{
if(v[nex][ney]+z+>ans1) continue;
if(in[nex][ney]) continue;
in[nex][ney]=true;
dp[nex][ney][z+]+=dp[x][y][z];
if(!out[nex][ney][z+]){
q1.push_back(nex);q2.push_back(ney);q3.push_back(z+);
out[nex][ney][z+]=true;
}
}
}
}
void dfs2(int x,int y,int z)
{
vis[x][y]=false;
for(int i=;i<=;i++){
int nex=x+dex[i],ney=y+dey[i];
if(a[nex][ney]==||nex<||nex>n||ney<||ney>m) continue;
if(a[nex][ney]==){
if(v[nex][ney]+z>ans1) continue;
if(!vis[nex][ney]) continue;
dfs2(nex,ney,z);
}
else{
if(v[nex][ney]+z+>ans1) continue;
in[nex][ney]=false;
}
}
}
void bfs2()
{
while(!q1.empty()){
q1.pop_back();q2.pop_back();
}
dp[sx][sy][]=;out[sx][sy][]=true;
q1.push_back(sx);q2.push_back(sy);q3.push_back();
while(!q1.empty()){
int x=q1.front(),y=q2.front(),z=q3.front();
out[x][y][z]=false;
q1.pop_front();q2.pop_front();q3.pop_front();
dfs1(x,y,x,y,z);
dfs2(x,y,z);
}
ans2=dp[ex][ey][ans1];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
scanf("%d",&a[i][j]);
if(a[i][j]==){
sx=i;sy=j;
}
if(a[i][j]==){
ex=i;ey=j;
}
}
}
bfs1();
if(ans1==-){
printf("%d\n",ans1);
return ;
}
else
printf("%d\n",ans1-);
bfs2();
printf("%lld\n",ans2);
return ;
}
T3
NOIP模拟测试19的更多相关文章
- NOIP模拟测试19「count·dinner·chess」
反思: 我考得最炸的一次 怎么说呢?简单的两个题0分,稍难(我还不敢说难,肯定又有人喷我)42分 前10分钟看T1,不会,觉得不可做,完全不可做,把它跳了 最后10分钟看T1,发现一个有点用的性质,仍 ...
- [考试反思]NOIP模拟测试19:洗礼
[]260 []230[]210 []200[8]170[9]160 这套题一般,数据很弱,T1T2暴力都能A,而且都是一些思维题,想不到就爆0. 原因不明,很多一直很强的人在这一次滑铁卢了,于是我个 ...
- NOIP模拟测试19考试反思
这次考试是存在很大问题的,(如果不是T1T2出乎意料地A了,鬼知道会发生什么) T2A是情理之中,考试的时候测的极限数据跑的很快(无论m什么范围),但是T1真的...... T3没有分配太多的时间+没 ...
- 2019.8.13 NOIP模拟测试19 反思总结
最早写博客的一次∑ 听说等会儿还要考试[真就两天三考啊],教练催我们写博客… 大约是出题最友好的一次[虽然我还是炸了],并且数据也非常水…忽视第三题的锅的话的确可以这么说.但是T3数据出锅就是你的错了 ...
- 「题解」NOIP模拟测试题解乱写II(36)
毕竟考得太频繁了于是不可能每次考试都写题解.(我解释个什么劲啊又没有人看) 甚至有的题目都没有改掉.跑过来写题解一方面是总结,另一方面也是放松了. NOIP模拟测试36 T1字符 这题我完全懵逼了.就 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 C. 分组
2019.8.3 [HZOI]NOIP模拟测试12 C. 分组 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 刚看这题觉得很难,于是数据点分治 k只有1和2两种,分别 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色
2019.8.3 [HZOI]NOIP模拟测试12 B. 数颜色 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 数据结构学傻的做法: 对每种颜色开动态开点线段树直接维 ...
- 2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci)
2019.8.3 [HZOI]NOIP模拟测试12 A. 斐波那契(fibonacci) 全场比赛题解:https://pan.baidu.com/s/1eSAMuXk 找规律 找两个节点的lca,需 ...
- NOIP模拟测试17&18
NOIP模拟测试17&18 17-T1 给定一个序列,选取其中一个闭区间,使得其中每个元素可以在重新排列后成为一个等比数列的子序列,问区间最长是? 特判比值为1的情况,预处理比值2~1000的 ...
随机推荐
- docker 运行springboot jar包
1.将jar包移至自定义的/usr/jar目录下; 2.在/usr/jar目录下创建Dockerfile文件 文件如下: #FROM命令定义构建镜像的基础镜像,该条必须是dockerfile的首个命令 ...
- POJ 2387 Til the Cows Come Home (dijkstra模板题)
Description Bessie is out in the field and wants to get back to the barn to get as much sleep as pos ...
- mysql分表分库 ,读写分离
1.分表 当项目上线后,数据将会几何级的增长,当数据很多的时候,读取性能将会下降,更新表数据的时候也需要更新索引,所以我们需要分表,当数据量再大的时候就需要分库了. a.水平拆分:数据分成多个表 b. ...
- linux shell设置颜色
使用echo或者printf时,可以添加输出文本的颜色设置 echo -e "Maximum \e[1;31m" $max_threads "\e[0mthreads a ...
- centos为用户添加sudo功能
su chmod a+w /etc/sudoers vim /etc/sudoers [找到root ALL=(ALL) ALL这行] 复制出新的一行,并且将root改为daniel(当前用户名) c ...
- linux中常用的命令大全
系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...
- python操作DB2数据库
#!/usr/bin/env python# -*- coding:utf-8 -*- ########################## excute python2.7.13# sudo pip ...
- 初探remoting双向通信(一)
原 初探remoting双向通信(一) 2013年06月24日 15:47:07 喜欢特别冷的冬天下着雪 阅读数 4389 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blo ...
- Python之字典推导式
推导式是个绝妙的东西,列表推导式一出,map.filter等函数黯然失色,自 Python2.7以后的版本,此特性扩展到了字典和集合身上,构建字典对象无需调用 dict 方法. bad numbers ...
- ASE——第一次结对作业
ASE--第一次结对作业 问题定义 很早就听说了MSRA的黄金点游戏,让大家写Bot来参加比赛看谁的AI比较聪明可以操盘割韭菜.深感ASE课程老师设计的任务太用心了,各种接口都准备好了,大家只用专注于 ...