采矿
一、题目
已知,矿场是一个平面直角坐标系;小周猪猪位于坐标(0, 0),即平面直角坐标系的原点。
已知有n个矿石,每一个矿石都一个坐标(x, y),其中x和y都是任意不为零的整数。当然,小周猪猪都能够准确的知道这些矿石的具体位置;它会依次按照矿石的编号,对这些矿石发射激光。
发射激光的规则是:
可以在所处的坐标原点上沿着某一个方向发射激光,这条激光上的每一个矿石都能被小周猪猪所有,且这些矿石被小周猪猪拿走以后便不复存在。
发射的激光是一条射线而不是一条直线,且这条射线是正比例函数(一定满足x > 0 或 x < 0).
现在,给你这 n 个点的坐标,小周猪猪会依次按照这 n 个点的顺序向其坐标所属方向发射激光。小周猪猪想要知道:每一次发射完激光完以后能够得到多少个矿石。
二、解法
用
[
a
g
c
d
,
b
g
c
d
]
[\frac{a}{gcd},\frac{b}{gcd}]
[gcda,gcdb]表示斜率,把它放进
m
p
mp
mp中,注意判方向即可。
???
#include <cstdio>
#include <iostream>
#include <map>
using namespace std;
#define int long long
#define ull unsigned long long
const int MAXN = 100005;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,k,x[MAXN],y[MAXN];
map<ull,int> mp[4];
int abs(int x)
{
return x>0?x:-x;
}
int gcd(int a,int b)
{
return !b?a:gcd(b,a%b);
}
ull get_hash(int x,int y)
{
return abs(x)*k+abs(y);
}
int locate(int x,int y)
{
if(x>0 && y>0) return 0;
if(x<0 && y>0) return 1;
if(x<0 && y<0) return 2;
return 3;
}
signed main()
{
n=read();
for(int i=1;i<=n;i++)
{
x[i]=read();y[i]=read();
k=max(k,max(x[i],y[i]));
}
for(int i=1;i<=n;i++)
{
int d=gcd(abs(x[i]),abs(y[i]));
mp[locate(x[i],y[i])][get_hash(x[i]/d,y[i]/d)]++;
}
for(int i=1;i<=n;i++)
{
int d=gcd(abs(x[i]),abs(y[i]));
printf("%d\n",mp[locate(x[i],y[i])][get_hash(x[i]/d,y[i]/d)]);
mp[locate(x[i],y[i])][get_hash(x[i]/d,y[i]/d)]=0;
}
}
流浪月球
一、题目
在流浪月球期间,需要集齐n个魔法石才能开启传送门。在第i天,小周猪猪会得到数量在 [1, i] 范围内的传送石。当然,这一个数量小周猪猪可以随意选择。
在每一天,小周猪猪会将得到魔法石数量做一个标记,在返回地球的时候便会得到一个和为n的序列。
例如n = 4时,有 3 种序列:
(1, 1, 1, 1)
(1, 2, 1)
(1, 1, 2)
例如(1, 2, 1)表示第一天取了1个魔法石, 第二天取了2个魔法石,第三天取了 1个魔法石。
请问有多少个这样的序列,答案对10^9 + 7取模。
二、解法
考虑
d
p
dp
dp,设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为第
i
i
i天拿到
j
j
j个宝石的方案数,则有:
d
p
[
i
]
[
j
]
=
∑
k
=
j
−
i
j
−
1
d
p
[
i
−
1
]
[
k
]
dp[i][j]=\sum_{k=j-i}^{j-1} dp[i-1][k]
dp[i][j]=∑k=j−ij−1dp[i−1][k]
这样写就有
90
90
90分,在观察
d
p
dp
dp式,发现可以用前缀和优化,时间复杂度
O
(
应
该
过
的
了
)
O(应该过的了)
O(应该过的了)。
???
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MOD = 1e9+7;
const int MAXN = 2005;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,cur,ans,dp[MAXN],sum[MAXN];
void mod(int &x)
{
x=(x%MOD+MOD)%MOD;
}
int main()
{
while(~scanf("%d",&n))
{
memset(sum,0,sizeof sum);
sum[0]=1;ans=0;
for(int i=1;i<=n;i++)
{
memset(dp,0,sizeof dp);
for(int j=i;j<=min(i*(i+1)/2,n);j++)
{
if(j==i)
dp[j]=1;
else
dp[j]=sum[j-1]-sum[j-i-1];
}
mod(ans+=dp[n]);
memset(sum,0,sizeof sum);
for(int j=i;j<=n;j++)
mod(sum[j]=sum[j-1]+dp[j]);
}
printf("%d\n",ans);
}
}
统计小猪猪
一、题目
已知小周猪猪家门口有一张1 * n的网格图,有m个关于小猪猪的信息需要统计。
每一个信息可以用一个四元组(ti, pi, di, vi)来表示。即第i头小猪在时间ti出现 ,在位置pi ,朝着方向di,以vi的速度运动着。di = 0表示朝向是左边, di = 1表示朝向是右边。
这些小猪猪运动到边界,即大于n或者小于1的位置,或者出现时间在统计时间以后的小猪猪,将跑出小周猪猪的视野范围,不会被小周猪猪统计。
现在有q个询问,每一个询问有一个数字 ,表示小周猪猪想要知道在时间Ti,有多少只小猪猪处于奇数点,有多少只小猪猪处于偶数点。由于猪猪国是一个和谐的国度,小猪猪相遇不会发生任何事情。
二、解法
这道题我们很用差分解决。
我们发现这些猪都有一个固定的出现时间和消失时间,那么这一段时间内的贡献+1。
如果
v
v
v是偶数,奇偶性不变,直接差分。
如果每一次的奇偶性是变化的,可能奇数时间的奇数答案+1 ,偶数时间的偶数答案+1。
当然存在另外的情况:奇数时间的偶数答案+1,偶数时间的奇数答案+1。
因此对于奇数和偶数交替的情况,维护每一个奇数位置在奇数时间/偶数时间的答案。同理,在偶数时间内维护奇数/偶数时间的答案。 .
#include <cstdio>
const int MAXN = 1000005;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,q,pre[2*MAXN][4];
int main()
{
n=read();m=read();q=read();
for(int i=1;i<=m;i++)
{
int t=read(),p=read(),d=read(),v=read(),e;
if(d==0)
e=t+(p-1)/v+1;
else
e=t+(n-p)/v+1;
if(v%2==0)
pre[t][p&1]++,pre[e][p&1]--;
else
{
pre[t][((t&1)^(p&1))+2]++,pre[e][((t&1)^(p&1))+2]--;
}
}
for(int i=1;i<=2*n;i++)
for(int j=0;j<4;j++)
pre[i][j]+=pre[i-1][j];
while(q--)
{
int t=read();
int ans1=pre[t][1],ans2=pre[t][0];
if(t&1) ans1+=pre[t][2],ans2+=pre[t][3];
else ans2+=pre[t][2],ans1+=pre[t][3];
printf("%d %d\n",ans1,ans2);
}
}
可爱路径
对于任意一个数,一定存在两种情况,即"大于中位数"和"小于中位数"两种。这样一来就具有单调性,我们会考虑枚举中位数,再通过判定就可以
log
V
\log V
logV的时间内得到中位数了。
考虑如何判定,判断一串数字的中位数是否大于
x
x
x,那么以为这一定有一半的数是大于
x
x
x的。
这个过程可以用
t
p
s
o
r
t
+
d
p
tpsort+dp
tpsort+dp实现,设
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]为跑到第
i
i
i个点经过
j
j
j个点,最多有多少个大于
x
x
x的点,则:
d
p
[
i
]
[
j
]
=
{
a
[
i
]
>
=
x
(
j
=
1
)
d
p
[
k
]
[
j
−
1
]
+
a
[
i
]
>
=
x
(
i
,
k
相
连
)
dp[i][j]=\begin{cases}a[i]>=x&(j=1)\\ dp[k][j-1]+a[i]>=x&(i,k相连)\end{cases}
dp[i][j]={a[i]>=xdp[k][j−1]+a[i]>=x(j=1)(i,k相连)
发现这个还是过不了链的数据,对于一条链,可以把它转化成区间问题,把大于 x x x的看作1,把小于 x x x的看作-1,发现只要满足 s i − s j ≥ 0 ∧ i − j ≥ k si-sj\geq 0\wedge i-j\geq k si−sj≥0∧i−j≥k,发现这个可以动态维护最小值(阉割版滑动窗口),就可以达到 O ( n ) O(n) O(n)检查了。
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
const int inf = 0x3f3f3f3f;
const int MAXN = 1005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,k,ans,tot,cnt,Min,Max,f[MAXN],in[MAXN],a[100005],tp[MAXN];
int dp[MAXN][MAXN],s[100005];
struct edge
{
int v,next;
}e[3*MAXN];
void topol()
{
queue<int> q;
for(int i=1;i<=n;i++)
if(in[i]==0)
q.push(i);
while(!q.empty())
{
int u=q.front();
q.pop();
tp[++cnt]=u;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v;
in[v]--;
if(!in[v])
q.push(v);
}
}
}
bool check(int x)
{
if(n>1000)
{
int t=0;
for(int i=1;i<=n;i++)
s[i]=s[i-1]+(a[i]>=x?1:-1);
for(int i=k+1;i<=n;i++)
{
if(s[i]-t>=0) return 1;
t=min(t,s[i-k]);
}
return 0;
}
memset(dp,0,sizeof dp);
for(int i=1;i<=n;i++)
dp[i][1]=(a[i]>=x);
for(int l=1;l<n;l++)
{
for(int i=1;i<=n;i++)
{
int u=tp[i];
for(int j=f[u];j;j=e[j].next)
{
int v=e[j].v;
dp[v][l+1]=max(dp[v][l+1],dp[u][l]+(a[v]>=x));
}
}
}
for(int i=1;i<=n;i++)
for(int j=k+1;j<=n;j++)
if(dp[i][j]>=(j+1)/2)
return 1;
return 0;
}
void conquer(int l,int r)
{
if(l>r) return ;
int mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
conquer(mid+1,r);
}
else
{
conquer(l,mid-1);
}
}
int main()
{
n=read();m=read();k=read();
Min=inf,Max=ans=-inf;
for(int i=1;i<=n;i++)
{
a[i]=read();
Min=min(Min,a[i]);
Max=max(Max,a[i]);
}
if(n<=1000)
{
for(int i=1;i<=m;i++)
{
int u=read(),v=read();
e[++tot]=edge{v,f[u]},f[u]=tot;
in[v]++;
}
topol();
}
conquer(Min,Max);
if(ans==-inf)
printf("No\n");
else
printf("%d\n",ans);
}
/*
7 8 3
46 79 97 33 22 1 122
1 2
1 5
2 3
2 6
3 4
6 4
5 7
4 7
*/