1109-1110两天时间两场模拟赛,总结一下;
1.prime
质数也叫素数,指除了1和它本身没有其他因子的整数。
质数是数论中很特殊的数,作用也很大,小x也在研究。
小x对小y说,你给我一个数字,我可以告诉你这个数字是不是质数。读题的你肯定很不屑小x的自大,这个还用说,只要学习OI的都会。
那么小x的问题就改为:给你一个区间[X,Y],问从X到Y有多少个质数。
题意:给出一个区间,问在这个区间有多少素数?区间大小10^6,区间范围int;
题解:这种题很容易想到筛法,int的数据范围,质因数不超过50000,枚举1-50000内所有质数,然后区间跳格子一样的一个个标记上,最后统计有多少个没被标记即可;
复杂度:nlogn (自然对数)
1 /*chad*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 using namespace std; 9 const int maxn(1000005),inf(1000000); 10 #define FILE "prime" 11 #define LL long long 12 #define up(i,j,n) for(LL i=(j);(i)<=(n);(i)++) 13 namespace IO{ 14 char buf[1<<15],*fs,*ft; 15 LL gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} 16 LL read(){ 17 LL x=0,ch=gc(),f=0; 18 while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();} 19 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();} 20 return f?-x:x; 21 } 22 }using namespace IO; 23 LL x,y; 24 LL prime[maxn],b[maxn],tail=0; 25 void getprime(){ 26 for(LL i=2;i<=inf;i++){ 27 if(!b[i])prime[++tail]=i; 28 for(LL j=1;prime[j]*i<=inf&&j<=tail;j++){ 29 b[prime[j]*i]=1; 30 if(i%prime[j]==0)break; 31 } 32 } 33 } 34 LL f[maxn]; 35 int main(){ 36 freopen(FILE".in","r",stdin); 37 freopen(FILE".out","w",stdout); 38 cin>>x>>y; 39 getprime(); 40 LL ans=0,s; 41 for(LL i=1;i<=tail&&prime[i]<=y;i++){ 42 s=x+(prime[i]-x%prime[i])%prime[i]; 43 for(LL j=s;j<=y;j+=prime[i]){ 44 f[j-x]=1; 45 } 46 if(prime[i]>=x&&prime[i]<=y)f[prime[i]-x]=0; 47 } 48 for(LL i=0;i<=y-x;i++)if(!f[i])ans++; 49 if(x==1)ans--; 50 cout<<ans<<endl; 51 return 0; 52 }
2. 飞天
小x和他的小伙伴们创作了一个舞蹈——飞天。
这个舞蹈由N个小朋友一起来完成,编号分别为[1..N]。每位小朋友都被威亚吊着飞在天空中,每个人的高度为H[i],做着各种高难度动作。
小x作为导演,又有了新的想法,他把舞蹈分为M大部分。每部分只挑选编号连续的某些小朋友,升到相同的高度,做相应的表演。等本部分表演结束,小朋友的高度会自动恢复到原来的高度。
但是,现在每次调整高度难坏了小x,并且每位选手调整的高度增加或减少1,小x就要花费单位为1的能量,小x就想知道,怎么安排调整高度,能让自己消耗的能量最少。
【数据范围】
对于50%的数据 n≤500,m≤1000;
对于80%的数据 n≤1000,m≤100000;
对于100%的数据n≤1000,m≤200000;
答案小于2^64。
题意:给定m个区间,求出每个区间所有数与中位数的绝对值之和;
题解:m特别大,肯定不能直接求,由于n只有1000,考虑做一个预处理;
设f[i][j]表示i到j的所求答案,n^2的枚举,怎么在加入一个新数后维护答案?
用两个堆(priority_queue)可以做到log级别的转移,O(n^2logn)的预处理,代码简单,速度不错;
我使用的是treap找第k大的方法找中位数,顺便维护比中位数小的数的和,尽管很直观,但我考试时就感觉不对,我只需要维护一个中位数和比中位数小的数的和,就用上了平衡树,太浪费了(我的预感也确实是对的了,标解是堆);
chty用的是莫队,使用平衡树(log)转移,复杂度O(n1/2mlog);
学长使用线段树维护一个区间内的所有的数值,这种方法在考试时考虑过,但hi无数据范围,我不太敢用,放弃了,使用了平衡树(平衡树调得我欲仙欲死,还需要提升代码能力);
1 /*chad*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 #include<ctime> 9 #include<string> 10 using namespace std; 11 const int maxn(1005),inf(1000000); 12 #define FILE "sky" 13 #define LL long long 14 #define up(i,j,n) for(LL i=(j);(i)<=(n);(i)++) 15 namespace IO{ 16 char buf[1<<15],*fs,*ft; 17 LL gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} 18 LL read(){ 19 LL x=0,ch=gc(),f=0; 20 while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();} 21 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();} 22 return f?-x:x; 23 } 24 }using namespace IO; 25 LL a[maxn]; 26 int n,m; 27 struct node{ 28 LL ch[2]; 29 LL r,v,siz,cnt,sum; 30 LL cmp(LL x){return x>v;} 31 }e[maxn]; 32 LL len=0,root=0; 33 void updata(LL o){e[o].siz=e[e[o].ch[0]].siz+e[e[o].ch[1]].siz+e[o].cnt;e[o].sum=e[e[o].ch[0]].sum+e[e[o].ch[1]].sum+e[o].v*e[o].cnt;} 34 void rotate(LL &o,LL d){ 35 LL k=e[o].ch[d]; 36 e[o].ch[d]=e[k].ch[d^1]; 37 e[k].ch[d^1]=o; 38 updata(o);updata(k); 39 o=k; 40 } 41 void insert(LL &o,LL x){ 42 if(o==0){o=++len;e[o].v=x;e[o].r=rand();e[o].cnt=1;e[o].siz=1;e[o].ch[0]=e[o].ch[1]=0;e[o].sum=x;return;} 43 if(x==e[o].v){e[o].cnt++;updata(o);return;} 44 LL d=e[o].cmp(x); 45 insert(e[o].ch[d],x); 46 updata(o); 47 if(e[e[o].ch[d]].r>e[o].r)rotate(o,d); 48 } 49 pair<LL,LL> getK(LL k){ 50 LL o=root; 51 LL left=0; 52 while(o){ 53 if(k>e[e[o].ch[0]].siz&&k<=e[e[o].ch[0]].siz+e[o].cnt)return make_pair(o,left+e[e[o].ch[0]].sum+e[o].v*e[o].cnt-e[o].v*(e[e[o].ch[0]].siz+e[o].cnt-k+1)); 54 if(k>e[e[o].ch[0]].siz+e[o].cnt)k=k-e[e[o].ch[0]].siz-e[o].cnt,left+=e[e[o].ch[0]].sum+e[o].cnt*e[o].v,o=e[o].ch[1]; 55 else o=e[o].ch[0]; 56 } 57 return make_pair(o,left); 58 } 59 LL f[maxn][maxn]; 60 int main(){ 61 freopen(FILE".in","r",stdin); 62 freopen(FILE".out","w",stdout); 63 n=read(),m=read(); 64 up(i,1,n)a[i]=read(); 65 LL sum=0,o,mid; 66 srand(time(0)); 67 for(LL i=1;i<=n;i++){ 68 len=0;root=0;sum=0; 69 for(LL j=i;j<=n;j++){ 70 insert(root,a[j]); 71 sum+=a[j]; 72 pair<LL,LL> p=getK((j-i+1)/2+1); 73 o=p.first; 74 mid=e[o].v; 75 f[i][j]+=(j-i+1)/2*mid-p.second; 76 f[i][j]+=sum-p.second-mid-mid*(j-i+1-((j-i+1)/2+1)); 77 } 78 } 79 int x,y; 80 LL ans=0; 81 while(m--){ 82 x=read(),y=read(); 83 ans+=f[x][y]; 84 } 85 cout<<ans<<endl; 86 return 0; 87 }
3.树
Fanvree很聪明,解决难题时他总会把问题简单化。
例如,他就整天喜欢把图转化为树。但是他不会缩环,那他怎么转化呢?
这是一个有n个点m条双向边的图,Fanvree会选定一个节点,然后删掉这个节点和这个点连出去的边,如果变成了一棵树,那么这个节点便是可行的,什么是树呢?树也即无简单环的无向连通图。
现在你需要告诉Fanvree可能的节点是什么。
题意:让你找到图上的所有节点,使删掉它后图成为一棵树;
题解:
要找到一个节点使删掉它之后图变成一颗树,本身就有着极苛刻的条件,只要这个点的度数符合要求就可以;
扫一遍度数,再用tarjan判断一下这个点是不是割点,符合条件输出;
1 /*chad*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 #include<ctime> 9 #include<string> 10 using namespace std; 11 const int maxn(200005),inf(1000000000); 12 #define FILE "tree" 13 #define LL long long 14 #define up(i,j,n) for(LL i=(j);(i)<=(n);(i)++) 15 namespace IO{ 16 char buf[1<<15],*fs,*ft; 17 LL gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} 18 LL read(){ 19 LL x=0,ch=gc(),f=0; 20 while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();} 21 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();} 22 return f?-x:x; 23 } 24 }using namespace IO; 25 int n,m; 26 struct node{ 27 int y,next; 28 }e[maxn<<1]; 29 int linkk[maxn],len=0,ru[maxn]; 30 void insert(int x,int y){ 31 e[++len].y=y; 32 e[len].next=linkk[x]; 33 linkk[x]=len; 34 } 35 int pre[maxn],low[maxn],dfs_clock=0,child[maxn],vis[maxn]; 36 void dfs(int x,int fa){ 37 pre[x]=low[x]=++dfs_clock; 38 for(int i=linkk[x];i;i=e[i].next){ 39 if(e[i].y==fa)continue; 40 if(!pre[e[i].y]){ 41 child[x]++; 42 dfs(e[i].y,x); 43 if(low[e[i].y]>=pre[x])vis[x]=1; 44 low[x]=min(low[x],low[e[i].y]); 45 } 46 else low[x]=min(low[x],pre[e[i].y]); 47 } 48 if(x==1&&child[x]==1)vis[x]=0; 49 } 50 int q[maxn],tail=0; 51 int main(){ 52 n=read(),m=read(); 53 int x,y; 54 up(i,1,m){ 55 x=read(),y=read(); 56 insert(x,y);insert(y,x); 57 ru[x]++,ru[y]++; 58 } 59 dfs(1,0); 60 for(int i=1;i<=n;i++)if(ru[i]==m-n+2&&!vis[i])q[++tail]=i; 61 printf("%d ",tail); 62 for(int i=1;i<=tail;i++)printf("%d ",q[i]); 63 return 0; 64 }
1.一道usaco的改编题
现在给定一个特殊的计数方式,混合进制数。也就是给定一个多位数,每位数上的进制都是不一样的。
比如给定一个三位数:这三位数的进制分别是2 3 2.也就是最小的位数逢2进1,次小位数逢3进制,最高位逢2进1.
那么,这个混合进制数最小数是0,最大数是121。一共有2*3*2=12个数。
分别是:000,001,010,011,020,021,100,101,110,111,120,121。
如果我想知道第7大的数,就是100.
现在的问题就是,给你每个位数上的进制,你找出从0开始,第k个数是多少?
1 /*chad*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 #include<ctime> 9 #include<string> 10 using namespace std; 11 const int maxn(50),inf(1000000000); 12 #define FILE "spehex" 13 #define LL long long 14 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++) 15 namespace IO{ 16 char buf[1<<15],*fs,*ft; 17 int gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} 18 LL read(){ 19 LL x=0,ch=gc(),f=0; 20 while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();} 21 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();} 22 return f?-x:x; 23 } 24 }using namespace IO; 25 int n,a[maxn],k,m; 26 char s[maxn]; 27 int main(){ 28 freopen(FILE".in","r",stdin); 29 freopen(FILE".out","w",stdout); 30 n=read(); 31 for(int i=1;i<=n;i++)a[i]=read(); 32 m=read(); 33 while(m--){ 34 k=read();k--; 35 if(k<0){ 36 printf("0 "); 37 continue; 38 } 39 for(int i=n;i>=1;i--){ 40 s[i]=k%a[i]+'0'; 41 k/=a[i]; 42 if(!k){ 43 printf("%s ",s+i); 44 break; 45 } 46 } 47 if(k)printf("-1 "); 48 } 49 return 0; 50 }
题解:模拟
2.旅行
小x要去旅游了。他决定开一辆耗油量很高的巨大的拉风的tank去旅游了。
这辆tank有一个巨大的油箱,可以装满G升燃油。tank是很费油的,每升燃油只够tank跑一公里,而小x的旅程要有D公里要跑。
虽然油箱很大,但是,显然旅途中是需要加油的。
小x得到了旅途中加油站的信息,一共有N个加油站,第i个加油站距离起点的距离为X_i(0 <= X_i <= D),每公升燃油价格为Y_i元(1 <= Y_i <= 1,000,000)。
现在小x想知道,如果出发时,tank里已经有B公升燃油(0 <= B <= D),那么,他最少花费多少钱,可以完成整个旅途。
如果中间因为燃油不够而无法完成旅程,那么你需要输出-1.
30% 数据保证 N<=100 G<=1000 D<=10000
60% 数据保证 N<=5000 G<=1000 D<=10000000
100% 数据 1 <= G <= 1,000,000 1 <= D <= 1,000,000,000 1 <= N <= 50,000
1 /*chad*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 #include<ctime> 9 #include<string> 10 #include<map> 11 #include<set> 12 using namespace std; 13 const int maxn(50100),inf(1000000000); 14 #define FILE "fule" 15 #define LL long long 16 #define up(i,j,n) for(LL i=(j);(i)<=(n);(i)++) 17 namespace IO{ 18 char buf[1<<15],*fs,*ft; 19 LL gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} 20 LL read(){ 21 LL x=0,ch=gc(),f=0; 22 while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();} 23 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();} 24 return f?-x:x; 25 } 26 }using namespace IO; 27 LL n,g,b,d; 28 struct node{ 29 LL d,y; 30 bool operator<(const node& b)const{return d<b.d;} 31 }e[maxn]; 32 LL q[maxn],r[maxn],v[maxn],head=1,tail=0,sum=0; 33 LL ans=0; 34 void print(LL x){printf("%d ",x);exit(0);} 35 int main(){ 36 freopen(FILE".in","r",stdin); 37 freopen(FILE".out","w",stdout); 38 n=read(),g=read(),sum=b=read(),d=read(); 39 up(i,1,n)e[i].d=read(),e[i].y=read(); 40 n++;e[n].d=d;e[n].y=0; 41 sort(e+1,e+n+1); 42 up(i,1,n)r[i]=e[i].d-e[i-1].d; 43 q[++tail]=b;v[tail]=0; 44 for(LL i=1;i<=n;i++){ 45 if(sum-r[i]<0)print(-1); 46 while(r[i]){ 47 if(r[i]>=q[head])r[i]-=q[head],sum-=q[head],head++; 48 else q[head]-=r[i],sum-=r[i],r[i]=0; 49 } 50 while(v[tail]>e[i].y&&head<=tail)sum-=q[tail],ans-=q[tail]*v[tail],tail--; 51 q[++tail]=g-sum;v[tail]=e[i].y;ans+=e[i].y*(g-sum);sum=g; 52 } 53 cout<<ans<<endl; 54 return 0; 55 }
题解:
是道好题,值得一做;
最初想到设f[i][j],设状态转移方程写,复杂度O(n^3),很暴力;
考虑一下用单调队列;
我们可以想象,我们车子的油是可以退回的,也就是只有燃烧的油才会真正花钱,那么我们每次进站之后,都先将油加满,等到到了下一个加油站发现了更便宜的油,我们把贵的油都退了,再把便宜的油加满油箱,这样就可以用单调队列实现这道题;
复杂度:O(n);
3.journey
给出一个长度为 N 的由小写字母’a’~’z’和’*’组成的字符串 A,一个长度为 M 的仅由小
写字母’a’~’z’组成的字符串 B。一个’*’可以匹配任意多个字符(包括 0 个)。求在 B 的所有 循环同构串中,有多少个能够与 A 匹配。
循环同构串:就是把 B 的前 k 个字母(0<=k<M)移到结尾所得到的字符串。例如 abc 的 循环同构串有 abc、bca 和 cab。
A 与 B 匹配:若除了 A 中的’*’号可以匹配 B 中的任意多个字符外,其余字符一一对应, 则称 A 与 B 匹配。例如 a*b*c 与 aadbc 是匹配的,其中第一个*对应 ad,第二个*对应空串。
对于 30% 的测试点,M≤20。
对于 80% 的测试点,M≤200。
对于 100% 的测试点,1<=N<=100,1≤M≤100000。
1 /*chad*/ 2 #include<iostream> 3 #include<cstdio> 4 #include<cmath> 5 #include<cstdlib> 6 #include<cstring> 7 #include<algorithm> 8 #include<ctime> 9 #include<string> 10 using namespace std; 11 const int maxn(110000),inf(1000000); 12 #define FILE "journey" 13 #define LL long long 14 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++) 15 namespace IO{ 16 char buf[1<<15],*fs,*ft; 17 int gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?-1:*fs++;} 18 LL read(){ 19 LL x=0,ch=gc(),f=0; 20 while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();} 21 while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();} 22 return f?-x:x; 23 } 24 }using namespace IO; 25 char ch[maxn],s[maxn<<1]; 26 int n,m; 27 char t[110][110]; 28 int top=0,cnt[110]; 29 int next[110][110]; 30 bool vis[maxn<<1][110]; 31 int f[maxn<<1][110]; 32 int main(){ 33 freopen(FILE".in","r",stdin); 34 freopen(FILE".out","w",stdout); 35 scanf("%s",ch);scanf("%s",s); 36 n=strlen(ch);m=strlen(s); 37 for(int i=0;i<m;i++)s[i+m]=s[i]; 38 m=strlen(s); 39 for(int i=0;i<n;i++){ 40 if(ch[i]!='*'){ 41 top++; 42 for(;i<n&&ch[i]!='*';i++){ 43 t[top][cnt[top]++]=ch[i]; 44 } 45 } 46 } 47 for(int k=1;k<=top;k++){ 48 int j=-1;next[k][0]=-1; 49 for(int i=1;i<cnt[k];i++){ 50 while(j!=-1&&t[k][i]!=t[k][j+1])j=next[k][j]; 51 if(t[k][i]==t[k][j+1])j++; 52 next[k][i]=j; 53 } 54 } 55 for(int k=1;k<=top;k++){ 56 int j=-1; 57 for(int i=0;i<m;i++){ 58 while(s[i]!=t[k][j+1]&&j!=-1)j=next[k][j]; 59 if(s[i]==t[k][j+1])j++; 60 if(j==cnt[k]-1){ 61 vis[i-cnt[k]+1][k]=1; 62 j=next[k][j]; 63 } 64 } 65 } 66 memset(f,10,sizeof(f)); 67 for(int i=m-1;i>=0;i--){ 68 for(int j=1;j<=top;j++){ 69 if(vis[i][j])f[i][j]=i; 70 else f[i][j]=f[i+1][j]; 71 } 72 } 73 int ans=0; 74 for(int i=0;i<m/2;i++){ 75 if(ch[0]!='*'&&!vis[i][1])continue; 76 if(ch[n-1]!='*'&&!vis[i-cnt[top]+m/2][top])continue; 77 int now=i; 78 for(int k=1;k<=top;k++){ 79 now=f[now][k]+cnt[k]; 80 if(now>inf)break; 81 } 82 if(now<=i+m/2)ans++; 83 } 84 cout<<ans<<endl; 85 return 0; 86 }
题解:
(先吐个槽,80%的数据范围那么小,我们还有什么心思去优化,打暴力得了)
80%,nm都是100左右,如果单个匹配,参照最长公共子序列n^2dp,题目强行加了一个循环,O(n^3)80分到手;
100%,M上涨到了100000,先对M进行一下复制加长,然后处理每个A串片段在B串中出现的位置,最后枚举B串开头点,再判断一下在M的范围内是否可以找全所有片段;
复杂度O(n*m);