巧克力棒(chocolate)
Time Limit:1000ms Memory Limit:64MB
题目描述
LYK 找到了一根巧克力棒,但是这根巧克力棒太长了, LYK 无法一口吞进去。
具体地,这根巧克力棒长为 n,它想将这根巧克力棒折成 n 段长为 1 的巧克力棒,然后
慢慢享用。
它打算每次将一根长为 k 的巧克力棒折成两段长为 a 和 b 的巧克力棒,此时若 a=b,则
LYK 觉得它完成了一件非常困难的事,并会得到 1 点成就感。
LYK 想知道一根长度为 n 的巧克力棒能使它得到最多几点成就感。
输入格式(chocolate.in)
第一行一个数 n。
输出格式(chocolate.out)
一个数表示答案。
输入样例
7
输出样例
4
数据范围
对于 20%的数据 n<=5。
对于 50%的数据 n<=20。
对于 80%的数据 n<=2000。
对于 100%的数据 n<=1000000000。
样例解释
将 7 掰成 3+4,将 3 掰成 1+2,将 4 掰成 2+2 获得 1 点成就感,将剩下的所有 2 掰成 1+1
获得 3 点成就感。总共 4 点成就感。
/* 发现ans=n-c(n)。c(n)表示n的二进制中1的个数。 粘一个严谨的证明(其实我自己也没认真看) 我们对c(n)进行归纳。 当c(n)=1时,即n=2^k,ans(n)显然=(n-1)。 我们假设c(n)<=x的情况下ans(n)=n-c(n)都成立。 当c(n)=x+1时,我们要证明ans(n)=n-c(n)。 令j为不超过n的2的幂次的最大值, 有ans(n)=ans(n-j)+ans(j)=n-j-(c(n)-1)+j-1=n-c(n)。 即ans(n)的下界为n-c(n)。 将ans(n)分成两个数i,j时有c(i)+c(j)>=c(n)。 当i不等于j时,有ans(n)<=i-c(i)+j-c(j)<=n-c(n)。 当i=j时,有c(i)+c(j)=2*c(n), ans(n)<=i-c(i)+j-c(j)+1<=n-c(n)*2+1,c(n)是正整数。 综上,ans(n)的上界也为n-c(n)。假设成立 */ #include<iostream> #include<cstdio> #include<cstring> using namespace std; int n,ans; int init() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int main() { freopen("chocolate.in","r",stdin); freopen("chocolate.out","w",stdout); n=init(); for(int i=1;n;i*=2) { if(n&1)ans+=i-1; n>>=1; } cout<<ans<<endl; return 0; }
LYK 快跑! (run)
Time Limit:5000ms Memory Limit:64MB
题目描述
LYK 陷进了一个迷宫!这个迷宫是网格图形状的。 LYK 一开始在(1,1)位置,出口在(n,m)。
而且这个迷宫里有很多怪兽,若第 a 行第 b 列有一个怪兽,且此时 LYK 处于第 c 行 d 列,此
时这个怪兽对它的威胁程度为|a-c|+|b-d|。
LYK 想找到一条路径,使得它能从(1,1)到达(n,m),且在途中对它威胁程度最小的怪兽的
威胁程度尽可能大。
当然若起点或者终点处有怪兽时,无论路径长什么样,威胁程度最小的怪兽始终=0。
输入格式(run.in)
第一行两个数 n,m。
接下来 n 行,每行 m 个数,如果该数为 0,则表示该位置没有怪兽,否则存在怪兽。
数据保证至少存在一个怪兽。
输入格式(run.out)
一个数表示答案。
输入样例
3 4
0 1 1 0
0 0 0 0
1 1 1 0
输出样例
1
数据范围
对于 20%的数据 n=1。
对于 40%的数据 n<=2。
对于 60%的数据 n,m<=10。
对于 80%的数据 n,m<=100。
对于 90%的数据 n,m<=1000。
对于另外 10%的数据 n,m<=1000 且怪兽数量<=100。
/* 没清空队列 WA了两个点...... 首先预处理出每个点到最近的怪物的距离 BFS即可(刚开始把所有的怪物全部进队然后bfs) 然后二分答案求解 */ #include<iostream> #include<cstdio> #include<cstring> #include<queue> #define maxn 1010 using namespace std; int n,m,l,r,ans; int dis[maxn][maxn],f[maxn][maxn]; int b[5]={0,0,1,0,-1}; int c[5]={0,1,0,-1,0}; queue<int>qx,qy; int init() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } void bfs() { while(!qx.empty()) { int nx=qx.front(); int ny=qy.front(); qx.pop();qy.pop(); for(int i=1;i<=4;i++) { int tx=nx+b[i]; int ty=ny+c[i]; if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&!f[tx][ty]) { f[tx][ty]=1; dis[tx][ty]=dis[nx][ny]+1; qx.push(tx); qy.push(ty); } } } } int judge(int x) { if(dis[1][1]<x)return 0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) f[i][j]=0; while(!qx.empty()) qx.pop(); while(!qy.empty()) qy.pop(); qx.push(1); qy.push(1); f[1][1]=1; while(!qx.empty()) { int nx=qx.front(); int ny=qy.front(); qx.pop();qy.pop(); if(nx==n&&ny==m)return 1; for(int i=1;i<=4;i++) { int tx=nx+b[i]; int ty=ny+c[i]; if(tx>=1&&tx<=n&&ty>=1&&ty<=m&&dis[tx][ty]>=x&&!f[tx][ty]) { f[tx][ty]=1; qx.push(tx); qy.push(ty); } } } return 0; } int main() { freopen("run.in","r",stdin); freopen("run.out","w",stdout); n=init();m=init(); memset(dis,127/3,sizeof(dis)); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { int x;x=init(); if(x==1) { dis[i][j]=0; qx.push(i); qy.push(j); f[i][j]=1; } } } bfs(); l=0,r=3000; while(l<=r) { int mid=(l+r)/2; if(judge(mid)) { ans=mid; l=mid+1; } else r=mid-1; } printf("%d ",ans); return 0; }
仙人掌(cactus)
Time Limit:1000ms Memory Limit:64MB
题目描述
LYK 在冲刺清华集训( THUSC)!于是它开始研究仙人掌,它想来和你一起分享它最近
研究的结果。
如果在一个无向连通图中任意一条边至多属于一个简单环(简单环的定义为每个点至多
经过一次),且不存在自环,我们称这个图为仙人掌。
LYK 觉得仙人掌还是太简单了,于是它定义了属于自己的仙人掌。
定义一张图为美妙的仙人掌,当且仅当这张图是一个仙人掌且对于任意两个不同的点 i,j,
存在一条从 i 出发到 j 的路径,且经过的点的个数为|j-i|+1 个。
给定一张 n 个点 m 条边且没有自环的图, LYK 想知道美妙的仙人掌最多有多少条边。
数据保证整张图至少存在一个美妙的仙人掌。
输入格式(cactus.in)
第一行两个数 n,m 表示这张图的点数和边数。
接下来 m 行,每行两个数 u,v 表示存在一条连接 u,v 的无向边。
输出格式(cactus.out)
一个数表示答案
输入样例
4 6
1 2
1 3
1 4
2 3
2 4
3 4
输出样例
4
样例解释
选择边 1-2,1-3,2-3,3-4,能组成美妙的仙人掌,且不存在其它美妙仙人掌有超过 4 条
边。
数据范围
对于 20%的数据 n<=3。
对于 40%的数据 n<=5。
对于 60%的数据 n<=8。
对于 80%的数据 n<=1000。
对于 100%的数据 n<=100000 且 m<=min(200000,n*(n-1)/2)。
/* 定义一张图为美妙的仙人掌,当且仅当这张图是一个仙人掌 且对于任意两个不同的点 i,j,存在一条从 i 出发到 j 的 路径,且经过的点的个数为|j-i|+1 个。 对于i和i+1要经过2个点即只能是这两个点之间直接有一条边 这n-1条连接i和i+1的边一定选 选完这些边之后正好满足了 任意两点之间的要求 现在即在保证是仙人掌的前提下多选边 接下来选的边要保证没有交集 因为现在已经选了i到i+1的边 其他的边会和现在已经选的边形成环 若选两个有交集的边 则不满足仙人掌的性质 所以问题就转化成了线段覆盖...... */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 200010 using namespace std; int n,m,tot,ans; struct edge { int x; int y; }e[maxn*2]; int init() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int cmp(edge a,edge b) { return a.y<b.y; } int main() { freopen("cactus.in","r",stdin); freopen("cactus.out","w",stdout); n=init();m=init(); for(int i=1;i<=m;i++) { int x,y;x=init();y=init(); if(x>=y)swap(x,y); if(x+1==y)continue; /* 好像不能直接continue 会有重边 比如 4 6 1 2 1 2 2 3 2 3 3 4 3 4 应该是6吧 但这样是3 但没有这样的数据 所以不管了 */ tot++;e[tot].x=x;e[tot].y=y; } sort(e+1,e+tot+1,cmp); int end=-1; for(int i=1;i<=tot;i++) { if(e[i].x>=end) { end=e[i].y; ans++; } } printf("%d ",ans+n-1); return 0; }