1.第K小数(number.cpp/c/pas)
题目描述
有两个正整数数列,元素个数分别为N和M。从两个数列中分别任取一个数
相乘,这样一共可以得到N*M个数,询问这N*M个数中第K小数是多少。
输入输出格式
输入格式:
输入文件名为number.in。
输入文件包含三行。
第一行为三个正整数N,M和K。
第二行为N个正整数,表示第一个数列。
第三行为M个正整数,表述第二个数列。
输出格式:
输出文件名为number.out。
输出文件包括一行,一个正整数表示第k小数。
输入输出样例
2 3 4
1 2
2 1 3
3
5 5 18
7 2 3 5 8
3 1 3 2 5
16
说明
1<=n,m<=200000,1<=k<=20000010000,数列元素<=10^9;
test的时候好不容易想到了二分,结果犯了几个sb错误,sort写错了,Judge函数参数传错了,还好没有爆零得了25分,GG。
两个数列(a,b)sort之后,a[i]*b[1-m]是满足单调性的,b[i]*a[1-n]也是满足单调性的,所以可以二分。
25分BUG-Code:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 200010
#define maxm 200010
ll a[maxn],b[maxm],k;
int n,m;
inline void input(ll &x) {
x=0;
ll a=1;char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
return;
}
inline void read(int &x) {
x=0;
int a=1;char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
return;
}
inline bool judge(int x) {
ll rank=0;
for(int i=1,j=m;i<=n;++i,rank+=(ll)j)
while(j>=1&&a[i]*b[j]>x) j--;
return rank<k?false:true;
}
int main() {
read(n),read(m),input(k);
for(int i=1;i<=n;++i) input(a[i]);
for(int i=1;i<=m;++i) input(b[i]);
sort(a+1,a+n+1);
sort(b+1,b+n+1);
ll l=0,r=a[n]*b[m],mid,ans;
while(l<=r) {
mid=l+r>>1;
if(judge(mid)) {
r=mid-1;
ans=mid;
} else l=mid+1;
}
cout<<ans;
return 0;
}
Judge参数改为ll,sort b数组改为b+m+1就可以A,mdzz;
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 200010
#define maxm 200010
ll a[maxn],b[maxm],k;
int n,m;
inline void input(ll &x) {
x=0;
ll a=1;char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
return;
}
inline void read(int &x) {
x=0;
int a=1;char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
return;
}
inline bool judge(ll x) {
ll rank=0,j=m;
for(int i=1;i<=n;++i,rank+=j)
while(j>=1&&a[i]*b[j]>x) j--;
return rank<k?false:true;
}
int main() {
read(n),read(m),input(k);
for(int i=1;i<=n;++i) input(a[i]);
for(int i=1;i<=m;++i) input(b[i]);
sort(a+1,a+n+1);
sort(b+1,b+m+1);
ll l=0,r=a[n]*b[m],mid,ans;
while(l<=r) {
mid=l+r>>1;
if(judge(mid)) {
r=mid-1;
ans=mid;
} else l=mid+1;
}
cout<<ans;
return 0;
}
2.dwarf tower(dwarf.cpp/c/pas)
题目描述
Vasya在玩一个叫做"Dwarf Tower"的游戏,这个游戏中有n个不同的物品,
它们的编号为1到n。现在Vasya想得到编号为1的物品。
获得一个物品有两种方式:
-
直接购买该物品,第i件物品花费的钱为ci
- 用两件其他物品合成所需的物品,一共有m种合成方式。
请帮助Vasya用最少的钱获得编号为1的物品。
输入输出格式
输入格式:
第一行有两个整数n,m(1<=n<=10000,0<=m<=100000),分别表示有n种物品以
及m种合成方式。
接下来一行有n个整数,第i个整数ci表示第i个物品的购买价格,其中
0<=ci<=10^9。
接下来m行,每行3个整数ai,xi,yi,表示用物品xi和yi可以合成物品ai,其
中(1<=ai,xi,yi<=n; ai<>xi, xi<>yi, yi<>ai)
输出格式:
一行,一个整数表示获取物品 1 的最少花费。
输入输出样例
5 3
5 0 1 2 5
5 2 3
4 2 3
1 4 5
2
说明
60%的数据,n<=100
100%的数据,n<=10000,m<=100000
T2一看上去就比较简单,自己手画了一下,感觉可以直接可以边读入边更新,最后输出w[1]就好了。不过很快我就发现了这个算法的缺陷,如果一个物品A能通过合成两个物品来减小费用,那么以前这个A更新过的费用就不是最小的,GG。苦思冥想最后想出来了一种类似于spfa的bfs的做法,开一个struct,记录x和y可以合成a,还有就是类似于存边的next和head,一开始先把所有物品push进队列里,确保每个物品都会更新,其余的就是类似于spfa的操作,这貌似是正解,本来可以A,可惜我TM入队入错了GG,还好数据水,得了40分。
40分BUG-Code
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 10010
#define maxm 100010
struct woyaoac {//忽略掉这个结构体名
int x,y,a,next;
woyaoac(int x=0,int y=0,int a=0,int next=0):
x(x),y(y),a(a),next(next) {}
}A[maxm<<1];
int cnt,w[maxn],head[maxn],n,m;
bool notinq[maxn]={0};
queue<int>q;
inline int input() {
int x=0,a=1;char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
return x*a;
}
inline void add(int x,int y,int a) {
A[++cnt]=woyaoac(x,y,a,head[x]);
head[x]=cnt;
return;
}
int lpfa() {//忽略掉这个函数名
for(int i=1;i<=n;i++) q.push(i);
while(!q.empty()) {
int x=q.front();
q.pop();
notinq[x]=true;
for(int i=head[x];i;i=A[i].next) {
int y=A[i].y,a=A[i].a;
if(w[x]+w[y]<w[a]) {
w[a]=w[x]+w[y];
if(notinq[y]==true) {
q.push(y);
notinq[y]=false;
}
}
}
}
return w[1];
}
int main() {
n=input(),m=input();
for(int i=1;i<=n;i++) w[i]=input();
for(int i=1;i<=m;i++) {
int a=input(),x=input(),y=input();
add(x,y,a);
add(y,x,a);
}
printf("%d",lpfa());
return 0;
}
看到那个if(notinq[])了吗?把y改成a就A了,GG。
#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 10010
#define maxm 100010
struct woyaoac {
int x,y,a,next;
woyaoac(int x=0,int y=0,int a=0,int next=0):
x(x),y(y),a(a),next(next) {}
}A[maxm<<1];
int cnt,w[maxn],head[maxn],n,m;
bool notinq[maxn]={0};
queue<int>q;
inline int input() {
int x=0,a=1;char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
return x*a;
}
inline void add(int x,int y,int a) {
A[++cnt]=woyaoac(x,y,a,head[x]);
head[x]=cnt;
return;
}
int lpfa() {
for(int i=1;i<=n;i++) q.push(i);
while(!q.empty()) {
int x=q.front();
q.pop();
notinq[x]=true;
for(int i=head[x];i;i=A[i].next) {
int y=A[i].y,a=A[i].a;
if(w[x]+w[y]<w[a]) {
w[a]=w[x]+w[y];
if(notinq[a]==true) {
q.push(a);
notinq[a]=false;
}
}
}
}
return w[1];
}
int main() {
n=input(),m=input();
for(int i=1;i<=n;i++) w[i]=input();
for(int i=1;i<=m;i++) {
int a=input(),x=input(),y=input();
add(x,y,a);
add(y,x,a);
}
printf("%d",lpfa());
return 0;
}
3.abcd(abcd.cpp/c/pas)
题目描述
有4个长度为N的数组a,b,c,d。现在需要你选择N个数构成数组e,数组e满足
a[i]≤e[i]≤b[i]以及
并且使得最大。
输入输出格式
输入格式:
输入文件名为abcd.in。
输入文件共 N+1 行。
第 1 行包含1个正整数N。
第 i+1 行包含4个整数a[i],b[i],c[i],d[i]。
输出格式:
输出文件名为abcd.out。
输出文件共 1 行,包含1个正整数表示所给公式的最大值,输入保证一定有解。
输入输出样例
10
1 10 1 0
-10 10 2 2
-10 10 2 2
-10 10 2 2
1 10 1 0
-10 10 2 2
-10 10 2 2
1 10 1 0
-10 10 2 2
1 10 1 0
-4
说明
对于 20%的数据,N≤10,-2≤a[i]<b[i]≤2;
对于 60%的数据,N≤50, -20≤a[i]<b[i]≤20;
对于 100%的数据,
N≤200,-25≤a[i]<b[i]≤25,1≤c[i]≤20,0≤d[i] ≤10000
人生第一道玄学DFS+卡时。稳稳当当的20分。
或许你可以学到卡时QAQ
#include<ctime>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 210
#define inf 0x7fffffff
int a[maxn],b[maxn],c[maxn],d[maxn];
int e[maxn],Ans=-inf,n,T;
inline int input() {
int x=0,a=1;char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
return x*a;
}
void dfs(int x,int ans) {
if(clock()-T>=900) {
printf("%d",Ans);
exit(0);
}
if(x==n+1) {
int sum=0;
for(int i=1;i<=n;i++) sum+=e[i]*c[i];
if(sum==0) Ans=max(Ans,ans);
else return;
} else for(int i=a[x];i<=b[x];i++) {
int t=e[x];
e[x]=i;
dfs(x+1,ans+e[x]*d[x]);
e[x]=t;
}
return;
}
int main() {
T=clock();
n=input();
for(int i=1;i<=n;i++)
a[i]=input(),b[i]=input(),c[i]=input(),d[i]=input();
dfs(1,0);
printf("%d",Ans);
return 0;
}
正解是多重背包。GG。
∑e[i]*c[i]=0 和 ans=∑b[i]*e[i]
因为e[i]=[a[i],b[i]]等价于[0,b[i]-a[i]]+a[i];
问题转化为使得 ∑(b[i]-a[i])*c[i]+∑a[i]*c[i]=0 且∑(b[i]-a[i])*d[i]+∑a[i]*d[i]最大.
∑a[i]*c[i]和∑a[i]*d[i]是可以计算的.
令V=∑a[i]*c[i](V<0),s[i]=b[i]-a[i].
问题转化为在体积为-V的背包中取物品s[i]求最大值. 可以二进制拆01背包.GG
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 210
#define maxm 100010
int a[maxn],b[maxn],c[maxn],d[maxn];
int dp[maxm],w[maxm],v[maxm];
int ans,V,cnt;
inline int input() {
int x=0,a=1;char c=getchar();
for(;c<'0'||c>'9';c=getchar())
if(c=='-') a=-1;
for(;c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
return x*a;
}
int main() {
int n=input();
for(int i=1;i<=n;i++) {
a[i]=input(),b[i]=input(),c[i]=input(),d[i]=input();
V+=a[i]*c[i];
b[i]-=a[i];
ans+=a[i]*d[i];
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=b[i];j<<=1) {
b[i]-=j;
w[++cnt]=d[i]*j;
v[cnt]=c[i]*j;
}
if(b[i]) {
w[++cnt]=d[i]*b[i];
v[cnt]=c[i]*b[i];
}
}
memset(dp,-127/3,sizeof(dp));//神TM的127
dp[0]=0,V*=-1;
for(int i=1;i<=cnt;i++)
for(int j=V;j>=v[i];j--)
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
ans+=dp[V];
printf("%d",ans);
return 0;
}
本来是100+100+20=220,结果最后25+40+20=GG。
神TM的sort,神TM的lpfa.