JOISC2019游记

这篇博客记录了作者参加JOISC2019竞赛的经历,详细介绍了Day1到Day4的题目类型和解决方案,涉及二维数点、动态规划、图论和区间操作等多种算法。通过题目解析和代码展示,分享了如何解决这些算法问题的思路和技巧。
摘要由CSDN通过智能技术生成

不定时更新(咕咕咕
UPD:3.28 Day1,2补完了
UPD:6.22 Day4被出成模拟赛了。。。所以补了上来

Day1

試験 (Examination)

Description

有n个学生,每个学生用二元组(Si,Ti)表示
有q组询问,每组询问给出三元组(Ai,Bi,Ci),问有多少个学生满足Si>=Ai,Ti>=Bi且Si+Ti>=Ci
n,q<=10^5

Solution

签到题,三维数点

Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

int read() {
   
	char ch;
	for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
	int x=ch-'0';
	for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x;
}

const int N=2e5+5;

int n,m,a[N],an[N],w;

struct P{
   int x,y,z,id;}p[N];

bool cmp(P a,P b) {
   
	if (a.x!=b.x) return a.x>b.x;
	if (a.y!=b.y) return a.y>b.y;
	if (a.z!=b.z) return a.z<b.z;
	return a.id<b.id;
}

bool cmp1(P a,P b) {
   return a.y>b.y;}

int tr[N];
void I(int x) {
   for(;x<=w;x+=x&-x) tr[x]++;}
int Q(int x) {
   int ret=0;for(;x;x-=x&-x) ret+=tr[x];return ret;}
void D(int x) {
   for(;x<=w;x+=x&-x) tr[x]=0;}

void solve(int l,int r) {
   
	if (l==r) return;
	int mid=l+r>>1;
	solve(l,mid);solve(mid+1,r);
	int j=l-1;
	fo(i,mid+1,r) {
   
		while (j<mid&&p[j+1].y>=p[i].y) {
   j++;if (!p[j].id) I(p[j].z);}
		if (p[i].id) {
   
			an[p[i].id]+=Q(p[i].z);
			p[i].id=p[i].id;
		}
	}
	fo(i,l,mid) if (!p[i].id) D(p[i].z);
	sort(p+l,p+r+1,cmp1);
}

int main() {
   
	n=read();m=read();
	fo(i,1,n) p[i].x=read(),p[i].y=read(),p[i].z=p[i].x+p[i].y;
	fo(i,1,m) p[i+n].x=read(),p[i+n].y=read(),p[i+n].z=read(),p[i+n].id=i;
	fo(i,1,n+m) a[++w]=p[i].z;
	sort(a+1,a+w+1);w=unique(a+1,a+w+1)-a-1;
	fo(i,1,n+m) p[i].z=w-(lower_bound(a+1,a+w+1,p[i].z)-a)+1;
	sort(p+1,p+n+m+1,cmp);solve(1,n+m);
	fo(i,1,m) printf("%d\n",an[i]);
	return 0;
}

ナン (Naan)

Description

有n个人在分一块naan(印度薄饼?),naan可以被分为L段长度为1的小块
这n个人打算在naan上切n-1刀,分成n段分别拿走
第i个人拿长度为1的第j段会获得V[i][j]的愉悦值
你需要给出一种方案,使得第i个人最终获得的愉悦值>= ∑ V i , j L \sum{V_{i,j}\over L} LVi,j
n<=2000,L<=2000

Solution

预处理每个人将naan分成n段,每段愉悦值相等的切割点
然后每次贪心找当前最小的切割点切就好了
正确性显然

Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define mp(a,b) make_pair(a,b)
using namespace std;

typedef long long ll;
typedef pair<ll,ll> pr;

const int N=2e3+5;

int n,L,v[N][N],id[N];
pr p[N][N],an[N];
bool vis[N];

bool small(pr a,pr b) {
   return (long double)a.first*b.second<(long double)b.first*a.second;}

int main() {
   
	scanf("%d%d",&n,&L);
	fo(i,1,n) fo(j,1,L) scanf("%d",&v[i][j]);
	fo(i,1,n) {
   
		int sum=0,now=0;
		fo(j,1,L) sum+=v[i][j];
		int k=1;
		fo(j,1,n) {
   
			while (k<L&&(ll)(now+v[i][k])*n<(ll)sum*j) now+=v[i][k++];
			ll a=(ll)(k-1)*n*v[i][k]+(ll)sum*j-(ll)now*n;
			ll b=(ll)n*v[i][k];ll d=__gcd(a,b);
			p[i][j]=mp(a/d,b/d);
		}
	}
	fo(i,1,n) {
   
		an[i]=mp(1,0);id[i]=0;
		fo(j,1,n) if (!vis[j]&&small(p[j][i],an[i])) an[i]=p[j][i],id[i]=j;
		vis[id[i]]=1;
	}
	fo(i,1,n-1) printf("%lld %lld\n",an[i].first,an[i].second);
	fo(i,1,n) printf("%d ",id[i]);
	return 0;
}

ビーバーの会合 (Meetings)

Description

交互题
有一棵n个点的树,你每次可以询问(x,y,z),满足x!=y,x!=z,y!=z,交互库会告诉你距离x,y,z的距离和最小的点,保证这棵树每个点的度数<=18
用不超过40000次询问还原这棵树
n<=2000

Solution

考虑先确定一个点x,随机一个点y,问所有其他的点z
如果query(x,y,z)=z,那么说明z在x到y的路径上,否则我们知道z在哪个点的子树内,递归下去就好了
然后我们只需要将x到y路径上所有的点排序
然后就过了,有没有dalao可以证明一下啊QwQ

Code
#include "meetings.h"
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define pb(a) push_back(a)
using namespace std;

typedef vector<int> vec;
typedef unsigned long long ull;

ull seed;
int ran(int l,int r) {
   
	seed^=seed<<15;
	seed^=seed>>13;
	seed^=seed<<5;
	return seed%(r-l+1)+l;
}

const int N=2e3+5;

int n,rt;
bool cmp1(int x,int y) {
   return Query(rt,x,y)==x;}

vec tmp;

void calc(int x,vec p) {
   
	int y=p[ran(0,p.size()-1)];
	vector<vec> q(n);vec t;t.pb(y);
	for(int z:p) {
   
		if (z==y) continue;
		int w=Query(x,y,z);
		if (w==z) t.pb(z);
		else q[w].pb(z);
	}
	if (q[x].size()) calc(x,q[x]);
	for(int z:t) if (q[z].size()) calc(z,q[z]);
	rt=x;sort(t.begin(),t.end(),cmp1);
	for(int i=0;i<t.size();i++) {
   
		int x=i?t[i-1]:rt,y=t[i];
		if (x>y) swap(x,y);
		Bridge(x,y);
	}
}

void Solve(int N) {
   
	n=N;fo(i
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值