幸运数字(number)
Time Limit:1000ms Memory Limit:64MB
题目描述
LYK 最近运气很差,例如在 NOIP 初赛中仅仅考了 90 分,刚刚卡进复赛,于是它决定使
用一些方法来增加自己的运气值。
它觉得,通过收集幸运数字可以快速的增加它的 RP 值。
它给幸运数字下了一个定义: 如果一个数x能被3整除或被5整除或被7整除,则这个
数为幸运数字。
于是它想让你帮帮它在 L~R 中存在多少幸运数字。
输入格式(number.in)
第一行两个数 L,R。
输出格式(number.out)
一个数表示答案。
输入样例
10 15
输出样例
4
数据范围
对于 50%的数据 1<=L<=R<=10^5。
对于 60%的数据 1<=L<=R<=10^9。
对于 80%的数据 1<=L<=R<=10^18。
对于 90%的数据 1<=L<=R<=10^100。
对于另外 10%的数据 L=1,1<=R<=10^100。
对于 100%的数据 L,R 没有前导 0。
思路;
容易发现1-n中,假设第i个数 i%3==0||i%5==0||i%7==0
那么统计出能被3整除的数为ans1
能被5整除的数为ans2
能被7整除的数为ans3
那么我们发现有些数可以同时被3和5整除,例如15
同理也有同时被5和7整除,例如 35;
也会有同时被3和7整除的数 例如21;
还有同时被3、5和7整除的数 例如105
可以看出来 只用ans=(ans1+ans2+ans3)是不对的
这样多重复了很多数,再减去能同时被两个数整除的数的个数也是不对的
因为多减了能同时被3、5、7同时整除的数 所以要再加上能被3个数整除的个数
写个简单程序验证一下就明白了
最恶心的来了,这题要写高精!!!!!!!
高精除、高精加、高精减!!!!!!!
代码:
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<iostream>
#define N 1000010
using namespace std;
int n,m;
struct node
{
int x,y;
};node a[N];
int read()
{
int num=0,flag=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')flag=-1;c=getchar();}
while(c>='0'&&c<='9'){num=num*10+c-'0';c=getchar();}
return num*flag;
}
bool check(int k)
{
int mn=1e9,mx=-1e9;
for(int i=1;i<=m;i++)
{
if(a[i].y-a[i].x<=k)continue;
mx=max(a[i].x+a[i].y-k,mx);
mn=min(a[i].x+a[i].y+k,mn);
}
if(mx>mn)return false;
mx=-1e9,mn=1e9;
for(int i=1;i<=m;i++)
{
if(a[i].y-a[i].x<=k)continue;
mx=max(a[i].x-a[i].y-k,mx);
mn=min(a[i].x-a[i].y+k,mn);
}
if(mx>mn)return false;
return true;
}
int main()
{
freopen("jh.in","r",stdin);
int t;scanf("%d%d",&n,&t);
while(t--)
{
int x,y;scanf("%d%d",&x,&y);
if(x>y)swap(x,y);
if(x!=y){a[++m].x=x;a[m].y=y;}
}
int l=0,r=N,ans;
while(l<=r)
{
int mid=(l+r)/2;
if(check(mid))
{
ans=mid;r=mid-1;
}
else l=mid+1;
}
printf("%d",ans);
return 0;
}
位运算(bit)
Time Limit:2000ms Memory Limit:64MB
题目描述
lyk 最近在研究位运算。它发现除了 xor,or,and 外还有很多运算。
它新定义了一种运算符“#”。
具体地,可以由 4 个参数来表示。令 a[i][j]表示 i#j。其中 i,j 与 a 的值均∈[0,1]。
当然问题可以扩展为>1 的情况,具体地,可以将两个数分解为 p 位,然后对于每一位
执行上述的位运算,再将这个二进制串转化为十进制就可以了。
例如当 a[0][0]=0,a[1][1]=0,a[0][1]=1,a[1][0]=1 时,3#4 在 p=3 时等于 7,2#3 在
p=4 时等于 1(实际上就是异或运算)。
现在 lyk 想知道的是,已知一个长为n的数列 b。它任意选取一个序列 c,满
足 c1<c2<...<ck,其中 1≤c1 且 ck≤n,定义这个序列的价值为b{c1}#b{c2}#...#b{ck}
的平方。
这里我们假设 k 是正整数,因此满足条件的 c 的序列个数一定是 2^n−1 。lyk 想知道
所有满足条件的序列的价值总和是多少。
由于答案可能很大,你只需输出答案对 1,000,000,007 取模后的结果即可。
输入格式(bit.in)
第一行两个数 n,p。
第二行 4 个数表示 a[0][0],a[0][1],a[1][0],a[1][1]。
第三行 n 个数表示 bi(0<=bi<2^p)。
输出格式(bit.out)
一个数表示答案。
输入样例
3 30
0 1 1 0
1 2 3
输出样例
28
样例解释
{1}的价值为 1,{2}的价值为4,{3}的价值为 9,{1,2}的价值为 9, {1,3}的价值为 4,{2,3}
的价值为1,{1,2,3}的价值为0,因此7个子集的价值总和为 28。
数据范围
总共 10 组数据。
对于第 1,2 组数据 n<=5。
对于第 3,4 组数据 n<=10000,p=1。
对于第 5 组数据 a 值均为 0。
对于第 6 组数据 a 值均为 1。
对于第 7 组数据 a[0][0]=0,a[1][0]=0,a[1][1]=1,a[0][1]=1。
对于第 8,9 组数据 n<=1000,p<=10。
对于所有数据 n<=10000,1<=p<=30。
思路:
将一个数的平方拆成若干幂次的平方,例如7^2=(1+2+4)^2。
观察答案的形式为1*1+1*2+1*4+2*1+2*2+2*4+4*1+4*2+4*4。
枚举每两位,令dp[i][j][k]表示到第i位,此时第一位为j,第二位为k的方案总数,累加即可。
代码:
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <assert.h>
using namespace std;
long long sum,ans;
int dp[3][3],DP[3][3];
int a[3][3],n,l,k,b[50005],X1,Y1,X2,Y2,i,j;
const int MOD=1000000007;
int main()
{
freopen("bit.in","r",stdin);
freopen("bit.out","w",stdout);
scanf("%d%d",&n,&k); assert(1<=n&&n<=50000&&1<=k&&k<=30);
scanf("%d%d%d%d",&a[0][0],&a[0][1],&a[1][0],&a[1][1]);
assert(0<=a[0][0] && a[0][0]<=1 && 0<=a[0][1] && a[0][1]<=1 && 0<=a[1][0] && a[1][0]<=1 && 0<=a[1][1] && a[1][1]<=1);
for (i=1; i<=n; i++)
{
scanf("%d",&b[i]);
assert(0<=b[i] && b[i]<=(1<<k));
}
for (i=0; i<k; i++)
for (j=i; j<k; j++)
{
for (X1=0; X1<2; X1++)
for (Y1=0; Y1<2; Y1++) dp[X1][Y1]=DP[X1][Y1]=0;
for (l=1; l<=n; l++)
{
for (X1=0; X1<2; X1++)
for (Y1=0; Y1<2; Y1++)
if (dp[X1][Y1])
{
if (b[l]&(1<<i))
X2=a[X1][1]; else X2=a[X1][0];
if (b[l]&(1<<j))
Y2=a[Y1][1]; else Y2=a[Y1][0];
DP[X2][Y2]+=dp[X1][Y1];
if (DP[X2][Y2]>=MOD) DP[X2][Y2]-=MOD;
}
if (b[l]&(1<<i)) X2=1; else X2=0;
if (b[l]&(1<<j)) Y2=1; else Y2=0;
DP[X2][Y2]++; if (DP[X2][Y2]>=MOD) DP[X2][Y2]-=MOD;
for (X1=0; X1<2; X1++)
for (Y1=0; Y1<2; Y1++)
{
dp[X1][Y1]+=DP[X1][Y1];
if (dp[X1][Y1]>=MOD) dp[X1][Y1]-=MOD;
DP[X1][Y1]=0;
}
}
sum=(1<<i);
sum*=(1<<j);
sum%=MOD;
if (i!=j) sum+=sum;
ans+=sum*dp[1][1];
ans%=MOD;
}
cout<<ans;
return 0;
}
蚂蚁运输(ant)
Time Limit:5000ms Memory Limit:64MB
题目描述
LYK 在观察一些蚂蚁。
蚂蚁想要积攒一些货物来过冬。积攒货物的方法是这样的。
对于第i只蚂蚁, 它要从li出发, 拿起货物, 走到ri处放下货物, 需要消耗的时间为|ri-li|。
而且所有蚂蚁都是可以同时进行的,也就是说,假如有 m 只蚂蚁,那么运输完货物的时间
为 max{|ri-li|}。
LYK 决定帮蚂蚁一把,它发明了空间传输装置。具体地,当蚂蚁走到 X 处时,它可以不
耗费任意时间的情况下瞬间到达 Y,或者从 Y 到达 X。也就是说,一个蚂蚁如果使用了空间
传输装置,它耗费的时间将会是 min{|li-X|+|ri-Y|,|li-Y|+|ri-X|},当然蚂蚁也可以选择徒步走
到目标点。
由于空间传输装置非常昂贵,LYK 打算只建造这么一台机器。并且 LYK 想让蚂蚁运输完
货物的时间尽可能短,你能帮帮它吗?
输入格式(ant.in)
第一行两个数 n,m,n 表示 li,ri 的最大值。
接下来 m 行,每行两个数 li,ri。
输出格式(ant.out)
一个数表示答案
输入样例
5 2
1 3
2 4
输出样例
1
数据范围
对于 20%的数据 n,m<=100。
对于 40%的数据 n,m<=1000。
对于 60%的数据 n<=100000,m<=1000。
对于 80%的数据 n,m<=100000。
对于 100%的数据 n,m<=1000000,1<=li,ri<=n(li=ri 时你甚至可以无视这只蚂蚁) 。
样例解释
令空间传输装置的参数中 X=2,Y=3 或者 X=3,Y=2 都行。
思路:
观察到答案具有可二分性。我们可以二分答案。
由于路径都是无向的,因此对于任意一条方案li,ri若li>ri则可以交换li和ri。
我们设二分的答案为x。
对于那些li+x>=ri的方案我们直接默认为可行。
我们规定X<=Y。
对于li+x<ri的方案。只有一种可能能够完成,即,从li出发,到达X,到达Y,到达ri。
也就是说,如果X确定,Y存在于一段区间内。
我们来看li>=X的情况。
先求出X=n时符合条件的Y的区间。当X慢慢减少时,这个区间肯定也在慢慢合拢,并且满足li>=X的条件也会越来越多。我们可以线性求出所有这个区间。
对于li<X的情况也同理。
这样就能做到线性判断,总复杂度nlgn。(这里默认n与m同阶)
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<assert.h>
using namespace std;
int n,m,L[1000005],R[1000005],l,r,mid,i,ll[1000005],rr[1000005];
struct node {int x,y;} t[1000005];
int cmp(node i,node j) {return i.x<j.x;}
bool OK(int x) {
int cnt=m,MIN=2000005,MAX=-5;
for (int i=n; i>=1; i--) {
while (cnt && t[cnt].x==i) {
if (t[cnt].y-t[cnt].x>x) MIN=min(MIN,t[cnt].y-t[cnt].x);
if (t[cnt].y-t[cnt].x>x) MAX=max(MAX,t[cnt].x+t[cnt].y);
cnt--;
}
ll[i]=MAX-x-i; rr[i]=MIN+x+i;
}
cnt=1; MIN=2000005; MAX=-5;
for (int i=1; i<=n; i++) {
while (cnt<=m && t[cnt].x==i) {
if (t[cnt].y-t[cnt].x>x) MIN=min(MIN,t[cnt].x+t[cnt].y);
if (t[cnt].y-t[cnt].x>x) MAX=max(MAX,t[cnt].y-t[cnt].x);
cnt++;
}
ll[i]=max(ll[i],MAX-x+i);
rr[i]=min(rr[i],MIN+x-i);
if (ll[i]<=rr[i]) return true;
}
return false;
}
int main() {
freopen("ant.in","r",stdin);
freopen("ant.out","w",stdout);
scanf("%d%d",&n,&m);
assert(1<=n && n<=1000000 && 1<=m && m<=1000000);
for (i=1; i<=m; i++) {
scanf("%d%d",&L[i],&R[i]);
assert(1<=L[i] && L[i]<=n && 1<=R[i] && R[i]<=n);
if (L[i]>R[i]) swap(L[i],R[i]);
t[i].x=L[i]; t[i].y=R[i];
}
sort(t+1,t+m+1,cmp);
l=0; r=n; mid=(l+r)/2;
while (l<=r) {
if (OK(mid)) {r=mid-1; mid=(l+r)/2;}
else{
l=mid+1;
mid=(l+r)/2;
}
}
cout<<l;
return 0;
}