T1.小 Z 切课本(cut)
小 Z 厌恶数学, 他决定将数学课本切成一块一块的。 他的课本是一个 n*m 的
矩形,小 Z 决定切 K 刀,每刀他可以横着切或者竖着切,但是切成的矩形的长和
宽都必须是整数。 当然, 小 Z 不会做出两次相同的操作。
不巧的是,小 Z 的数学老师知道了他这个行为,并且刁钻的老师肯定会找到
切出的矩形中面积最小的那一块来 D 他,所以小 Z 想知道最优情况下面积最小
的那一块面积最大能是多少?
[输入格式]
从 cut.in 中读取数据。
输入数据只包含一行三个整数 n,m,k,含义如题目所述。
[输出格式]
输出一个数字,表示答案。如果没有合法的方案,输出-1。
[样例输入]
6 4 2
[样例输出]
8
[数据范围与约定]
本题采用子任务制, 你只有通过一个 subtask 下的所有测试点才能得到对应的分
数。
Subtask1 : 包含 20Points 满足 n,m<=10
Subtask2 : 包含 20Points 满足 n,m<=5000
Subtask3 : 包含 20Points 满足 n,m<=10^7
Subtask4 : 包含 40Points 满足 n,m<=10^9
对于所有数据,满足 n,m,k>=1, k<=10^9
solution:如果能只切一边的话,只切一边一定最优。
假设 n<=m,k>=m 时, 考虑将一边全部切开, 剩下的平均切到另一边。 长的边切 m-1 刀,
另一边切 k-(m-1)刀一定最优因为,n/(k-(n-1))>=m/(k-(m-1))
二分也可以做,只不过复杂度较高。
#include<iostream> #include<cstdio> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m,k;long long ans=0; int main() { freopen("cut.in","r",stdin); freopen("cut.out","w",stdout); n=read();m=read();k=read(); if(n+m-2<k) return 0*puts("-1"); for(int i=1,last;i<=n;i=last+1) { last=n/(n/i);int j=min(last,k+1); if(j>=i) ans=max(ans,1LL*(n/j)*(m/(k-(j-1)+1))); } printf("%lld ",ans); return 0; }
T2.小 Z 爱数组(array)
小 Z 最喜欢数组了,现在他得到了由 n 个正整数组成的数组 ai,他想构造
一个相同大小的正整数数组 bi 满足两个数组的差异度∑abs(ai-bi)
最小。 特殊的
是,bi 数组的所有元素必须满足两两互质。
[输入格式]
从 array.in 中读取数据。
第一行一个数 n,表示数组大小。
接下来一行 n 个正整数 ai,表示给定的数组。
[输出格式]
输出一行 n 个正整数 bi,表示答案。
输出的数字必须满足 1<=bi<=10^9。如果有多个答案,你可以输出任意一个。
[样例输入]
5
1 6 4 2 8
[样例输出]
1 5 4 1 9
[样例解释]
差异度为 3,可以证明是最小的差异度。还有其它可行的方案。
[数据范围与约定]
本题采用子任务制,你只有通过一个子任务所有的数据才能得到对应的分数。
Subtask1:包含 20Points,满足 n<=8 1<=ai<=5
Subtask2:包含 30Points,满足 n<=50 1<=ai<=15
Subtask3:包含 50Points,满足 n<=100 1<=ai<=30
t2真的难,所以贴标程。
原题 ai 不超过 30,所以选择的 bi 肯定小于 59,不然可以用 1 代替。
#include<iostream> #include<cstring> #include<cstdio> #define MN 100 using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int p[16]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53}; int f[MN+5][1<<16],from[MN+5][1<<16],n,a[MN+5],bit[MN+5],q[MN+5]; void R(int&x,int&y,int X,int Y){X<x?(x=X,y=Y):0;} inline int abs(int x){return x<0?-x:x;} int main() { freopen("array.in","r",stdin); freopen("array.out","w",stdout); n=read(); for(int i=1;i<=n;++i) a[i]=read(); memset(f,63,sizeof(f));f[0][0]=0; for(int i=1;i<59;++i) for(int j=0;j<16;++j) if(i%p[j]==0) bit[i]|=1<<j; for(int i=1;i<=n;++i) for(int j=0;j<1<<16;++j) if(f[i-1][j]<1e9) for(int k=1;k<59;++k) if(!(bit[k]&j)) R(f[i][j|bit[k]],from[i][j|bit[k]],f[i-1][j]+abs(k-a[i]),k); int mn=1e9,fr=0; for(int i=0;i<1<<16;++i) if(f[n][i]<mn) mn=f[n][i],fr=i; for(int i=n,j=fr;i;j^=bit[from[i--][j]]) q[i]=from[i][j]; for(int i=1;i<=n;++i) printf("%d ",q[i]); return 0; }
58 以内只有 16 个质数,所以将这些质数的状态压起来之后 dp 即可,复杂度 O(2^16*n*58)
T3.小 Z 爱修路(road)
L 国包含 n 个城市和 m 条双向道路, 第 i 条道路连接 ui,vi 两个城市, 距离为
ti,这些道路将所有 n 个城市连接在一起。明年,L 国将会在首都,也就是 1 号
城市举办一年一度的 noi,所以 L 国的国王委托小 Z 新建一些道路来减少一些城
市到达首都的距离。小 Z 很快修好了道路,但是他却不是很满意。他想知道最多
可以少新建多少道路,满足首都到所有城市的最短路长度和现在相同。
[输入格式]
从 road.in 中读取数据。
第一行读入三个数字 n,m,k,依次表示城市的数量,原有道路的数量和新建道路
的数量。
接下来 m 行,每行三个数字 ui,vi,ti,表示一条原有的道路
最后 k 行,每行两个数字 si,wi,表示一条新建的道路连接 1 和 si,距离为 wi。
[输出格式]
输出一个整数,表示最多能少修建多少条新建的道路。
[样例输入]
3 2 2
1 2 1
2 3 1
2 2
3 1
[样例输出]
1
[数据范围与约定]
对于 20%的数据,满足 n,m,k<=100
对于另外 20%的数据,满足 k<=4
对于 100%的数据,满足 n,k<=50000 , m<=200000 , 1<=ti,wi<=10^9 , si>=2
solution:
把所有新加的边去个重,到相同的点的边只保留一个最小的,然后跑一次最短路。一条新加
的边如果长度不是最短路一定可以去掉, 否则只有满足有其它最短路径到达这个点的时候才
可以去掉。算出到每个点最短路条数是否大等于 2 即可。
复杂度 O((n+k)logn)。
注意去重!!
#include<iostream> #include<cstring> #include<cstdio> #include<queue> #define pa pair<long long,int> #define MN 100000 using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } priority_queue<pa,vector<pa>,greater<pa> >q; int n,m,K,head[MN+5],cnt=0,mn[MN+5],ans=0;long long d[MN+5],num[MN+5]; struct edge{int to,next,w;}e[MN*8+5]; inline void ins(int f,int t,int w) { e[++cnt]=(edge){t,head[f],w};head[f]=cnt; e[++cnt]=(edge){f,head[t],w};head[t]=cnt; } void Dij() { memset(d,63,sizeof(d));q.push(make_pair(d[1]=0,1));num[1]=1; while(!q.empty()) { while(!q.empty()&&d[q.top().second]!=q.top().first) q.pop(); if(q.empty()) return; int x=q.top().second;q.pop(); for(int i=head[x];i;i=e[i].next) if(d[x]+e[i].w<d[e[i].to]) q.push(make_pair(d[e[i].to]=d[x]+e[i].w,e[i].to)),num[e[i].to]=num[x]; else if(d[x]+e[i].w==d[e[i].to]) num[e[i].to]=min(num[e[i].to]+num[x],(long long)1e9); } }