2017福建夏令营Day2(贪心.分治.枚举)
奇怪的道路
【问题描述】从前,有一座网格城市,城市中每个房子占据一个正方形小格子的中 心,每个正方形小格子的边长均为1。 这座城市道路的设计方式是这样的,首先,定义(𝑎)图为一个基本图 形,其阶为1,之后,将(𝑎)图中每一个房子都用一个基本图形代替,得 到(𝑏)图,那么(𝑏)图的阶即为2,再将(𝑏)图中的每一个房子都用基本图形替 代,得到阶为3的(𝑐)图,以此类推,只要知道这座城市的阶𝑛,就可以知道 它的道路设计。 这种七拐八弯的道路设计使得这座城市之间的道路交通运输相当不便, 于是该市的市长决定改造一下这座城市的道路,但在此之前他需要做一系 列的评估,比如这座网格城市中,连接第𝑖1行第𝑗1列的房屋与第𝑖2行第𝑗2列 的房屋之间(两座房屋可能相同)的道路有多长,由于这种道路设计太过 奇怪,人力难以计算,于是这个任务就交给作为软件工程师的你了。
【输入格式】 每个测试点第一行有两个正整数𝑛, 𝑇,表示城市的阶数和询问数。 接下来𝑇行,每行4个正整数𝑖1 𝑗1 𝑖2 𝑗2,表示要查询的两个房屋的坐 标。
【输出格式】 对每个询问输出一行相应的值表示答案。
【样例输入】 2 4 2 1 3 1 3 2 2 2 2 3 3 3 3 4 2 4
【样例输出】 13 11 1 3 第 3 页 共 9 页 2017福建省夏令营Day2练习 2017 年 7 月 18 日 奇怪的道路
【样例解释】 样例对应题目中的(𝑏)图。 第一个询问问的是图中编号为2的房子与编号为15的房子的距离。 第二个询问问的是图中编号为14的房子与编号为3的房子的距离。 第三个询问问的是图中编号为8的房子与编号为9的房子的距离。 第四个询问问的是图中编号为10的房子与编号为7的房子的距离。
【数据规模】 Easy:对于30%的数据,1 ≤ 𝑛 ≤ 3。 Normal:对于60%的数据,1 ≤ 𝑛 ≤ 8。 Hard:对于100%的数据,均有1 ≤ 𝑛 ≤ 15,1 ≤ 𝑖1, 𝑗1, 𝑖2, 𝑗2 ≤ 2 𝑛, 1 ≤ 𝑇 ≤ 10000。
题解
去年出过
道路之间的距离就是两个房子编号之差
发现阶之间的变换为2^n
当(x,y)在第二象限时,结果即为0×2^(2n-2)+getnum(n-1,y,x)
当(x,y)在第一象限时,结果即为1×2^(2n-2)+getnum(n-1,x,y-2^(n-1))
当(x,y)在第四象限时,结果即为2×2^(2n-2)+getnum(n-1,x-2^(n-1),y-2^(n-1))
当(x,y)在第三象限时,结果即为3×2^(2n-2)+getnum(n-1,2^(n-1)+1-y,2^n+1-x)
总之找规律然后分治
只是这个规律真心难找而已
#include<bits/stdc++.h> using namespace std; int getnum(int n,int x,int y) { if(n==0) return 1; int mid=1<<(n-1),b=mid*mid; if(x<=mid && y<=mid) return getnum(n-1,y,x); else if(x<=mid && y>mid) return b+getnum(n-1,x,y-mid); else if(x>mid && y>mid) return b+b+getnum(n-1,x-mid,y-mid); else return b+b+b+getnum(n-1,mid+1-y,mid+mid+1-x); } int main() { freopen("road.in","r",stdin); freopen("road.out","w",stdout); int n,T,i1,j1,i2,j2; scanf("%d%d",&n,&T); while(T--) { scanf("%d%d%d%d",&i1,&j1,&i2,&j2); printf("%d\n",abs(getnum(n,i1,j1)-getnum(n,i2,j2))); } }
标签
分治
黑白矩阵
【问题描述】 给定一个𝑛 × 𝑚的矩阵,每个格子有黑白两种颜色。 现在要求修改至多𝑘个格子(可以一个都不修改)的颜色使得每个同色 极大连通块都是矩形,即使得这个矩阵看上去由一块一块的矩阵构成。 如果可行的话求最小的操作次数,否则输出-1。 如果你不会判定题目中“每个同色极大连通块都是矩形”的条件,那 么这里给你一个易于实现的方法:这等价于任意一个边长为2的正方形内黑 色和白色的格子的个数均为偶数。
【输入格式】 第一行为一个正整数𝑇,表示数据组数。 接下来𝑇组数据,每组数据的第一行有三个整数𝑛, 𝑚, 𝑘,表示矩阵的大 小和最多能够修改多少个格子。 接下来𝑛行,每行𝑚个整数𝑎𝑖𝑗,描述这个矩阵,𝑎𝑖𝑗 = 1表示该格子为黑 色,𝑎𝑖𝑗 = 0则为白色。
【输出格式】 对每组数据输出单独的一行,表示答案。
【样例输入】 3 5 5 2 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 4 1 1 0 0 0 0 1 1 1 1 1 1 0 3 4 1 1 0 0 1 0 1 1 0 1 0 0 1
【样例输出】 1 -1 0
【数据规模】 Easy:对于30%的数据,有𝑛, 𝑚 ≤ 4。 Normal:对于60%的数据,有𝑛, 𝑚 ≤ 10。 Hard:对于100%的数据,有1 ≤ 𝑛, 𝑚, 𝑇 ≤ 100, 1 ≤ 𝑘 ≤ 10, 0 ≤ 𝑎𝑖𝑗 ≤ 1。
题解
可以发现第一行确定后,其他每行都只有两种状态,与第一行一样或全部相反
于是可以枚举第一行的状态,求出一样与实际修改的步数与相反与实际修改的步数,取min即可
但是当k<n时我们可以知道一定至多有k行存在被修改的格子,而剩下n-k行的格子均没有变化过
枚举1-k+1行,当做没有被修改,贪心,然后看下是否<=k即可
#include<bits/stdc++.h> using namespace std; const int N=110,M=110,Inf=0x7fffffff; int n,m,k,ans,a[N][M]={}; void init() { ans=Inf; scanf("%d%d%d",&n,&m,&k); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",&a[i][j]); } void work() { for(int i=1;i<=min(m,k+1);++i) { int s=0; for(int k=1;k<=m;++k) { int s1=0,s2=0; for(int j=1;j<=n;++j) a[j][i]==a[j][k] ? ++s1 : ++s2; s+=min(s1,s2); } ans=min(ans,s); } } void calc(int base) { int s=base; for(int i=2;i<=n;++i) { int s1=0,s2=0; for(int j=1;j<=m;++j) a[1][j]==a[i][j] ? ++s1 : ++s2; s+=min(s1,s2); } ans=min(s,ans); } void tryy(int row,int t) { if(row>m) { calc(t); return; } tryy(row+1,t); a[1][row]=!a[1][row]; tryy(row+1,t+1); a[1][row]=!a[1][row]; } int main() { freopen("table.in","r",stdin); freopen("table.out","w",stdout); int T=1; scanf("%d",&T); while(T--) { init(); m>k ? work() : tryy(1,0); printf("%d\n",(ans>k ? -1 : ans)); } fclose(stdin); fclose(stdout); return 0; }
标签
贪心
商店
【问题描述】 从前有一个奇怪的商店,一共售卖𝑘种物品,第𝑖种物品的初始价格 为𝑖。 但是这商店有个很奇怪的规矩,就是你每次购买一样物品之后,这种 物品的价格都会在当前基础上翻一倍。 现在我们想要用最少的钱从里面买𝑛样物品,不限购买的物品种数和 每种物品购买的次数,请求出若这样做,所买到的最贵的物品的价格,由 于这个数字可能过大,你只需要输出其模1000000007 = 109 + 7的结果即 可。
【输入格式】 每个测试点第一行一个整数𝑇,表示数据组数。 接下来𝑇行,每行两个正整数𝑛, 𝑘。
【输出格式】 𝑇行,第𝑖行表示第𝑖组数据的答案。
【样例输入】 5 3 1 3 2 5 3 1000000000 1 987654321 876543210
【样例输出】 4 2 4 570312504 493827168
【数据规模】 Easy:对于20%的数据,1 ≤ 𝑛 ≤ 60。 Normal:对于50%的数据,1 ≤ 𝑇 ≤ 20,1 ≤ 𝑛, 𝑘 ≤ 50000。 Hard:对于100%的数据,1 ≤ 𝑇 ≤ 2000,1 ≤ 𝑛, 𝑘 ≤ 109。
题解
50分:维护小根堆,但是由于数据太大会爆long long于是取个log放入堆,取出时q.push(log(exp(now)*2))即可
100分(复制题解)
注意到我们都是取最便宜的𝑛件商品,所以我们可以 理论上二分答案𝑎𝑛𝑠,判定方法即为统计一下不超过𝑎𝑛𝑠最多可以买几件商 品。 但是这种理论上的二分显然过不了这道题,我们需要压缩一下二分价 格的范围。 先考虑枚举我们买了多少件第𝑘种的商品,设为𝑡件,接下来我们二分 一下买了𝑡 + 1件的商品的种数,由这些信息便可以确定最贵的物品的价格, 相关的统计也十分简单,𝑂(log 𝑘)做一遍就出来了 显然,由于商品价格指数级增长的原因,在最终的方案里,初始价格 为𝑘的物品与初始价格为1的物品购买的种数不会相差太多,所以我们只需 要在𝑛/𝑘这个值的附近枚举变量𝑡即可。
代码(50)
#include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<queue> #include<cmath> #include<vector> #define re register using namespace std; double now; std::priority_queue<double,std::vector<double>,std::greater<double> > q; int T; long long n,k,ans; int main() { freopen("shop.in","r",stdin); freopen("shop.out","w",stdout); scanf("%d",&T); while(T--) { while(!q.empty()) q.pop(); ans=-1; scanf("%lld%lld",&n,&k); for(re long long i=1;i<=k;++i) q.push((double)log(i)); for(re long long i=1;i<=n;++i) { now=q.top(); q.pop(); q.push((double)log(exp(now)*2)); ans=std::max(ans,(long long)exp(now)); } printf("%lld\n",ans%1000000007); } return 0; }
标签
堆,二分