题目按难度从简到难手动排序————题记
T1:三笔画(3lines)
【题目描述】:
二维平面内有 n 个不同的点, Alice 需要在平面内画至多 3 条直线使得所有点在直线上。
问: Alice 能否完成任务, 如果能, 输出”YES”; 否则, 输出”NO”。
注意: 由于 Alice 的画图水平有限, 直线只能平行于坐标轴。
【输入数据】:
第一行,一个整数 n。
接下来 n 行,第 i+1 行包含空格隔开的整数 xi,yi,表示第 i 个点的坐标。
【输出数据】:
若 Alice 能完成任务, 输出”YES”, 否则输出”NO”。
【样例输入】:
6
1 7
0 0
1 2
2 0
1 4
3 4
【样例输出】:
YES
【样例解释】:
三条直线分别为 x=1,y=0,y=4。
【数据范围】:
对于 30%的数据,1 <= n <= 13。
对于 60%的数据,1 <= n <= 20。
对于 100%的数据,1 <= n <= 5e4,0 <= xi, yi <= 1e9。
心路历程:对于这道题,在考场上我思考了很久,经过分析,我发现如果在一条线上有超过3个点,说明必须要使用一条线去覆盖这几个点,就使Line−1Line-1Line−1(LineLineLine初始值为3),将这几个点删去。如果没有这种情况,那么就暴力枚举剩余Line条线,判断是否可以覆盖。对于大数据,这种方法很有效,对于小数据,那就直接暴力吧!【φ(>ω<*)】
正解:对于三条直线的状态,只有以下2种状态:
- 1.1.1.三条水平线
- 2.2.2.两条水平线+一条垂直线
(剩余情况旋转坐标轴即可)
我们用一个数组统计同一 yyy 坐标上有几个点。
对于 ① 的情况,只需判断是否只有三个及以下的 yyy 坐标上有点即可。
对于 ② 的情况,可以枚举垂直线的 x 坐标,将这条垂直线上的点全部删去,判断剩下的点的 yyy 坐标是否只有两种及以下。
因为xi,yixi,yixi,yi有些大,可以先进行离散化预处理后再进行操作。
将点按 xxx 坐标排序后即可做到 O(n)O(n)O(n)的扫描.
非正解代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 600000;
int n;
int x[N], y[N];
bool check(int p1,int t1,int p2,int t2,int p3,int t3)
{
int ans = 0;
for (int i=1;i<=n;++i) {
if (x[i] == p1 && t1 == 1) ans ++;
else if (x[i] == p2 && t2 == 1) ans ++;
else if (x[i] == p3 && t3 == 1) ans ++;
else if (y[i] == p1 && t1 == 2) ans ++;
else if (y[i] == p2 && t2 == 2) ans ++;
else if (y[i] == p3 && t3 == 2) ans ++;
}
return ans == n;
}
bool work1(void)
{
for (int i=1;i<=n;++i)
for (int j=1;j<=n;++j)
for (int k=1;k<=n;++k) {
if (check(x[i],1,x[j],1,x[k],1)) return 1;
if (check(x[i],1,x[j],1,y[k],2)) return 1;
if (check(x[i],1,y[j],2,x[k],1)) return 1;
if (check(x[i],1,y[j],2,y[k],2)) return 1;
if (check(y[i],2,x[j],1,x[k],1)) return 1;
if (check(y[i],2,x[j],1,y[k],2)) return 1;
if (check(y[i],2,y[j],2,x[k],1)) return 1;
if (check(y[i],2,y[j],2,y[k],2)) return 1;
}
return 0;
}
bool work2(void)
{
map <int,int> cnt1;
map <int,int> cnt2;
map <int,int> cnt3;
map <int,int> cnt4;
int cnt_x = 0, cnt_y = 0, P = -1, T = -1;
for (int i=1;i<=n;++i)
cnt1[x[i]] ++, cnt2[y[i]] ++;
for (int i=1;i<=n;++i)
{
if (cnt1[x[i]] > 3) {
P = x[i], T = 1;
break;
}
if (cnt2[y[i]] > 3) {
P = y[i], T = 2;
break;
}
}
if (P == -1 && T == -1) return 0;
for (int i=1;i<=n;++i)
{
if (x[i] == P && T == 1 || y[i] == P && T == 2) continue;
cnt3[x[i]] ++;
if (cnt3[x[i]] == 1) cnt_x ++;
cnt4[y[i]] ++;
if (cnt4[y[i]] == 1) cnt_y ++;
}
return cnt_x <= 2 || cnt_y <= 2;
}
int main(void)
{
freopen("3lines.in","r",stdin);
freopen("3lines.out","w",stdout);
scanf("%d", &n);
for (int i=1;i<=n;++i)
scanf("%d %d", x+i, y+i);
if (n <= 20) work1() ? puts("YES") : puts("NO");
else work2() ? puts("YES") : puts("NO");
return 0;
}
正解代码(不是我写的):
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
struct point{
int x,y;
}a[50001];
int xx[50001];
int yy[50001];
int sum[50001];
int fx,fy;
int read(){
scanf("%d",&n);
for (int i=1;i<=n;++i){
scanf("%d%d",&xx[i],&yy[i]);
a[i].x=xx[i];
a[i].y=yy[i];
}
sort(xx+1,xx+n+1);
sort(yy+1,yy+n+1);
fx=unique(xx+1,xx+n+1)-xx-1;
fy=unique(yy+1,yy+n+1)-yy-1;
for (int i=1;i<=n;++i){
a[i].x=lower_bound(xx+1,xx+fx+1,a[i].x)-xx;
a[i].y=lower_bound(yy+1,yy+fy+1,a[i].y)-yy;
}
}
int cmp(const point &a,const point &b){
return a.x<b.x?1:((a.x==b.x)&&(a.y<b.y));
}
int work(){
int tot=0;
sort(a+1,a+n+1,cmp);
for (int i=1;i<=n;++i)
if (sum[a[i].y]++==0)++tot;
if (tot<=3)return 1;
int t=1,w;
while (t<=n){
w=t;
while (w<n&&a[w+1].x==a[t].x)++w;
for (int i=t;i<=w;++i)
if (sum[a[i].y]--==1)--tot;
if (tot<=2)return true;
for (int i=t;i<=w;++i)
if (sum[a[i].y]++==0)++tot;
t=w+1;
}
return 0;
}
int main(){
freopen("3lines.in","r",stdin);
freopen("3lines.out","w",stdout);
read();
if (work()){
puts("YES");
}
else {
memset(sum,0,sizeof(sum));
for (int i=1;i<=n;++i)
swap(a[i].x,a[i].y);
puts(work()? "YES" : "NO");
}
fclose(stdin);
fclose(stdout);
}
T2 放积木(block)
【问题描述】:
AliceAliceAlice 有 nnn 块积木,放置第iii块积木会占据区间[Li,Ri][Li, Ri][Li,Ri]。
AliceAliceAlice 每次会腾出一个区间放积木,她希望放的积木尽可能多,对每个询问区间,你需要回答 AliceAliceAlice 最多可放置的积木数量。
注意: 积木与积木的放置区间不可重叠,且任意选定的积木放置区间不能超出询问区间。
lenlenlen指木块的最长区间长度。
【输入格式】:
第一行 三个整数 n,q,lenn,q,lenn,q,len,表示积木的数量,询问数和 lenlenlen 的大小(数据保证 1≤Li,Ri≤len1≤Li,Ri≤len1≤Li,Ri≤len)。
接下来 nnn 行,每行两个整数Li,RiLi,RiLi,Ri,表示积木的区间。
接下来 qqq 行,每行两个整数ai,biai,biai,bi,表示 AliceAliceAlice 选定的区间。
【输出格式】:
对于每组询问输出对应的答案。
【输入样例】:
3 2 4
1 2
2 3
3 4
1 3
3 3
【输出样例】:
1
0
【数据范围】:
对于 303030%的数据满足 n<=10,q<=10n <= 10, q <= 10n<=10,q<=10。
对于 606060%的数据满足 n<=1,000,q<=1,000,1<=Li,Ri<=1,000n <= 1,000, q <= 1,000, 1 <= Li, Ri <= 1,000n<=1,000,q<=1,000,1<=Li,Ri<=1,000。
对于 100100100%的数据满足 n<=1e5,q<=1e5,1<=Li,Ri<=len<=1e5n <= 1e5, q <= 1e5, 1 <= Li, Ri <= len <= 1e5n<=1e5,q<=1e5,1<=Li,Ri<=len<=1e5。
心路历程:对于这道题,一看完题,就知道这是一个裸的贪心(先按右端点排序,然后再从左到右在询问区间中放入积木,最后的出的一定是最优解),再看数据范围,直接暴力贪心可得60分,这是一个令我比较接受的分数,再加上我思考了一会儿没有相处优化的方法,就直接打上了暴力贪心,评测结果也得到了60分。
正解:没错,这道题就是裸的贪心,但是如何优化呢?答案是:倍增。
设f[i,j]f[i,j]f[i,j]表示从位置 i 开始,选择 2j2^j2j 条线段, Ri 最大的线段 Ri 最小是多少。显然所有的f[i,j]f[i,j]f[i,j]可以在 O(nlogn)O(nlogn)O(nlogn)的时间内计算出来 (lenlenlen 与 nnn 同级)。
询问时我们从xxx开始,从大到小枚举kkk,如果选择2k2^k2k条线段后没有超出区间的范围,那么答案加上 2k2^k2k,然后继续统计f[x,k]+1f[x,k]+1f[x,k]+1 ~ yyy这段区间的答案。这样单次询问复杂度是 O(logn)O(logn)O(logn)的,询问的总复杂度是 O(Qlogn)O(Qlogn)O(Qlogn),可以解决这道题。
正解代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 200001, M = 30;
int n, m;
struct edge{
int l, r;
}a[N];
int q, len;
int fa[N][M] = {};
bool cmp(edge x,edge y) {
return x.r < y.r;
}
int main() {
freopen("block.in","r",stdin);
freopen("block.out","w",stdout);
scanf("%d%d%d",&n,&q,&len);
for (int i=1;i<=n;i++)
scanf("%d%d",&a[i].l,&a[i].r);
sort(a+1,a+n+1,cmp);
memset(fa, 20, sizeof(fa));
for (int i=1;i<=n;i++)
fa[a[i].l][0] = min(fa[a[i].l][0], a[i].r);
for (int i=len;i>=1;i--)
fa[i][0] = min(fa[i][0], fa[i+1][0]);
for (int j=1;(1<<j)<len;j++)
for (int i=1;i<=len;i++)
if (fa[i][j-1] <= len) fa[i][j] = fa[fa[i][j-1]+1][j-1];
for (int i=1;i<=q;i++) {
int x, y;
int ans = 0;
scanf("%d%d",&x,&y);
for (int j=25;j>=0;j--)
if (fa[x][j] <= y) x = fa[x][j] + 1, ans += 1<<j;
printf("%d\n",ans);
}
return 0;
}
T3:取石子(stone)
【题目描述】:
有 nnn 堆石子,第iii 堆有 xixixi 个。
AliceAliceAlice 和 BobBobBob 轮流取石子(先后手未定),Alice 每次从一堆中取走 aaa 个,BobBobBob 每次从一
堆中取走bbb 个,无法操作者输。
不难发现只会有四种情况:AliceAliceAlice 必胜;BobBobBob 必胜;先手必胜;后手必胜。
你需要选定若干堆石子(共有 2n2^n2n 种方案),AliceAliceAlice 和 BobBobBob 只能在你选出的堆中取,问
以上四种情况对应的方案数。对 109+710^9+7109+7 取模。
【输入数据】:
第一行三个整数 n,a,bn,a,bn,a,b,第二行 nnn 个整数x1x1x1 ~ xnxnxn。
【输出数据】:
一行四个整数,分别表示AliceAliceAlice 必胜、BobBobBob 必胜、先手必胜和后手必胜的方案数,对
109+710^9+7109+7 取模。
【样例输入】:
2 2 3
2 3
【样例输出】:
2 0 1 1
【样例解释】:
选定空集时后手必胜, 选定{2}时 AliceAliceAlice 必胜, 选定{3}时先手必胜, 选定{2,3}时 AliceAliceAlice 必胜。
【数据范围】:
对于 101010%的数据,n,xi<=5n, xi <= 5n,xi<=5。
对于 505050%的数据,n<=20n <= 20n<=20。
对于另外 101010%的数据,a=ba = ba=b。
对于又另外 202020%的数据,a=1a = 1a=1。
对于 100100100%的数据,1<=n<=1e5,1<=a,b,xi<=1e91 <= n <= 1e5, 1 <= a, b, xi <= 1e91<=n<=1e5,1<=a,b,xi<=1e9。
心路历程:看完题目,果断放弃,并在考试之前未再看此题(除了打刷分程序)【(´Д`)y-~】
事实证明我是对的,我现在还未将正解写出来。。。。【( ̄ω ̄;)】
正解:
- 不妨假设a<ba < ba<b。
- 每堆石子先对a + b取模,然后可以分为4种:
- ① xi<axi < axi<a,没用。
- ② a<=xi<ba <= xi < ba<=xi<b,只要存在则a必胜。
- ③ b<=xi<2ab <= xi < 2ab<=xi<2a,只和奇偶性有关。
- ④ 2a<=xi2a <= xi2a<=xi, 存在至少222个则aaa必胜, 存在111个且③为偶数则先手必胜, 存在111个且③为奇数则aaa必胜, 不存在且③为奇数则先手必胜, 不存在且③为偶数则后手必胜。
时间复杂度:O(n)O(n)O(n)