题解
今天这个题和昨天的题目完全不是一个水平的吗…orz今天这个打第三题的时间昨天还在打第一题…就不能两天的题目匀一下么…
第一题——吴翼的木棍(kusac)
【题目描述】
- 给出n条长度为1的木棍,可以在任意位置进行切割,要求木棍平均分配给m个人的情况下切的刀数最少。
- 感谢dasxxx大佬提供算法思路。
- 可以算出,每个人平均分到 n m \frac{n}{m} mn的长度。
- 可以把这些木棍接起来,然后你会发现每 n m \frac{n}{m} mn的地方来一刀效果最好。
- 但是原本这些木棍是断开的,那么原来断开的地方和你要的切点重合的地方就不用动刀子,就要减去。
- 原来要切m刀,但是会有 n ∗ m / l c m ( m , n ) n*m/lcm(m,n) n∗m/lcm(m,n)个空格是多出来的。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
inline void fff(){
freopen("kusac.in","r",stdin);
freopen("kusac.out","w",stdout);
}
int n,m;
int gcd(int a,int b){return (b==0)?a:gcd(b,a%b);}
int lcm(int a,int b){int g=gcd(a,b);return a/g*b;}
int main(){
// fff();
scanf("%d%d",&n,&m);
printf("%d",m-n*m/lcm(n,m));
return 0;
}
第二题——吴翼的书(lopov)
【题目描述】
- 给出 n n n本书,价值为 v i v_i vi,质量为 w i w_i wi。
- 给出 k k k个包,容量为 c i c_i ci,且一个包只能放一本书。
- 求最大价值是多少。
- 看上去像背包,但是其实是贪心。因为一个包只能放一本书,那么可以说在这个包的范围内装上他能装的最大的价值。
- 那么就可以把包升序排一个序,然后把书按照质量也升序排序。然后质量比当前的包的容量小的书当中取出最大价值的。
- 那就会用到堆来求得最小值。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define LL long long
using namespace std;
inline void fff(){
freopen("lopov.in","r",stdin);
freopen("lopov.out","w",stdout);
}
const int N=301000;
inline LL read(){
LL x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}
LL n,k;
struct node{
LL w,v;
bool operator < (const node x) const{
return v<x.v;
}
}a[N];
LL c[N];
priority_queue <node> HEAP;
bool cmp1(node x1,node x2){
return x1.w<x2.w;
}
LL ans=0;
int main(){
// fff();
n=read(),k=read();
for(LL i=1;i<=n;i++) a[i].w=read(),a[i].v=read();
for(LL i=1;i<=k;i++) c[i]=read();
sort(c+1,c+k+1);
sort(a+1,a+n+1,cmp1);
int tt=1;
for(LL i=1;i<=k;i++){
while(a[tt].w<=c[i]&&tt<=n) HEAP.push(a[tt++]);
if(HEAP.empty()) continue;
node temp=HEAP.top();HEAP.pop();
ans+=(LL)temp.v;
}
cout<<ans;
return 0;
}
第三题——吴翼的矩阵(ratar)
【题目描述】
- 有一个N*N 的矩阵,矩阵内元素大小 a i ∈ [ − 1000 , 1000 ] a_i\in [-1000,1000] ai∈[−1000,1000]
- 现在你要选两个子矩阵,满足:
- 1.两个子矩阵内的元素之和恰好相等。
- 2.两个子矩阵的边界(指的是矩形的边界,不是指边界的元素)恰好有一个公共点,并且不存在一个元素同时属于这两个子矩阵。
- 求方案数。
- 做法一:暴力枚举两个矩阵的左上右下节点求出和(前缀和),复杂度 O ( n 8 ) O(n^8) O(n8)
- 做法二:发现会有一个节点是公共节点,可以减少枚举一个点 O ( n 6 ) O(n^6) O(n6)
- 做法三。由于要求出和相等的矩阵,那么可以枚举固定点,然后枚举左上的矩阵,统计相同和的矩阵个数,再枚举右下矩阵的, a n s = ∑ n u m [ s u m ] ans=\sum num[sum] ans=∑num[sum]。复杂度就是降到了 O ( 6 ∗ n 4 ) O(6*n^4) O(6∗n4)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
inline void fff(){
freopen("ratar.in","r",stdin);
freopen("ratar.out","w",stdout);
}
const int N=55;
const int MAXN=2500000;
int n;
int mp[N][N],sum[N][N];
int mmp[5000100];
vector <pair<point,point> > v[10000000];
inline int get_sum(int x1,int y1,int x2,int y2){
return sum[x1][y1]+sum[x2-1][y2-1]-sum[x1][y2-1]-sum[x2-1][y1];
}
int ans=0;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&mp[i][j]);
memset(sum,0,sizeof(sum));
for(int x1=1;x1<=n;x1++)
for(int y1=1;y1<=n;y1++)
sum[x1][y1]=sum[x1-1][y1]+sum[x1][y1-1]-sum[x1-1][y1-1]+mp[x1][y1];
int k=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
for(int x=1;x<=i;x++)
for(int y=1;y<=j;y++)
mmp[get_sum(i,j,x,y)+MAXN]++;
for(int x=i+1;x<=n;x++)
for(int y=j+1;y<=n;y++)
ans+=mmp[get_sum(x,y,i+1,j+1)+MAXN];
for(int x=1;x<=i;x++)
for(int y=1;y<=j;y++)
mmp[get_sum(i,j,x,y)+MAXN]--;
for(int x=1;x<=i;x++)
for(int y=j;y<=n;y++)
mmp[get_sum(i,y,x,j)+MAXN]++;
for(int x=i+1;x<=n;x++)
for(int y=1;y<j;y++)
ans+=mmp[get_sum(x,j-1,i+1,y)+MAXN];
for(int x=1;x<=i;x++)
for(int y=j;y<=n;y++)
mmp[get_sum(i,y,x,j)+MAXN]--;
}
cout<<ans;
return 0;
}