不定时更新(咕咕咕
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