题目:
2017 JUST Programming Contest 4.0
Jordan University of Science and Technology, September, 16, 2017A:给你一个序列长度为n(n<=1e5),定义Beauty(l, r) = al & al + 1 & al + 2 & ... & ar,求
其实自己在纸上推一下不难发现,我们只需要维护二进制中每一位连续为1的数量,然后每一次都加num[i]*2的i次方即可。
代码:
#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 10010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
ll c[maxn],a[maxn];
int n,m,k;
ll num[maxn];
ll ans,tmp,cnt;
int main() {
c[0]=1;
for(int i=1;i<=28;i++)
c[i]=c[i-1]*2;
int T,cas;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
memset(num,0,sizeof(num));
ans=0;
for(int i=0;i<n;i++)
{
ll x;
scanf("%lld",&x);
for(ll j=0;j<=28;j++)
{
if(x&(1<<j)) num[j]++;
else num[j]=0;
ans+=num[j]*c[j];
}
}
printf("%lld\n",ans);
}
return 0;
}
B:给你一个序列,其中-1处为数字丢失处。而每个数满足a[i] = ( a[i-1]+ 1) % m,(1 < i ≤ n)(n<=1000,m<=1e9)
因此只需要找一个不为-1的数字分别往前、往后填上就行了。
代码:
#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 100010
#define ll long long
#define inf 1e9+7
using namespace std;
int n,m,k;
ll a[maxn],c[maxn];
ll ans,tmp,cnt;
ll num[maxn];
int main() {
int T,cas;
c[0]=1;
for(int i=1;i<=30;i++)
c[i]=c[i-1]*2;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
ll ans;
int t;
memset(num,0,sizeof(num));
for(int i=0;i<n;i++)
{
ll x;
scanf("%lld",&a[i]);
if(a[i]!=-1){ans=a[i];t=i;}
}
for(int i=t-1;i>=0;i--)
{
if(a[i]==-1) {a[i]=a[i+1]-1;if(a[i]<0) a[i]=(ll)(m-1);}
}
for(int i=t+1;i<n;i++)
{
if(a[i]==-1) a[i]=(a[i-1]+1)%(ll)m;
}
for(int i=0;i<n;i++)
printf("%lld%c",a[i],i==n-1?'\n':' ');
}
return 0;
}
C:给你一个序列a,你要对每个a[i]找到一个a[j]使得(a[i]+a[j])%1000000007最大。(2<=n<=1e5)
不难想到,从小到大排一遍序以后,j=n-1,从最小的那个数开始(i=0),必定满足若a[i]+a[j]<mo,则a[j]就是a[i]找的那个数。
若>=mo,则j--;且满足使(a[i-1]+a[k])%mo最大的k一定小于等于j。
有几种特殊情况:
1、j=-1时,若i!=n-1,则一定(a[i]+a[n-1])%mo最大
2、j=-1时,若i==n-1,则一定则一定(a[i]+a[n-2])%mo最大
3、i==j时,j--;
题目不难,细节不少。
代码:
#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 100010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
ll aa[maxn],c[maxn];
ll tmp,cnt;
ll ans[maxn];
int n;
struct node
{
ll v;
int id;
bool operator<(node aa)const
{
return v<aa.v||v==aa.v&&id<aa.id;
}
}a[maxn];
int main() {
int T,cas;
c[0]=1;
for(int i=1;i<=30;i++)
c[i]=c[i-1]*2;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int t;
for(int i=0;i<n;i++)
{
scanf("%lld",&a[i].v);
a[i].id=i;
}
sort(a,a+n);
int j=n-1;
for(int i=0;i<n;i++)
{
ll tmp=a[i].v;
while(j>=0&&(tmp+a[j].v>=mo||(i==j))) j--;
if(j<0)
{
if(i!=n-1) ans[a[i].id]=(tmp+a[n-1].v)%mo;
else ans[a[i].id]=(tmp+a[n-2].v)%mo;
}
else ans[a[i].id]=(tmp+a[j].v);
}
for(int i=0;i<n;i++)
printf("%lld%c",ans[i],i==n-1?'\n':' ');
}
return 0;
}
D:给你一个只包含小写字母的无限长的串,循环节为s,长度为n(n<=1e4),求字母ch在下标为[l,r]长度的区间里出现了多少次。(l<=r<=1e9)
首先预处理一下前缀和,就是字母ch在0~i区间里出现了num[i][ch-'a']次。
然后对于区间[l,r](0<l<=r<n)中ch出现的次数即为num[r][ch-'a']-num[l-1][ch-'a'];
即对于l/len==r/len时,ans=num[r][ch-'a']-num[l-1][ch-'a'];
否则 ans=(r/len-l/len-1)*num[n-1][ch-'a']+num[r][ch-'a']+num[n-1][ch-'a']-num[l-1][ch-'a'];
代码:
#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 10010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
int aa[maxn],c[maxn];
int tmp,cnt;
int ans[maxn];
int num[maxn][27];
char s[maxn];
int n,m,q;
int find(int l,int r,int ch)
{
if(l==0) return num[r][ch];
else return num[r][ch]-num[l-1][ch];
}
int main() {
int T,cas;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&q);
memset(num[0],0,sizeof(num[0]));
int t;
scanf("%s",s);
for(int i=0;i<n;i++)
{
int id=s[i]-'a';
if(i)
for(int j=0;j<26;j++)
num[i][j]=num[i-1][j];
num[i][id]++;
}
for(int i=0;i<q;i++)
{
int x,y,id;
char ch[2];
scanf("%d %d %s",&x,&y,ch);
id=ch[0]-'a';
x--;y--;
if((y/n)==(x/n))
{
x%=n;y%=n;
int ans=find(x,y,id);
printf("%d\n",ans);
}
else
{
int ans=(y/n)-(x/n)-1;
ans*=num[n-1][id];
x%=n;y%=n;
ans+=find(x,n-1,id);
ans+=find(0,y,id);
printf("%d\n",ans);
}
}
}
return 0;
}
E:给你n(n<=14)个骰子,每个骰子六个面都标有数字(1~100)你要从每个骰子选一个面,把面上的数字乘起来%mod(1e9+7)为m。求有多少种不同的选法。
上来就来了一发爆搜TLE,经提示后才知道神奇的折半法。。。
即先爆搜前一半骰子的乘积%mod后用map存起来其方法总数
再对后半段爆搜,对每个结果t,m*t的逆元%mod即为乘积等于m需要的数,直接map找其方法总数即可。
具体见代码:
#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 10010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
ll d[maxn],c[maxn];
ll tmp,cnt;
ll ans;
ll a[20][8];
int n,f1,f2,n1;
ll m,q;
map<ll,ll>mp;
ll power(ll a,ll n) //a的n次方mod
{
ll ans=1;
a=a%mo;
while (n)
{
if(n&1) ans=(ans*a)%mo;
n>>=1;
a=(a*a)%mo;
}
return ans;
}
void dfs(int i,ll tmp)
{
if(i==n1)
{
if(mp.count(tmp)==0)
{mp[tmp]=1;}
else
{
mp[tmp]++;
}
return;
}
for(int j=0;j<6;j++)
{
dfs(i+1,(tmp*a[i][j])%mo);
}
}
void dfs2(int i,ll tmp)
{
if(i==n)
{
ans+=mp[(m*(power(tmp,mo-2)))%mo];
return;
}
for(int j=0;j<6;j++)
{
dfs2(i+1,(tmp*a[i][j])%mo);
}
}
int main() {
int T,cas;
scanf("%d",&T);
while(T--)
{
ans=0;
scanf("%d %lld",&n,&m);
mp.clear();
for(int i=0;i<n;i++)
{
for(int j=0;j<6;j++)
{
scanf("%lld",&a[i][j]);
}
sort(a[i],a[i]+6);
}
n1=(n+1)/2;
dfs(0,1);
dfs2(n1,1);
printf("%lld\n",ans);
}
return 0;
}
F:题意是求x的总数,其中
1、1 < x < n (n<1e6)
2、ay ≤ ax, for each y (1 ≤ y < x).
3、ax ≤ az, for each z (x < z ≤ n).
只需要处理一下前缀最大值和后缀最小值,扫一遍就出来了。
代码:
#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 1000010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
int ma[maxn],mi[maxn];
int tmp,cnt;
int ans;
int a[maxn];
int n;
int m,q;
int main() {
int T,cas;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
ma[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
ma[i]=max(ma[i-1],a[i]);
}
mi[n+1]=inf;
for(int i=n;i>=1;i--)
{
mi[i]=min(mi[i+1],a[i]);
}
int ans=0;
for(int i=2;i<n;i++)
if(a[i]>=ma[i-1]&&a[i]<=mi[i+1]) ans++;
printf("%d\n",ans);
}
return 0;
}
G:给你一个n*m(n,m<50)的01棋盘,你要使其第一行、第一列、最后一行、最后一列的格子全为1,每次可以把任意两个格子交换数值,求最少需要交换多少次。如果不能使其第一行、第一列、最后一行、最后一列的格子全为1,则输出-1。
只需要记录边界上0的数量和非边界1的数量,比较一下即可。。。
代码:
#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 1010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
int tmp,cnt;
int ans;
int a[maxn];
int n;
int m,q;
char s[maxn][maxn];
int main() {
int T,cas;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&m);
// getchar();
for(int i=0;i<n;i++) scanf("%s",s[i]);
int ans=0,tmp=0;
for(int i=0;i<n;i++)
for(int j=0;j<m;j++)
{
int id=(s[i][j]&15);
if(i==0||j==0||(i==n-1)||(j==m-1))
{
if(!id) ans++;
}
else if(id) tmp++;
}
//cout<<tmp<<ans<<endl;
if(tmp<ans) puts("-1");
else printf("%d\n",ans);
}
return 0;
}
H:给你n(n<2e5)个数a[1]~a[n],你刚开始在1的位置,你要跳到位置n。假设你现在在位置i
1、你可以跳到a[i]右边和a[i]值相等的最近的一个位置
2、你可以直接向右跳一个格子
求最少用多少部跳到位置n。
这不就是牛客网上某场大佬赛签到题的变种么。。。
记忆化bfs,记忆每个已经跳过的点。至于每个点可以往右跳几个预处理一下就行了。。。
#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 200010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
int tmp,cnt;
int ans;
int a[maxn],c[maxn];
int tiao[maxn];
int n;
int m,q;
struct node
{
int x;
int tmp;
}no;
void bfs()
{
queue<node>q;
no.x=1;
no.tmp=0;
q.push(no);
c[1]=1;
while(!q.empty())
{
node k=q.front(); q.pop();
node kk=k;
kk.tmp++;
if(k.x==n)
{
printf("%d\n",k.tmp);
return;
}
if(!c[k.x+1]){
c[k.x+1]=1;
kk.x++;
q.push(kk);
kk.x--;
}
if(tiao[kk.x]!=-1)
{
kk.x=tiao[k.x];
if(!c[kk.x])
{
c[kk.x]=1;
q.push(kk);
}
}
}
}
int main() {
int T,cas;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
memset(c,0,sizeof(c));
memset(tiao,-1,sizeof(tiao));
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(!c[a[i]]) c[a[i]]=i;
else tiao[c[a[i]]]=i,c[a[i]]=i;
}
int ans=0;
memset(c,0,sizeof(c));
bfs();
}
return 0;
}
I:给你n(n<1e5)个数a[i]~a[n],你要求其中所有不重复子序列的乘积的和 mod 1e9+7。
又来了这道题,之前在牛客网上也见到过类似的,不过那是每个序列挑一个数的乘积总和。公式类似,直接求即可。
sum=sum+(sum+1)*a[i].(1<=i<=n) 公式很好推
代码:
#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 200010
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mo=1e9+7;
int tmp,cnt;
int ans;
ll a[maxn],c[maxn];
int n;
int m,q;
int main() {
int T,cas;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
ll ans=0;
ll sum=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
ans=(ans+((sum+1)*a[i])%mo)%mo;
sum=ans;
}
printf("%lld\n",ans);
}
return 0;
}
J:给你一个串,长度为n(n<=20)求把其中的字母重新排列组合能形成的不重复的回文串的数量。
记录每个字母出现的次数,然后
1、n为偶数,有字母出现奇数次 答案为0
2、n为奇数,有多余一个字母出现奇数次 答案为0
3、否则 求n/2,即每个字母挑选a[i]个位置 即组合数C(n/2,a[i]) 注意求完后n/2要减去a[i]
代码:
#pragma comment(linker,"/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define maxn 210
#define ll long long
#define inf 1e9+7
using namespace std;
const ll mod=1e9+7;
int tmp,cnt;
int ans;
int c[maxn];
int n;
int m,q;
char s[maxn];
int k,flag,x,f,y,p;
long long ni[maxn];
long long a[maxn];
long long zuhe(int x,int y){ //组合数
return a[x]*ni[y]%mod*ni[x-y]%mod;
}
long long calc(long long x,long long y){
long long z=1;
while (y){
if (y&1)(z*=x)%=mod;
(x*=x)%=mod,y/=2;
}
return z;
}
int main() {
a[0]=1;
for (int i=1;i<maxn;i++)a[i]=a[i-1]*i%mod;
ni[maxn-1]=calc(a[maxn-1],mod-2);
for (int i=maxn-2;i>=0;i--)
ni[i]=ni[i+1]*(i+1)%mod;
int T,cas;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
memset(c,0,sizeof(c));
scanf("%s",s);
for(int i=0;i<n;i++)
{
int id=s[i]-'a';
c[id]++;
}
int tmp=0;
for(int i=0;i<26;i++)
{
if(c[i]&1) tmp++;
}
if((!(n&1))&&(tmp)) puts("0");
else if(tmp>1) puts("0");
else {
ll ans=1;
n/=2;
sort(c,c+26);
for(int i=0;i<26;i++)
{
c[i]/=2;
if(!n) break;
if(c[i]){
ans=ans*zuhe(n,c[i]);
n-=c[i];
}
}
printf("%lld\n",ans);
}
}
return 0;
}
总体难度中等偏下,可惜由于今天考试,昨天的比赛没打,今天的估计也打不了了。。。