二维平面内有 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。
考虑这三条直线的状态,只可能为
(1)三条水平线
(2)两条水平线+一条垂直线
(剩余情况交换 x,y 坐标即可)
我们用一个数组统计同一 y 坐标上有几个点。
对于 1)的情况,只需判断是否只有三个及以下的 y 坐标上有点即可。
对于 2)的情况,可以枚举垂直线的 x 坐标,将这条垂直线上的点全部删去,判断剩下的
点的 y 坐标是否只有两种及以下。
将点按 x 坐标排序后即可做到 O(n)的扫描。
由于坐标较大,可以离散化预处理。
#include
using namespace std;
const int N=5e4;
int n,maxx=0,maxn=0,lenx=0,leny=0,ans1,ans2;
int x[2*N],y[2*N];
int sumx[2*N],sumy[2*N],sum1[2*N],sum2[2*N];
bool vis[2*N];
int main()
{
freopen("3lines.in","r",stdin);
freopen("3lines.out","w",stdout);
int _x,_y,i;
map<int,int> X;map<int,int> Y;
for(scanf("%d",&n),i=0;++i<=n&&scanf("%d%d",&_x,&_y);)
{
if(!X[_x])X[_x]=++lenx;
if(!Y[_y])Y[_y]=++leny;
x[i]=X[_x],y[i]=Y[_y];
sumx[x[i]]++,sumy[y[i]]++;
}//桶排
bool o=1,ok=1;
for(memset(vis,1,sizeof(vis)),i=0;++i<=n;)
{
if(sumx[x[i]]>maxx)maxx=sumx[x[i]],maxn=x[i],o=1;
if(sumy[y[i]]>maxx)maxx=sumy[y[i]],o=0,maxn=y[i];
}//找出最多的
for(i=0;++i<=n;)
if(o&&x[i]==maxn||!o&&y[i]==maxn)vis[i]=0;//记录
for(i=0;++i<=n;)
if(!vis[i])--sumx[x[i]],--sumy[y[i]];//去除
for(maxx=0,maxn=0,o=1,i=0;++i<=n;)
{
if(!vis[i])continue;
if(sumx[x[i]]>maxx)maxx=sumx[x[i]],maxn=x[i],o=1;
if(sumy[y[i]]>maxx)maxx=sumy[y[i]],o=0,maxn=y[i];
}//TWO
for(i=0;++i<=n;)
if((o&&x[i]==maxn)||(!o&&y[i]==maxn))vis[i]=0;
for(ans1=0,ans2=0,o=1,ok=1,i=0;++i<=n;)
{
if(!vis[i])continue;
if(!sum1[x[i]])ans1++;
if(!sum2[y[i]])ans2++;
sum1[x[i]]=1,sum2[y[i]]=1;
}
o=ans1<2,ok=ans2<2;
if(!o&&!ok)printf("NO");
else printf("YES");
return 0;
}
有 n 堆石子,第 i 堆有 xi 个。
Alice 和 Bob 轮流取石子(先后手未定),Alice 每次从一堆中取走 a 个,Bob 每次从一
堆中取走 b 个,无法操作者输。
不难发现只会有四种情况:Alice 必胜;Bob 必胜;先手必胜;后手必胜。
你需要选定若干堆石子(共有 2^n 种方案),Alice 和 Bob 只能在你选出的堆中取,问
以上四种情况对应的方案数。对 10^9+7 取模。
第一行三个整数 n,a,b,第二行 n 个整数 x1~xn。
一行四个整数,分别表示 Alice 必胜、Bob 必胜、先手必胜和后手必胜的方案数,对
109+7 取模。
2 2 3
2 3
2 0 1 1
选定空集时后手必胜, 选定{2}时 Alice 必胜, 选定{3}时先手必胜, 选定{2,3}时 Alice 必胜。
对于 10%的数据,n, xi <= 5。
对于 50%的数据,n <= 20。
对于另外 10%的数据,a = b。
对于又另外 20%的数据,a = 1。
对于 100%的数据,1 <= n <= 1e5, 1 <= a, b, xi <= 1e9。
假设a < b。
每堆石子先对a + b取模,然后可以分为4种:
(1)xi < a,没用。
(2)a <= xi < b,只要存在则a必胜。
(3)b <= xi < 2a,只和奇偶性有关。
(4)2a <= xi, 存在至少2个则a必胜, 存在1个且(3)为偶数则先手必胜, 存在1个且(3)为奇
数则a必胜, 不存在且(3)为奇数则先手必胜, 不存在且(3)为偶数则后手必胜。
时间复杂度 O(n)
#include
using namespace std;
long long n,m,k,_a,_b;
const long long MOD=1e9+7;
long long a[5],b[5];
//int Alice_s,Bob_s,Quite_s,Slow_s;
bool f;
inline long long work(long long x,long long y)
{
if(!y)return 1;
long long z=work(x,y>>1)%MOD;
z=z*z%MOD;
if(y&1)z=z*x%MOD;
return z%MOD;
}
int main()
{
// freopen("stone.in","r",stdin);
// freopen("stone.out","w",stdout);
long long j,i;f=0;
scanf("%lld%lld%lld",&n,&_a,&_b);
if(_a>_b)swap(_a,_b),f=1;
for(i=0;++i<=n&&scanf("%lld",&j);)
{
j%=_a+_b;
++a[(_a<=j)+(_b<=j)+(_b<=j&&j>=2*_a)+1];
}
b[1]=((work(2,a[2])-1)*(work(2,a[3]+a[4]))%MOD+(work(2,a[4])-a[4]-1+MOD)*work(2,a[3]))%MOD;
if(a[3]>0)b[1]+=a[4]*work(2,a[3]-1)%MOD,b[3]=work(2,a[3]-1)%MOD+a[4]*work(2,a[3]-1)%MOD,b[4]=work(2,a[3]-1);
else b[4]=1,b[3]=a[4]%MOD;
for(i=0;++i<=4;)b[i]=b[i]*work(2,a[1])%MOD;
if(f)swap(b[1],b[2]);
for(i=0;++i<4;)printf("%lld ",b[i]%MOD);
printf("%lld",b[4]%MOD);
return 0;
}
Alice 有 n 块积木,放置第 i 块积木会占据区间[Li, Ri]。
Alice 每次会腾出一个区间放积木,她希望放的积木尽可能多,对每个询问区间,你需
要回答 Alice 最多可放置的积木数量。
注意: 积木与积木的放置区间不可重叠,且任意选定的积木放置区间不能超出询问区间。
第一行三个整数 n,q,len,表示积木的数量,询问数和 len 的大小(数据保证 1≤Li,Ri≤
len)。
接下来 n 行,每行两个整数 Li,Ri,表示砖头的魔法标记。
接下来 q 行,每行两个整数 ai,bi,表示 moreD 选定的区间。
对于每组询问输出对应的答案。
3 2 4
1 2
2 3
3 4
1 3
3 3
1
0
对于 30%的数据满足 n <= 10, q <= 10。
对于 60%的数据满足 n <= 1,000, q <= 1,000, 1 <= Li, Ri <= 1,000。
对于 100%的数据满足 n <= 1e5, q <= 1e5, 1 <= Li, Ri <= len <= 1e5。
对于 60%的数据,
我们可以用贪心算法。把所有线段按 Ri 小到大排序,每次询问时按 Ri 坐标从小到
大扫描所有线段,只要线段被区间包含且不与之前的线段冲突,那么就把这条线段加入
解。
时间复杂度 O(q*n)。
对于 100%的数据,
每次暴力扫描太慢了,考虑倍增。
f[i,j]表示从位置 i 开始,选择 2^j 条线段, Ri 最大的线段 Ri 最小是多少。显然所有
的 f[i,j]可以在 O(nlogn)的时间内计算出来(len 与 n 同级)。
询问时我们从 x 开始,从大到小枚举 k,如果选择 2^k 条线段后没有超出区间的范
围,那么答案加上 2^k,然后继续统计 f[x,k]+1~y 这段区间的答案。这样单次询问复杂
度是 O(logn)的,询问的总复杂度是 O(Qlogn),可以解决这道题。
60分:
#include
using namespace std;
inline int read() {
int num=0;
char c=getchar();
for(;c<'0' || c>'9';c=getchar());
for(;c>='0' && c<='9';c=getchar()) num=(num<<1)+(num<<3)+c-48;
return num;
}
struct node {
int x,y;
}a[100100],b[100100];
int t;
int n,m,L;
inline bool cmp(node x,node y) {
return x.y < y.y || x.y == y.y && x.x > y.x;
}
int main() {
freopen("block.in","r",stdin);
freopen("block.out","w",stdout);
n=read() ,m=read() ,L=read();
for(int i=1;i<=n;i++) a[i].x=read(),a[i].y=read();
sort(a+1,a+n+1,cmp);
// for(int i=1;i<=n;i++) cout<
for(int i=1;i<=n;i++) if (a[i].y ^ a[i-1].y) b[a[i-1].y].y=i-1,b[a[i].y].x=i;
b[a[n].y].y=n;
// cout<<"------------------"<
// for(int i=1;i<=n;i++) cout<
// cout<<"------------------"<
for(int k=1;k<=m;k++) {
int l=read(),r=read();
int p=l,ans=0;
for(int i=l;i<=r;i++) if (a[b[i].x].x>=p) ans++,p=i+1;
printf("%d\n",ans);
}
return 0;
}
#include
using namespace std;
int n,m,len,ans=0;
struct node{
int l,r;
}a[1010101];
int fa[200010][50];
inline bool mycmp(node x,node y){
return x.r<y.r;
}
int main(){
freopen("block.in","r",stdin);
freopen("block.out","w",stdout);
scanf("%d %d %d",&n,&m,&len);
for (int i=1;i<=n;i++)
scanf("%d %d",&a[i].l,&a[i].r);
memset(fa,20,sizeof(fa));
sort(a+1,a+n+1,mycmp);
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<=m;i++){
ans=0;
int l,r;
scanf("%d %d",&l,&r);
int now=l;
for (int k=25;k>=0;k--)
if (fa[now][k]<=r)
now=fa[now][k]+1,ans+=1<<k;
printf("%d\n",ans);
}
return 0;
}
给定 n 个数,有 m 个询问。每次询问, Alice 想知道区间内[l, r]内是否出现过 xi 这个数。
第一行一个整数 n。
第二行 n 个正整数 ai。
第三行一个整数 m。
接下来 m 行每行三个整数 li, ri 和 xi, 表示询问区间为[li, ri], 询问数字为 xi。
对于每个询问,输出一个字符。 ‘0’表示没出现, ‘1’表示出现了。
5
1234567 666666 3141593 666666 4343434
5
1 5 3141593
1 5 578202
2 4 666666
4 4 7135610
1 1 1234567
10101
对于 50%的数据, n <= 1000, m <= 1000。
对于 100%的数据, n <= 1e5, m <= 1e5, ai, xi <= 1e9。
据说,这题是看昨天我们有没有认真听课的?
分块二分,模板题
成功满分(也是本场唯一一道我拿到超过10分的题)
当然也可以离线莫队 O(nsqrt(n)) 或直接开个桶用 set
但是莫队不优秀,常数太大,会被卡成50分的
#include
using namespace std;
const int N=2e5+5;
int a[N],b[N];
int n,k,m;
inline int kuaishu(int x){return (x-1)/k+1;}
inline int work_f(int l,int r,int x)
{
int i,cnt=0;
int _l=kuaishu(l);
int _r=kuaishu(r);
if(_l==_r)
{
for(i=l-1;++i<=r;)if(a[i]==x)++cnt;
if(cnt>0)return 1;
else return 0;
}
for(i=l-1;++i<=_l*k;)if(a[i]==x)return 1;
for(i=_l;++i<_r;)
{
cnt+=upper_bound(b+(i-1)*k+1,b+i*k+1,x)-lower_bound(b+(i-1)*k+1,b+i*k+1,x);
if(cnt>0)return 1;
}
for(i=(_r-1)*k;++i<=r;)if(a[i]==x)return 1;
return 0;
}
int main()
{
freopen("statistic.in","r",stdin);
freopen("statistic.out","w",stdout);
int i,j;
for(scanf("%d",&n),i=0;++i<=n&&scanf("%d",&a[i]);b[i]=a[i]);
for(k=sqrt(n),i=0;++i<=k+1;)
{
if((i-1)*k+1>n)break;
sort(b+(i-1)*k+1,b+min(i*k,n)+1);
}
scanf("%d",&m);
while(m--)
{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
int ans=work_f(l,r,x);
printf("%d",ans);
}
return 0;
}
n 个同学在长度为 C 的环形跑道上跑 L 圈。所有人的出发点相同,跑步的速度不同。
跑步过程中一个人可能会超过另一个人,即发生“套圈事件”。
Alice 想知道从开始跑步到第一名跑完全程的过程中,会发生多少次“套圈事件”。
注意: 由于不同学生的体能差异巨大, 一个人可能被另一个人套多圈。 (对于一对同学(x,y),
“套圈事件”的次数可能大于 1)。
第一行三个整数: n, L 和 C。
第 2…n+1 行,每行一个整数。 第 i+1 行表示第 i 个同学的速度 vi。
一个整数表示 “套圈事件”发生的总次数。
4 2 100
20
100
70
1
4
4 个同学跑 2 圈,跑道长度为 100。
同学们的速度分别是: 20, 100, 70 和 1。
同学 2 花费 2 个单位时间跑完全程。
这段时间里发生了 4 次“套圈事件”: 同学 2 超过同学 1 和 4, 同学 3 超过同学 1 和 4。
对于 30%的数据, n <= 5000
对于 100%的数据, n <= 1e5, 1 <= L, C <= 25000, 1 <= vi <= 1e6。
50%的数据 n <= 5000。
将所有人按速度排序,因为套圈事件只可能是速度快的人超过速度慢的人。
n ^ 2 枚举一对同学,计算它们发生的套圈事件次数。
设最快的人速度为 Vmax , 那么总用时 T = L * C / Vmax。
而一对同学(x, y) (Vx > Vy)发生一次套圈事件要 t = C / (Vx - Vy) 的时间。
所以一对同学(x, y)(Vx > Vy)对答案的贡献为:
考虑优化
L / Vmax 是个定值,我们只要求Σ(Vx - Vy)就可以了。
不过还没完,你会发现这么做样例都不过。
为什么?因为上面那个式子的答案要求是整除,而累加和之后答案自然会发生变化。
我们记录 L * Vx mod Vmax 的余数,计算整除后的答案, 再余数对答案的贡献记入
即可。 用 BIT 询问前缀和。
好,这是std……(反正这题我爆零,暴力都不会打)
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = 1e5 + 3;
int n, c, t, Vmax, tr[N];
ll ans, sum, l;
struct cow {
int speed, rank;
cow(): speed(0), rank(0){}
bool operator < (const cow &b) const {
return speed < b.speed;
}
} a[N];
struct BIT {
int f[N];
BIT(){ memset(f, 0, sizeof(f)); }
void Insert(int x) {
for (; x <= t; x += x & -x)
++f[x];
}
int Query(int x) {
int res = 0;
for (; x; x -= x & -x)
res += f[x];
return res;
}
} Bit;
int main() {
freopen("running.in", "r", stdin);
freopen("running.out", "w", stdout);
scanf("%d%I64d%d", &n, &l, &c);
for (int i = 1; i <= n; ++i)
scanf("%d", &a[i].speed);
sort(a + 1, a + n + 1);
Vmax = a[n].speed;
for (int i = 1; i <= n; ++i)
tr[i] = a[i].rank = l * a[i].speed % Vmax;
sort(tr + 1, tr + n + 1);
t = unique(tr + 1, tr + n + 1) - tr - 1;
for (int i = 1; i <= n; ++i)
a[i].rank = lower_bound(tr + 1, tr + t + 1, a[i].rank) - tr;
for (int i = 1; i <= n; ++i) {
ll cur = l * a[i].speed / Vmax;
ans += cur * (i - 1) - sum - (i - 1) + Bit.Query(a[i].rank);
sum += cur;
Bit.Insert(a[i].rank);
}
cout << ans << endl;
fclose(stdin); fclose(stdout);
return 0;
}
有 n 个正整数, 选出其中的若干个数构成一个非空可重集,若该可重集能被划分为和相
等的两个部分,则称它为平衡集。
问有多少个不同的平衡集。
平衡集 A,B 不同当且仅当存在下标为 i 的数在平衡集 A 中而不在平衡集 B 中。
第一行输入 n,接下来 n 行每行输入一个正整数 ai。
输出一共有多少种选法。
4 1 2 3 4
3
三个平衡集分别为{1, 2, 3}, {1, 3, 4}, {1, 2, 3, 4}。
对于 20%的数据, n <= 10。
各有 5%的数据满足, n = 16, 17, 18, 19
对于 100%的数据, n <= 20, ai <= 1e8。
比较靠谱的暴力:
枚举一个集合,再枚举这个集合的子集,判断是否合乎题意。
复杂度为(3 ^ n),期望得分 30-40
n 太大了导致算法超时考虑将所有数分成两组(分别记作 Black 与 White)
若一个集合分成 A,B 两个子集后,他们的和相等即 Sum(A) = Sum(B)
就会有下面的等式出现:
Sum(Black in A) - Sum(Black in B) = Sum(White in B) - Sum(White in A)
对两组数分别处理:
将 Black 一组的所有可能分成的集合枚举出来,用 hash 记录 sum 差值,
对 White 组进行相同处理,若发现有某两个 sum 差值相等,则说明出现一个解。
复杂度大概为 O(6 ^ (n / 2) )
注意判重,同一种方案可能会出现多种不同的 sum 差值
#include
#include
typedef long long ll;
typedef std :: pair <int, int> pii;
const int N = 25;
const int CNT = 1e5;
bool isok[1 << 21];
int n, a[N], nL, nR, ans;
pii tL[CNT], tR[CNT];
int Get() {
char ch;
while ((ch = getchar()) < '0' || ch > '9');
int Num = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9')
Num = Num * 10 + ch - '0';
return Num;
}
void dfs(int l, int r, int sum, int s, pii *t, int &n) {
if (l > r) {
t[++n] = std :: make_pair(sum, s);
return;
}
dfs(l + 1, r, sum, s, t, n);
dfs(l + 1, r, sum + a[l], s | (1 << l - 1), t, n);
dfs(l + 1, r, sum - a[l], s | (1 << l - 1), t, n);
}
int main() {
//freopen("subset.in", "r", stdin);
//freopen("subset.out", "w", stdout);
n = Get();
for (int i = 1; i <= n; ++i) a[i] = Get();
dfs(1, n >> 1, 0, 0, tL, nL), dfs((n >> 1) + 1, n, 0, 0, tR, nR);
std :: sort(tL + 1, tL + nL + 1);
nL = std :: unique(tL + 1, tL + nL + 1) - tL;
std :: sort(tR + 1, tR + nR + 1);
nR = std :: unique(tR + 1, tR + nR + 1) - tR;
int ir = 1, jr;
for (int il = 1, jl; (jl = il) <= nL; il = jl + 1) {
while (jl < nL && tL[jl + 1].first == tL[il].first) ++jl;
while (ir < nR && tR[ir].first < tL[il].first) ++ir;
if (tR[ir].first == tL[il].first) {
jr = ir;
while (jr < nR && tR[jr + 1].first == tR[ir].first) ++jr;
for (int l = il; l <= jl; ++l)
for (int r = ir; r <= jr; ++r)
isok[tL[l].second | tR[r].second] = true;
ir = jr;
}
}
for (int i = 1; i < (1 << n); ++i) ans += isok[i];
printf("%d\n", ans);
fclose(stdin);
fclose(stdout);
}