文章目录
由于版权原因,不给出题面
T1
分析:
第一题可以用数据结构做,但是也是可以用离线算法,毕竟会好写一点。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
char FIL[100000],*FIL1_=FIL,*FIL2_=FIL;
#define getchar() ((FIL1_==FIL2_&&(FIL2_=(FIL1_=FIL)+fread(FIL,1,100000,stdin)),FIL1_==FIL2_)?EOF:*FIL1_++)
const int maxn=1e5+5;
struct js{
int L,R,h,id;
bool operator <(const js&b)const{return h<b.h;}
}a[maxn];
struct js1{
int x,s;
bool operator <(const js1&b)const{return s<b.s;}
}p[maxn];
int c[maxn],ans[maxn],n,m;
void add(int x){while (x<=n+1) ++c[x],x+=x&-x;}
int get(int x){int s=0;while (x) s+=c[x],x-=x&-x;return s;}
int rad()
{
int ret=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
int main()
{
freopen("query.in","r",stdin);
freopen("query.out","w",stdout);
int T=rad();
while (T--)
{
n=rad();m=rad();
for (int i=1;i<=n;++i) p[i]=(js1){i,rad()};
for (int i=1;i<=m;++i) a[i]=(js){rad(),rad(),rad(),i};
sort(p+1,p+n+1);sort(a+1,a+m+1);
int now=1;
while (a[now].h<p[1].s&&now<=m) ans[a[now].id]=0,++now;
for (int i=1,j;i<=n;i=j)
{
j=i;
while (p[i].s==p[j].s&&j<=n) add(p[j].x+1),++j;
while (a[now].h<p[j].s&&now<=m) ans[a[now].id]=get(a[now].R+1)-get(a[now].L),++now;
}
while (now<=m) ans[a[now].id]=get(a[now].R+1)-get(a[now].L),++now;
for (int i=1;i<=m;++i) printf("%d\n",ans[i]);
}
return 0;
}
T2
分析:
这题是贪心。
对于答案可以分成两种情况讨论。
- 如果没有人带武器,那么只需按照损耗排序,然后依次杀即可。
- 如果有人带武器。
①我们能杀掉损耗最小的,且带武器的人。
对于这种用情况,我们肯定不能得到它们带的武器,那么同1.的处理方式即可。
②我们不能杀掉损耗最小的带武器的人。
这种情况下,我们可能不用武器,只是和1.一样一通杀就好了。
当然我们也可能需要用到武器。
如果我们需要用到,那么我们只杀损耗最小的人就好了。接下来我们通过武器杀死所有有武器的人。[证明这样是最优的1]
如果所有人的武器共有 B 把,若 B+1 >= n,那么我们一定能杀死 n 个人。
否则用这些武器,出去已经杀死的那个人,按照损耗值从大到小的顺序杀,剩余的人按照又回到 1. 的规则了。
我想,这样得到的一定是最优解吧!
代码
//这次代码完全不敢缩行之类的扫操作,因为调了整整一个下午……
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
char FIL[100000],*FIL1_=FIL,*FIL2_=FIL;
#define getchar() ((FIL1_==FIL2_&&(FIL2_=(FIL1_=FIL)+fread(FIL,1,100000,stdin)),FIL1_==FIL2_)?EOF:*FIL1_++)
const int maxn=1e5+5;
int ans1,ans2,sum1,sum2,m1,m2,L,R;
long long B;
int a[maxn],b[maxn],n,m;
int rad()
{
int ret=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
void work()
{
if (L&&m2>=a[1])
{
m2-=a[1];
if (B+1>=n) {sum2=n;return;}
sum2=1+B;
int i=2,j=1;
for (int k=1,t=n-1-B;k<n&&t;--t,++k)
if (i<=L&&(a[i]<b[j]||j>R))
{
if (m2<a[i]) break;
m2-=a[i];
++sum2;
++i;
}else
{
if (m2<b[j]) break;
m2-=b[j];
++sum2;
++j;
}
}
}
int main()
{
freopen("assassin.in","r",stdin);
freopen("assassin.out","w",stdout);
int T=rad();
while (T--)
{
n=rad();m=rad();
m1=m2=m;L=R=0;B=0;
for (int i=1;i<=n;++i)
{
int x=rad(),y=rad();
if (y) a[++L]=x,B+=y;
else b[++R]=x;
}
sort(a+1,a+L+1);
sort(b+1,b+R+1);
int i=1,j=1;
sum1=sum2=0;
while (true)
{
if (i<=L&&(a[i]<b[j]||j>R))
{
if (m1<a[i]) break;
m1-=a[i];
++sum1;
++i;
if (sum1==n) break;
}else
{
if (m1<b[j]) break;
m1-=b[j];
++sum1;
++j;
if (sum1==n) break;
}
}
work();
if (sum1<sum2||(sum1==sum2&&m1<m2)) sum1=sum2,m1=m2;
printf("%d %d\n",sum1,m-m1);
}
return 0;
}
T3
最后一题是某题的弱化版,但是我还是要按照原版来做。
好吧,一翻开提交记录挺尴尬的,洛谷做过原题,而且是一种完全看不懂的解法(⊙﹏⊙b汗)……可见过去的我还是比较强势的。再次感慨……
分析:
第一步就是把名次抽象成线段。(自动过滤不合法的排名,即a+b>n-1)
加入我们按照分数排序,对于第 i 个人,比它低的有 a 个,比它高的有 b 个,由此可以得到,第 a+1 个人到第 n-b个人是和他分数相等的。
这样我们就把它的排名抽象成了一条线段。
我们要使每个人的排名都在自己线段的范围内且不与别人排名相等。
那么就可以定义
f
[
i
]
f[i]
f[i] 为前 i 名人(按分数排序)的最优解。
f
[
i
]
=
m
a
x
{
f
[
j
]
+
c
[
j
]
[
j
]
}
f[i]=max\{f[j]+c[j][j]\}
f[i]=max{f[j]+c[j][j]}
其中
j
∈
[
0
,
i
)
j\in [0,i)
j∈[0,i),
c
[
L
]
[
R
]
c[L][R]
c[L][R] 表示排名在
L
L
L 到
R
R
R 范围的人数。(这个数目不可能超过R-L+1)
O
(
n
2
)
O(n^2)
O(n2)转移。这样对于这题的数据范围就已经足够了。
终于看懂之前写的代码了。
实际上就是用树状数组优化,同时吧 f 数组省略了。
没懂就直接看代码吧。
代码:
by今天的我
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
char FIL[100000],*FIL1_=FIL,*FIL2_=FIL;
#define getchar() ((FIL1_==FIL2_&&(FIL2_=(FIL1_=FIL)+fread(FIL,1,100000,stdin)),FIL1_==FIL2_)?EOF:*FIL1_++)
const int maxn=5e2+5;
int n,c[maxn][maxn],f[maxn];
int rad()
{
int ret=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
int main()
{
freopen("group.in","r",stdin);
freopen("group.out","w",stdout);
int T=rad();
while (T--)
{
n=rad();
memset(f,0,sizeof f);
memset(c,0,sizeof c);
for (int i=1;i<=n;i++)
{
int x=rad(),y=rad();
if (x+y<n)
{
y=n-y;
if (c[x][y]!=y-x) c[x][y]++;
}
}
for (int i=0;i<n;i++)
{
f[i+1]=max(f[i+1],f[i]);
for (int j=i+1;j<=n;j++) if (c[i][j])
if (c[i][j]&&f[j]<f[i]+c[i][j]) f[j]=f[i]+c[i][j];
}
printf("%d\n",f[n]);
}
return 0;
}
by过去的我
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1e5+5;
int n,m,ans,f[maxn];
struct js{
int a,b,w;
bool operator <(const js &c)const{return a<c.a||(a==c.a&&b<c.b);}
}p[maxn];
int rad()
{
int ret=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
return ret*f;
}
void add(int x,int dat){while (x<=n) f[x]=max(f[x],dat),x+=x&-x;}
int get(int x){int su=0;while (x>=1) su = max(f[x],su), x-=x&-x;return su;}
int main()
{
n=rad();
for (int i=1;i<=n;++i)
{
p[i].a=rad()+1;p[i].b=n-rad();
if (p[i].a<=p[i].b){++m;p[m]=p[i];p[m].w=1;}
}
sort(p+1,p+m+1);
int ans=0,N=1;
for (int i=2;i<=m+1;++i)
if (p[i].a!=p[i-1].a||p[i].b!=p[i-1].b)
{
p[N].w=min(p[N].w,p[N].b-p[N].a+1);
add(p[N].b,get(p[N].a-1)+p[N].w);
p[++N]=p[i];
}
else ++p[N].w;
printf("%d",n-get(n));
return 0;
}
如果我们现在杀了一个有武器的人,并得到若 a 个武器。我们选择用这些武器去杀一些没武器的人。那么我们可以杀 a+1 个人。若我们用这 a 个武器先去杀有武器的人,则每用 1 把武器杀死 1 个,能得到最少 1 把武器,这就相当于不但杀了人,武器还莫名其妙地多了起来。显然更优。 ↩︎