2018年全国多校算法寒假训练营练习比赛(第五场)
A 逆序数
题解:用树状数组进行计算有多少个前面比他大的数,只需要getsum(MAX)-getsum(a[i]),getsum(MAX)得到的是当前a[i]及出现在之前所有的数的个数,getsum(a[i])得到的是比a[i]在a[i]之前小于等于它的数的个数,相减就是比它大的个数;
1 #include<algorithm> 2 #include<cstdio> 3 #define ll long long 4 using namespace std; 5 const int N =1e5+5; 6 int a[N]; 7 int c[N]; 8 void update(int x){ 9 while(x<=N){ 10 c[x]++; 11 x+=x&-x; 12 } 13 } 14 int getsum(int x){ 15 int sum=0; 16 while(x>0){ 17 sum+=c[x]; 18 x-=x&-x; 19 } 20 return sum; 21 } 22 int main(){ 23 int n; 24 scanf("%d",&n); 25 int MAX=-1; 26 for(int i=1;i<=n;i++){ 27 scanf("%d",&a[i]); 28 MAX=max(MAX,a[i]); 29 } 30 31 ll ans=0; 32 for(int i=1;i<=n;i++){ 33 ans+=getsum(MAX)-getsum(a[i]); 34 update(a[i]); 35 } 36 printf("%lld\n",ans); 37 return 0; 38 }
题解:线段数,树状数组都可以,现在想想直接前缀和不就好了。。。。。。。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #define ll long long 6 #define lson l,m,rt<<1 7 #define rson m+1,r,rt<<1|1 8 using namespace std; 9 const int N =1e5+5; 10 int n,m; 11 ll val[4*N]; 12 int lazy[4*N]; 13 void pushup(int rt){ 14 val[rt]=val[rt<<1]+val[rt<<1|1]; 15 } 16 void build(int l,int r,int rt){ 17 if(l==r){ 18 scanf("%lld",&val[rt]); 19 return ; 20 } 21 int m=(l+r)>>1; 22 build(l,m,rt<<1); 23 build(m+1,r, rt<<1|1); 24 pushup(rt); 25 } 26 void update(int x,int c,int l,int r,int rt){ 27 val[rt]+=c; 28 int m=(l+r)>>1; 29 if(l==r)return; 30 if(m>=x)update(x,c,lson); 31 else update(x,c,rson); 32 } 33 ll query(int L,int R,int l,int r,int rt){ 34 if(L<=l&&R>=r)return val[rt]; 35 ll ans=0; 36 int m=(l+r)>>1; 37 if(m>=L)ans+=query(L,R,lson); 38 if(R>m)ans+=query(L,R,rson); 39 return ans; 40 } 41 int main(){ 42 scanf("%d %d",&n,&m); 43 build(1,n,1); 44 while(m--){ 45 int x,l,r; 46 scanf("%d%d%d",&x,&l,&r); 47 if(x==1)update(l,r,1,n,1); 48 else cout<<query(l,r,1,n,1)<<endl; 49 } 50 return 0; 51 }
C 字符串的问题
题解:KMP求出next数组,next数组就是最大匹配前后缀。设L为字符串长度则如果存在字符串中(不含两端)的子字符串和字符串的前后缀和相同,一定存在next[M]=next[L](0<M<L)
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 const int N =1e6+5; 6 int n,m; 7 int nx[N]; 8 char str[N]; 9 int num[N]; 10 int len; 11 void getnx(){ 12 int j=0,k=-1; 13 nx[0]=-1; 14 while(j<len){ 15 if(k==-1||str[j]==str[k]){ 16 nx[++j]=++k; 17 } 18 else 19 k=nx[k]; 20 } 21 } 22 int main(){ 23 scanf("%s",str); 24 len=strlen(str); 25 getnx(); 26 int l=nx[len]; 27 for(int i=1;i<len;i++){ 28 num[nx[i]]++; 29 } 30 bool flag=0; 31 while(l){ 32 if(num[l]){ 33 for(int i=0;i<l;i++)cout<<str[i]; 34 flag=1; 35 break; 36 } 37 l=nx[l]; 38 } 39 if(!flag)cout<<"Just a legend\n"; 40 return 0; 41 }
D 集合问题
题解:这题一开始我直接dfs一路找对应的数添加到集合中,结果TLE了。赛后后来看了别人的代码,用并查集来维护集合这个概念。
#include<iostream> #include<map> #include<cstring> #include<cstdio> using namespace std; const int N =1e5+5; int p[N]; int fa[N]; int find(int x){ return fa[x]==x?x:find(fa[x]); } void unite(int x,int y){ x=find(x); y=find(y); if(x!=y)fa[y]=x; } map<int,int>mp; int main(){ int n,a,b; cin>>n>>a>>b; int MAX=-1; for(int i=1;i<=n;i++){ scanf("%d",&p[i]); MAX=max(MAX,p[i]); mp[p[i]]=i; } if(MAX>max(a,b))cout<<"NO\n"; else { for(int i=0;i<=n+1;i++)fa[i]=i; for(int i=1;i<=n;i++){ if(mp[a-p[i]])unite(i,mp[a-p[i]]); else unite(i,n+1); if(mp[b-p[i]])unite(i,mp[b-p[i]]); else unite(i,0); } int u=find(0); int v=find(n+1); if(u==v)printf("NO\n"); else { printf("YES\n"); for(int i=1 ;i<=n;i++){ if(i!=1)printf(" "); int l=find(i); if(l!=u)printf("1"); else printf("0"); } } } }
E 情人节的电灯泡
题解:一开始的思路是二维化一维直接做差来写,后来想想还是不对,因为矩形一维做差肯定会T掉,因为要对每一行都要做差。接着想到二维树状数组,但是忘了怎么写。。(待补)
若要求黄色矩形内的亮灯个数,设黄色矩形左上角坐标为(a,b)则右下角为(c,d)。getsum(c,d)=黄色面积+绿色面积+红色面积-白色面积,因此黄色面积=getsum(c,d)-绿色面积-红色面积+白色面积=getsum(c,d)-getsum(c,b-1)-getsum(a-1,d)+getsum(a-1,b-1)
#include<cstdio> using namespace std; const int N =1e3+5; int data[N][N]; int sum[N][N]; void Update(int x,int y,int c){ for(int i=x;i<=N;i+=i&-i){ for(int j=y;j<=N;j+=j&-j){ sum[i][j]+=c; } } } int getsum(int x,int y){ int ans=0; for(int i=x;i>0;i-=i&-i) for(int j=y;j>0;j-=j&-j) ans+=sum[i][j]; return ans; } int main(){ int n,m; scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ scanf("%d",&data[i][j]); if(data[i][j])Update(i,j,1); } while(m--){ int f,x1,y1,x2,y2; scanf("%d %d %d",&f,&x1,&y1); if(f==1){ if(data[x1][y1]==1) Update(x1,y1,-1),data[x1][y1]=0; else Update(x1,y1,1),data[x1][y1]=1; } else { scanf("%d %d",&x2,&y2); printf("%d\n",getsum(x2,y2)+getsum(x1-1,y1-1)-getsum(x2,y1-1)-getsum(x1-1,y2)); } } return 0; }
题解:水题。。
1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 int main(){ 5 int n; 6 scanf("%d",&n); 7 int sum=0; 8 while(1){ 9 while(n){ 10 sum+=n%10; 11 n/=10; 12 } 13 if(sum/10==0)break; 14 else{ 15 n=sum; 16 sum=0; 17 } 18 } 19 cout<<sum; 20 return 0; 21 }
G 送分啦-QAQ
题解:斐波那契博弈。先手胜当且仅当n不是斐波那契数。
1 #include<iostream> 2 using namespace std; 3 const int N =1e5+5; 4 ll a[N]; 5 int main(){ 6 fio; 7 int n; 8 cin>>n; 9 a[1]=1; 10 a[0]=1; 11 int i; 12 for( i=2;;i++){ 13 a[i]=a[i-1]+a[i-2]; 14 if(a[i]>=1000000000)break; 15 } 16 for(int j=0;j<=i;j++){ 17 if(a[j]==n)return cout<<"Sha"<<endl,0; 18 } 19 cout<<"Xian"<<endl; 20 return 0; 21 }
题解:线段树区间更新+区间求和
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define ll long long 5 #define lson l,m,rt<<1 6 #define rson m+1,r,rt<<1|1 7 using namespace std; 8 const int N =1e5+5; 9 int n,m; 10 ll val[4*N]; 11 ll lazy[4*N]; 12 void pushup(int rt){ 13 val[rt]=val[rt<<1]+val[rt<<1|1]; 14 } 15 void build(int l,int r,int rt){ 16 if(l==r){ 17 scanf("%lld",&val[rt]); 18 return ; 19 } 20 int m=(l+r)>>1; 21 build(l,m,rt<<1); 22 build(m+1,r,rt<<1|1); 23 pushup(rt); 24 } 25 void pushdown(int l,int r,int rt){ 26 if(lazy[rt]){ 27 int m=(l+r)>>1; 28 lazy[rt<<1]+=lazy[rt]; 29 lazy[rt<<1|1]+=lazy[rt]; 30 val[rt<<1]+=lazy[rt]*(m-l+1); 31 val[rt<<1|1]+=lazy[rt]*(r-m); 32 lazy[rt]=0; 33 } 34 } 35 void update(int L,int R,int c,int l,int r,int rt){ 36 if(L<=l&&R>=r){ 37 lazy[rt]+=c; 38 val[rt]+=1LL*c*(r-l+1); 39 return ; 40 } 41 pushdown(l,r,rt); 42 int m=(l+r)>>1; 43 if(L<=m)update(L,R,c,lson); 44 if(R>m) update(L,R,c,rson); 45 pushup(rt); 46 } 47 ll query(int L,int R,int l,int r,int rt){ 48 if(L<=l&&R>=r){ 49 // cout<<val[rt]<<" "<<rt<<endl; 50 return val[rt]; 51 } 52 pushdown(l,r,rt); 53 ll ans=0; 54 int m=(l+r)>>1; 55 if(m>=L)ans+=query(L,R,lson); 56 if(R>m)ans+=query(L,R,rson); 57 return ans; 58 } 59 int main(){ 60 scanf("%d %d",&n,&m); 61 build(1,n,1); 62 while(m--){ 63 int l,r,c; 64 char x[5]; 65 scanf("%s%d%d",x,&l,&r); 66 if(x[0]=='C'){ 67 scanf("%d",&c); 68 update(l,r,c,1,n,1); 69 } 70 else cout<<query(l,r,1,n,1)<<endl; 71 } 72 return 0; 73 }