A
题目大概:
给出n个数,求所有的连续区间(包含区间长度为1的区间)的数字相与之和。
如7 11 9
(7) + (7 & 11) + (7 & 11 & 9) + (11) + (11 & 9) + (9) = 40
思路:
这种区间的题正常来说都需要推规律,还是与数学有关。这道题的区间长度为1e5,暴力是不行的。如果做过类似的题目。就会把这些数转换成二进制的形式,题目数字最大为1e6,为2的20次方就够了。所以开一个二维数组,n行20多列,表示这些数字的二进制形式。
至于计算相与之和。那就是每列每列的找就可以了。因为是相与,区间链式连续的,所以找出连续的1就可以了。这些1组成的连续区间的数量,就是这些数可以组成的有效连续区间的数量。区间数量乘本列的二进制权值,就是和。
连续1的区间的数量的规律就是一个等差数列,一个长度为5的数列,组成的连续区间数量可以用5+4+3+2+1来表示。分表示长度为1区间数,长度为2.。。。。然后这就是一个等差数列。
那么现在,该问题的所有地方都可以写了。
代码:
#include <bits/stdc++.h> #define PI acos(-1.0) #define ll long long using namespace std; const int MAX=1e5+10; bool vis[MAX][25]; long long a[MAX]; long long bound[25]; int main() { int c=1; for(int i=1;i<=24;i++) { bound[i]=c; c*=2; } int T; cin>>T; while(T--) { int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%I64d",&a[i]); } for(int i=0;i<n;i++) { long long cnt=a[i]; for(int j=1;j<=22;j++) { vis[i][j]=(cnt&1); cnt>>=1; } } long long ans=0; for(int i=1;i<=22;i++) { long long len=0; for(int j=0;j<n;j++) { if(vis[j][i]) len++; else { ans+=(len*(len+1))/2*bound[i]; len=0; } } ans+=(len*(len+1))/2*bound[i]; } cout<<ans<<endl; } }B
略
C
题目大概:
给出n个数,然后让每个数都在数列中找一个和他加和%1e9+7之后,最大的数。
思路:
这道题,n最大为1e5,所以n2不行,需要优化到nlogn。一般都是用二分什么的优化。
仔细分析一下这道题,数列中的数最大不超过1e9+7,所以两者加和最大不超过2e9+7,题目求%1e9+7后,最大的数,就是最接近1e9+7,或者2e9+7的数,那么可以sort一下,在区间中求两个二分,分别求最接近1e9+7和2e9+7.然后比较就可以了。
代码:
#include <bits/stdc++.h> #define ll long long using namespace std; const ll mod=1e9+7; struct node { ll val; ll id; }a[100005]; bool cmp(node s,node t) { if(s.val==t.val) return s.id<t.id; return s.val<t.val; } ll n; ll ans[100005]; ll search(ll begin,ll end,ll e) { ll mid,left=begin,right=end; while(left<=right) { mid=(left+right)>>1; if(a[mid].val>e) right=mid-1; else left=mid + 1; } return right; } int main() { int T; scanf("%d",&T); while(T--) { scanf("%lld",&n); for(int i=1;i<=n;i++) { scanf("%lld",&a[i].val); a[i].id=i; } sort(a+1,a+1+n,cmp); for(int i=1;i<=n;i++) { ll t=search(1,n,mod-a[i].val-1); if(a[i].id==a[t].id) t--; //cout<<t<<endl; if(t!=0) { ans[a[i].id]=(a[t].val+a[i].val)%mod; //cout<<ans[a[i].id]<<endl; } else ans[a[i].id]=0; //cout<<ans[a[i].id]<<endl; t=search(1,n,2*mod-a[i].val-1); if(a[i].id==a[t].id) t--; ans[a[i].id]=max(ans[a[i].id],(a[t].val+a[i].val)%mod); } for(int i=1;i<=n;i++) cout<<ans[i]<<" ";cout<<endl; } }D
题目大概:给出长度为n的字符串(只包含小写字母)m个询问,求区间l到r之间包含字母q的个数。(l r可以超过n,字符串自动加长(复制自身))。
思路:
这种题当然求一个前缀和,然后r的前缀和减去l-1的前缀和,就可以了。
代码:
#include <bits/stdc++.h> #define ll long long using namespace std; int t; char a[10005]; int dp[10005][30]; int n,q; int l,r; char b; int sum; int main() { scanf("%d",&t); while(t--) { memset(dp,0,sizeof(dp)); scanf("%d%d",&n,&q); cin>>a; dp[1][a[0]-'a']++; for(int i=2;i<=n;i++) { for(int j=0;j<26;j++) { dp[i][j]=dp[i-1][j]; } dp[i][a[i-1]-'a']++; } while(q--) { sum=0; scanf("%d %d %c",&l,&r,&b); int ans1,ans2; ans1=dp[n][b-'a']*((l-1)/n)+dp[(l-1)%n][b-'a']; ans2=dp[n][b-'a']*(r/n)+dp[r%n][b-'a']; sum=ans2-ans1; printf("%d\n",sum); } } return 0; }
E
给出n行数,每行6个数,从每行选择一个数,使得所有数乘积为x,问方案数。
思路:
一看这道题,就感觉是dfs就完了,但是数据量n是14,而且是多组输入,所以需要优化。
可以把这个n行数分为两半,先查一半,把这一半的数的乘积存入map,并且记录乘积为i的方案数是多少,记为map【i】。然后搜索,另一半,只要找出(x/j)%1e9+7==i的情况,然后加和就行了。这样算出来的总和,就是总的方案数了,map中没存的数为0.有除法取模。用逆元来求就可以了。
代码:
暂无
F
题目大概:
给出n个数求一种数的个数。这种数前面的数都小于等于它,后面的数都大于等于它。求这种数的个数。
思路:前缀最大一下。后缀最小一下。
然后扫一遍数组,找出符合条件的数的个数。
代码:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int maxn=1100000; int a[maxn]; int qima[maxn]; int houmi[maxn]; int main() { int t; scanf("%d",&t); while(t--) { int n; memset(a,0,sizeof(a)); memset(qima,0,sizeof(qima)); memset(houmi,0,sizeof(houmi)); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); qima[i]=max(a[i],qima[i-1]); } houmi[n+1]=11111111; for(int i=n;i>=1;i--) { houmi[i]=min(houmi[i+1],a[i]); } int ans=0; for(int i=2;i<n;i++) { if(a[i]>=qima[i]&&a[i]<=houmi[i]) { ans++; } } printf("%d\n",ans); } return 0; }
G
题目大概:
一个矩形四周缺了多少1,如果内部有这些1那就补上,没有就返回-1.
思路:
模拟一遍。
代码:
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; const int maxn=55; char a[maxn][maxn]; int main() { int t; scanf("%d",&t); while(t--) { int n,m; int c1=0,c2=0; scanf("%d%d",&n,&m); for(int i=0;i<n;i++) scanf("%s",a[i]); for(int i=0;i<n;i++) for(int j=0;j<m;j++) { if(i==0) { if(a[i][j]=='0') c1++; } else if(i==n-1) { if(a[i][j]=='0') c1++; } else if(j==0) { if(a[i][j]=='0') c1++; } else if(j==m-1) { if(a[i][j]=='0') c1++; } if(i>0&&i<n-1&&j>0&&j<m-1) { if(a[i][j]=='1') c2++; } } if(c1>c2) printf("-1\n"); else printf("%d\n",c1); } return 0; }
H
题目大概:
给出n个数,要求从第1个到第n个,每个位置,可以向右走1个数,也可以向右走到最近的和本位置的数相同的数上。问最少走几步。
思路:
每个位置要么向右走1步,要么向右走很多步,dp。
或者bfs。
代码:
#include <bits/stdc++.h> #define LL long long using namespace std; const int maxn = 5e5 + 10; int num[maxn], pos[maxn]; int dp[maxn]; int main(){ int t; scanf("%d", &t); while(t--) { int n; scanf("%d", &n); for(int i = 1; i <= n; ++i) { scanf("%d", num + i); } memset(pos,-1,sizeof(pos)); dp[1] = 0; pos[num[1]] = 1; for(int i = 2; i <= n;i++) { dp[i]=dp[i-1]+1; if(pos[num[i]]!=-1) { dp[i]=min(dp[i],dp[pos[num[i]]]+1); } pos[num[i]]=i; } printf("%d\n", dp[n]); } return 0; }I
题目大概:
给出n个数,求所有子区间的每个数的乘积之和。
思路:
与A题,从题意上很类似。
从数据量上看,n2的的dp会炸。根据组合数学的(a1+1)*(a2+1)=a1a2+a1+a2+1
这个dp的规律与它很相似dp【i】=dp【i-1】*(a【i】+1)+a【i】;不过没有+1.
代码:
#include <bits/stdc++.h> #define ll long long using namespace std; const ll mod=1e9+7; const int maxn=110000; ll a[maxn]; ll dp[maxn]; int main() { int T; scanf("%d",&T); while(T--) { ll n; scanf("%lld",&n); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); dp[1]=a[1]%mod; for(int i=2;i<=n;i++) { dp[i]=(dp[i-1]+((dp[i-1]*a[i])%mod+a[i])%mod)%mod; } cout<<dp[n]<<endl; } }J
题目大概:
给出n个字母(有重复),求由这些字母组成的串中的回文串数量。
思路:
组合方式太多,肯定要推一下规律。
首先n奇数的情况下,字符串中,26个英文字母只能由1个由奇数个才有解。
n偶数的情况,字符串中,26个英文字母,不能有奇数个的。
如果有解。
那么26个英文字母一定有偶数个,或者变为偶数个了。
因为数回文串。那就把它一分为2,只需要求一半数量的26的英文字母的全排列就行,直接套有重复元素的全排列公式。
代码:
#include <bits/stdc++.h> #define ll long long using namespace std; using namespace std; ll n; char s[30]; int vis[30]; ll f[30]; int main() { f[0]=f[1]=1; for(ll i=2;i<=15;i++) { f[i]=f[i-1]*i; //cout<<f[i]<<endl; } int T; scanf("%d",&T); while(T--) { scanf("%lld",&n); scanf("%s",s); memset(vis,0,sizeof(vis)); for(int i=0;i<n;i++) { vis[s[i]-'a'+1]++; } ll e=0,sum=0; for(int i=1;i<=26;i++) { if(vis[i]%2==1) {e++;} sum+=(vis[i]/2); } if(e>1) {cout<<0<<endl;continue;} ll ans=f[sum]; for(int i=1;i<=26;i++) { if(vis[i]!=0) { if(vis[i]>=2) ans=ans/f[vis[i]/2]; } } cout<<ans<<endl; } }