内有小量私货,慎重食用
全是 WATER 你还上不了400???
蒟蒻之疑惑
T1 Count
考点:无
我是真的不知道这能有神马考点?
你甚至不需要离散化
每个数均不超过 1500000000 ( 1.5 × 1 0 9 ) 1500000000(1.5\times10^9) 1500000000(1.5×109)
自然,桶排是不行的,考虑快排
将原数组进行排序后,我们依次遍历,如果 a [ i ] = a [ i + 1 ] a[\ i\ ]=a[\ i+1\ ] a[ i ]=a[ i+1 ] ,就说明当前的元素没有统计完, s u m sum sum 自增,否则,说明当前元素已经统计完了,输出对应的值,并将 s u m sum sum 清空
#include<cstdio>
#include<algorithm>
using namespace std;
int a[200005];
int read(){
int x=1,b=0;
char ch=getchar();
while(!(ch>='0'&&ch<='9')){
if(ch=='-'){
x=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
b=b*10+ch-'0';
ch=getchar();
}
return x*b;
}
void write(int num){
if(num>=10){
write(num/10);
}
putchar(num%10+'0');
} //考虑输入输出较大,采用快读快写
int main(){
int n,ans=1; //由于开始统计时,至少都有1个元素,所以初始值为1
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
}
sort(a+1,a+1+n);
for(int i=1;i<=n;i++){
if(a[i]!=a[i+1]){ //当前元素统计完成
write(a[i]);
putchar(' ');
write(ans);
putchar('\n'); //输出
ans=1;
}else{
ans++; //同上
}
}
return 0;
}
T2 Friend
考点:埃氏筛
真的,你甚至不需要欧拉筛
其实就是将总的元素数量减去质数的元素数量即可
#include<cstdio>
bool flag[2000005];
void build(int n){ //埃氏筛模板
for(int i=2;i<=n;i++){
if(flag[i]==0){
for(int j=i+i;j<=n;j+=i){
flag[j]=1;
}
}
}
}
int main(){
int n,ans=1; //因为 1 很特殊,所以特殊处理
scanf("%d",&n);
build(n);
for(int i=2;i<=n;i++){ // 1 特殊处理,循环从2开始
if(flag[i]){ //不是质数
ans++;
}
}
printf("%d",ans);
return 0;
}
T3 Self-Number
考点:暴力
我的做法和郭老师的做法不太一样…
当我们枚举到每一个 i i i 时,我们把以 i i i 作为发生器的数标记一下,然后再跑一遍循环,把所有没有标记的数塞进答案数组,最后输出——cqbzgm
下面是蒟蒻的接近于打表的做法:
考虑一位数 a ‾ \overline{a} a ,以 a ‾ \overline{a} a 作为发生器的数为 a + a = 2 a a+a=2a a+a=2a
考虑两位数 a b ‾ \overline{ab} ab ,以 a b ‾ \overline{ab} ab 作为发生器的数为 10 a + b + a + b = 11 b + 2 b 10a+b+a+b=11b+2b 10a+b+a+b=11b+2b
考虑三位数 a b c ‾ \overline{abc} abc ,以 a b c ‾ \overline{abc} abc 作为发生器的数为 100 a + 10 b + c + a + b + c = 101 a + 11 b + 2 c 100a+10b+c+a+b+c=101a+11b+2c 100a+10b+c+a+b+c=101a+11b+2c
由此,我们想到:
枚举每一个数位(因为 1 ≤ N ≤ 1 0 7 1\le N\le10^7 1≤N≤107 ,所以最多枚举 7 7 7 位),如果以该书为发生器所得到的数在 N N N 的范围以内,我们将其进行标记
最后,把所有没有标记的数塞进答案数组并输出
#include<cstdio>
bool flag[10000005];
int ans[10000005],cnt;
void build(int num){
for(int a1=1;a1<=9;a1++){
for(int a2=0;a2<=9;a2++){
for(int a3=0;a3<=9;a3++){
for(int a4=0;a4<=9;a4++){
for(int a5=0;a5<=9;a5++){
for(int a6=0;a6<=9;a6++){
for(int a7=0;a7<=9;a7++){ //枚举7个数位
if(a1*2<=num){ //处理1位数
flag[a1*2]=1;
}
if(a1*11+a2*2<=num){ //处理2位数
flag[a1*11+a2*2]=1;
}
if(a1*101+a2*11+a3*2<=num){ //处理3位数
flag[a1*101+a2*11+a3*2]=1;
}
if(a1*1001+a2*101+a3*11+a4*2<=num){ //处理4位数
flag[a1*1001+a2*101+a3*11+a4*2]=1;
}
if(a1*10001+a2*1001+a3*101+a4*11+a5*2<=num){ //处理5位数
flag[a1*10001+a2*1001+a3*101+a4*11+a5*2]=1;
}
if(a1*100001+a2*10001+a3*1001+a4*101+a5*11+a6*2<=num){ //处理6位数
flag[a1*100001+a2*10001+a3*1001+a4*101+a5*11+a6*2]=1;
}
if(a1*1000001+a2*100001+a3*10001+a4*1001+a5*101+a6*11+a7*2<=num){ //处理7位数
flag[a1*1000001+a2*100001+a3*10001+a4*1001+a5*101+a6*11+a7*2]=1;
}
}
}
}
}
}
}
}
}
int read(){
int x=1,b=0;
char ch=getchar();
while(!(ch>='0'&&ch<='9')){
if(ch=='-'){
x=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
b=b*10+ch-'0';
ch=getchar();
}
return x*b;
}
void write(int num){
if(num>=10){
write(num/10);
}
putchar(num%10+'0');
} //快读快写
int main(){
int n,m,x;
n=read(),m=read();
build(n); //标记自身数和非自身数
for(int i=1;i<=n;i++){
if(flag[i]==0){ //自身数
ans[++cnt]=i; //塞入答案中
}
}
write(cnt);
putchar('\n');
for(int i=1;i<=m;i++){
x=read();
write(ans[x]); //输出
putchar(' ');
}
return 0;
}
T4 [CQOI2006]简单题
蒟蒻来教大家手切省选啦
考点:树状数组
就是一个树状数组板题,只需要最后输出时模 2 2 2 即可
#include<cstdio>
int Bit[100005],n,m,x,y;
int Low_Bit(int num){
return num&-num;
}
void Up_Date(int num,int sum){
for(int i=num;i<=n;i+=Low_Bit(i)){
Bit[i]+=sum;
}
}
long long int Sum(int num){
long long int ans=0;
for(int i=num;i>=1;i-=Low_Bit(i)){
ans+=Bit[i];
}
return ans;
} //树状数组模板
int read(){
int a=1,b=0;
char ch=getchar();
while(!(ch>='0'&&ch<='9')){
if(ch=='-'){
a=(~a)+1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
b=(b<<1)+(b<<3)+ch-'0';
ch=getchar();
}
return a*b;
} //快读
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
x=read();
if(x==1){
x=read(),y=read();
Up_Date(x,1);
Up_Date(y+1,-1); //区间修改,单点查询模板
}else{
x=read();
printf("%lld\n",Sum(x)%2); //结果模 2 即可
}
}
return 0;
}
T5 火神之友
巴巴托斯(某知名游戏的风神),干点正事吧
考点:并查集
我们考虑如下图所示的情况:
我们尝试从左到右进行枚举,当枚举到第 2 2 2 位的时候,刚好出现了两个 3 3 3 ,此时,我们需要进行答案的累加,及 U p D a t e ( 1 , 1 ) UpDate(1,1) UpDate(1,1)
当枚举到第 3 3 3 位的时候,考虑区间 [ 2 , 3 ] [\ 2,3\ ] [ 2,3 ] ,是满足要求的,进行 U p D a t e ( 2 , 1 ) UpDate(2,1) UpDate(2,1)
然而,考虑区间 [ 1 , 3 ] [\ 1,3\ ] [ 1,3 ] ,出现了 3 3 3 个 3 3 3 ,是不满足要求的,又因为我们已经加了两个 1 1 1 了,所以,我们在第 1 1 1 位进行 − 2 -2 −2 操作即可抵消两个 + 1 +1 +1 ,即 U p D a t e ( 1 , − 2 ) UpDate(1,-2) UpDate(1,−2)
当枚举到第 3 3 3 位的时候,我们可以依葫芦画瓢,进行 U p D a t e ( 3 , 1 ) UpDate(3,1) UpDate(3,1) 和 U p D a t e ( 2 , − 2 ) UpDate(2,-2) UpDate(2,−2) 操作
但是,考虑区间
[
1
,
4
]
[\ 1,4\ ]
[ 1,4 ] ,我们又多减了
1
1
1 ,怎么办?加回来就行了(真的很潦草,但就是正解),即
U
p
D
a
t
e
(
1
,
1
)
UpDate(1,1)
UpDate(1,1)
就这么:我们可以得到一般性的规律:对于第 i i i 号位,我们需要进行如下操作: U p D a t e ( i − 1 , 1 ) ( 1 < i ) , U p D a t e ( i − 2 , − 2 ) ( 2 < i ) , U p D a t e ( i − 3 , 1 ) ( 3 < i ) UpDate(i-1,1)(1<i),UpDate(i-2,-2)(2<i),UpDate(i-3,1)(3<i) UpDate(i−1,1)(1<i),UpDate(i−2,−2)(2<i),UpDate(i−3,1)(3<i)
对于每一个区间,我们也需要注意:
我们需要按照每一个区间右端点从小到大进行排序,再依次处理(即先处理靠右的区间)
原因很简单,因为我们在处理靠左的区间的时候,可能会多出一些 -2 和 +1 操作,然而,对于靠右的区间来说,这些操作可能会对其正确结果产生干扰,所以,要先处理靠右的区间
另外,因为靠后的操作会对靠右的区间造成干扰,所以要边处理边记录答案
BUT!
以上所讲都是元素相同的情况,那不同的情况呢?(不同情况自然白给)
我们可以将所有的相同元素全部塞入一条链中进行处理,这样,不同的元素之间就互不干扰,在进行答案累加时,所有答案又能加在一起
下面是完整代码:
#include<map>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[500005],n,m,x,y;
int Bit[500005],last[500005];
map<int,int> ma; //用来记录每一个元素是否出现过以及上一个对应元素的下标
struct node{
int l,r,id;
}q[500005]; //记录每一个问题区间
int ans[500005];
int Low_Bit(int num){
return num&-num;
}
void Up_Date(int num,int sum){
for(int i=num;i<=n;i+=Low_Bit(i)){
Bit[i]+=sum;
}
}
int Sum(int num){
int ans=0;
for(int i=num;i>=1;i-=Low_Bit(i)){
ans+=Bit[i];
}
return ans;
} //树状数组模板
bool cmp(node a,node b){
return a.r<b.r;
} //右端点排序
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(ma.find(a[i])!=ma.end()){ //该元素出现过
last[i]=ma[a[i]]; //将前驱对应下标进行赋值
}else{
last[i]=-1; //第一个,没有前驱
}
ma[a[i]]=i; //更新该元素前驱下标
}
for(int i=1;i<=m;i++){
scanf("%d%d",&q[i].l,&q[i].r); //输入区间
q[i].id=i;
}
sort(q+1,q+1+m,cmp);
int tot=1;
for(int i=1;i<=m;i++){
while(tot<=q[i].r){ //在完成一个区间处理前一直处理
if(last[tot]!=-1){
Up_Date(last[tot],1);
if(last[last[tot]]!=-1){
Up_Date(last[last[tot]],-2);
if(last[last[last[tot]]]!=-1){
Up_Date(last[last[last[tot]]],1);
}
}
} //对应处理,上文已提
tot++;
}
ans[q[i].id]=Sum(q[i].r)-Sum(q[i].l-1); //答案赋值
}
for(int i=1;i<=m;i++){
printf("%d\n",ans[i]); //输出
}
return 0;
}