1.第K小数(number.cpp/c/pas)
题目描述
有两个正整数数列,元素个数分别为N和M。从两个数列中分别任取一个数
相乘,这样一共可以得到N*M个数,询问这N*M个数中第K小数是多少。
输入输出格式
输入格式:
输入文件名为number.in。
输入文件包含三行。
第一行为三个正整数N,M和K。
第二行为N个正整数,表示第一个数列。
第三行为M个正整数,表述第二个数列。
输出格式:
输出文件名为number.out。
输出文件包括一行,一个正整数表示第k小数。
输入输出样例
输入样例#1:
2 3 4 1 2 2 1 3
输出样例#1:
3
输入样例#2:
5 5 18 7 2 3 5 8 3 1 3 2 5
输出样例#2:
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 的最少花费。
输入输出样例
输入样例#1:
5 3 5 0 1 2 5 5 2 3 4 2 3 1 4 5
输出样例#1:
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个正整数表示所给公式的最大值,输入保证一定有解。
输入输出样例
输入样例#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
输出样例#1:
-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.