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
题解
如果能只切一边的话,只切一边一定最优。
假设 n<=m, k>=m 时,考虑将一边全部切开,剩下的平均切到另一边。 长的边切 m-1 刀,
另一边切 k-(m-1)刀一定最优因为, n/(k-(n-1))>=m/(k-(m-1))。
#include<cstdio> long long n,m,k; int main(){ freopen("cut.in","r",stdin); freopen("cut.out","w",stdout); scanf("%lld%lld%lld",&n,&m,&k); if(n+m-2<k){ puts("-1");return 0; } if(n>m){ long long t=n;n=m;m=t; } if(m>=k+1){ if(n/(k+1)*m>m/(k+1)*n) printf("%lld",n/(k+1)*m); else printf("%lld",m/(k+1)*n); } else{ if(m/(k-(n-1)+1)>n/(k-(m-1)+1)) printf("%lld",m/(k-(n-1)+1)); else printf("%lld",n/(k-(m-1)+1)); } return 0; }
T2.小 Z 爱数组(array)
小 Z 最喜欢数组了,现在他得到了由 n 个正整数组成的数组 ai,他想构造
一个相同大小的正整数数组 bi 满足两个数组的差异度∑ |ai - bi|最小。特殊的
是, bi 数组的所有元素必须满足两两互质。
题解
原题 ai 不超过 30,所以选择的 bi 肯定小于 59,不然可以用 1 代替。
58 以内只有 16 个质数,所以将这些质数的状态压起来之后 dp 即可,复杂度 O(2^16*n*58)
T3.小 Z 爱修路
把所有新加的边去个重,到相同的点的边只保留一个最小的,然后跑一次最短路。一条新加
的边如果长度不是最短路一定可以去掉,否则只有满足有其它最短路径到达这个点的时候才
可以去掉。算出到每个点最短路条数是否大等于 2 即可。
复杂度 O((n+k)logn)
还没打,先贴标程。
#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; }
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
把所有新加的边去个重,到相同的点的边只保留一个最小的,然后跑一次最短路。一条新加
的边如果长度不是最短路一定可以去掉,否则只有满足有其它最短路径到达这个点的时候才
可以去掉。算出到每个点最短路条数是否大等于 2 即可。
复杂度 O((n+k)logn)
#include<cstdio> #include<cstring> struct edge{ int to,w,nx; }e[5000001]; int q[5000001],he,ta; int h[50010],p; int n,m,k; void ae(int fr,int to,int w){ e[++p]=(edge){to,w,h[fr]};h[fr]=p; } long long d[50010]; int vis[50010],can[50010]; void spfa(){ for(int i=1;i<=n;i++) d[i]=0x3f3f3f3f3f3f3f3f; d[1]=0; q[++ta]=1; for(int i=1,x,y;i<=k;i++){ scanf("%d%d",&x,&y); if(d[x]>y){ d[x]=y,can[x]=1; if(vis[x]==0){ vis[x]=1; q[++ta]=x; } } } while(he<ta){ int u=q[++he]; vis[u]=0; for(int i=h[u];i;i=e[i].nx){ int v=e[i].to,dis=e[i].w; if(d[v]>=d[u]+dis&&can[v]) can[v]=0; if(d[v]>d[u]+dis){ d[v]=d[u]+dis; if(vis[v]==0){ vis[v]=1; q[++ta]=v; } } } } for(int i=1;i<=n;i++)k-=can[i]; printf("%d ",k); } int main(){ freopen("road.in","r",stdin); freopen("road.out","w",stdout); scanf("%d%d%d",&n,&m,&k); for(int u,v,w,i=1;i<=m;i++)scanf("%d%d%d",&u,&v,&w),ae(u,v,w),ae(v,u,w); spfa(); return 0; }
爆int爆得好爽。。。