2018.10.24

21 篇文章 0 订阅
10 篇文章 0 订阅

由于版权原因,不给出题面

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. 如果没有人带武器,那么只需按照损耗排序,然后依次杀即可。
  2. 如果有人带武器。
    ①我们能杀掉损耗最小的,且带武器的人。
    对于这种用情况,我们肯定不能得到它们带的武器,那么同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;
}

  1. 如果我们现在杀了一个有武器的人,并得到若 a 个武器。我们选择用这些武器去杀一些没武器的人。那么我们可以杀 a+1 个人。若我们用这 a 个武器先去杀有武器的人,则每用 1 把武器杀死 1 个,能得到最少 1 把武器,这就相当于不但杀了人,武器还莫名其妙地多了起来。显然更优。 ↩︎

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值