P7960 [NOIP2021] 报数【民间数据】
该题题目大意为:给出一个数,判断该数是否为满足:
1、不被7整除;
2、十进制位数中不含有7;
3、不被十进制位数中含有7的数整除。
若满足找出下一个满足该条件的数,若不满足则输出-1。
思路:开始时想到直接找出给出数的所有质因数,然后一个个判断是否含有7,结果无论怎么弄都是超时,后来一看数据时1e7想到了打表,不过还是超时,后来发现还得是打表+二分,而且第一次打表方法是1-1e7遍历一个个判断,而第二次是找出含有7的数然后找出其所有的在数据范围的倍数,这样快了很多。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
inline int read()
{
int x=0; bool f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
for(; isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
if(f) return x;
return 0-x;
}
int pd[100000000],cz[100000000],js;
int main()
{
for(int j=1;j<=1e7+100000;j++)//打表
{
if(pd[j])
continue;
int t=j,p=0;
while(t)
{
if(t%10==7)
{
p=1;
break;
}
t/=10;
}
if(!p)
{
cz[++js]=j;
continue;
}
for(t=1;t*j<=1e7+100000;t++)
pd[t*j]=1;
}
int n;
n=read();
while(n--)
{
int x;
x=read();
if(x%7==0||pd[x])
printf("-1\n");
else
{
int ks=upper_bound(cz+1,cz+1+js,x)-cz;//二分查找
for(;;ks++)
if(cz[ks]%7!=0)
{
printf("%d\n",cz[ks]);
break;
}
}
}
}
P1121 环状最大两段子段和
题目:给出一段长度为 n 的环状序列 a,即认为 a1和 an 是相邻的,选出其中连续不重叠且非空的两段使得这两段和最大。
思路:若数据中只有一个整数,或者全是负数,则需要特殊判断。如果有两个及以上的正数,则写一个从1到n再到1再到n的循环,找出该环的最大子段a,再在剩余数据中找出一个最大子段b(当然这段数据不能看成环),再从第一个最大子段a中找出一个最小子段c,最后判断a+b大,还是a-c大,然后输出。
a和b的位置有图中两种情况,c还有多种情况此处不全部列举。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
long long c[200001],dp[200001],pre[200001];
int main()
{
//freopen("in.txt","r",stdin);
long long n;
scanf("%lld",&n);
long long fs=0;
for(long long j=1;j<=n;j++)
{
scanf("%lld",&c[j]);
if(c[j]<0)
fs++;
}
if(fs>=n-1)//若数据中只有一个整数,或者全是负数,则需要特殊判断。
{
sort(c+1,c+1+n);
printf("%lld\n",c[n]+c[n-1]);
return 0;
}
long long fin=2*n,j=1,i=1;
while(i<=fin)//在环上做最大子段和。
{
long long t=j-1;
if(j==n+1)
{
j=1;
t=n;
}
if(dp[t]<=0)
{
dp[j]=c[j];
pre[j]=j;
}
else if(pre[t]!=j)
{
dp[j]=dp[t]+c[j];
pre[j]=pre[t];
}
j++;
i++;
}
long long ks,js,maxi=0,t2=0,maxi2=0;
for(long long j=1;j<=n;j++)//找出该环的最大子段a
if(dp[j]>maxi)
{
maxi=dp[j];
ks=pre[j];
js=j;
}
for(long long j=js+1;;j++)//再在剩余数据中找出一个最大子段b
{
if(j==n+1)
j=1;
if(j==ks)
break;
t2=max(c[j],t2+c[j]);
maxi2=max(t2,maxi2);
}
t2=0;
long long mini=0;
for(long long j=ks;;j++)//再从第一个最大子段a中找出一个最小子段c
{
if(j==n+1)
j=1;
t2=min(c[j],t2+c[j]);
mini=min(t2,mini);
if(j==js)
break;
}
printf("%lld\n",max(maxi+maxi2,maxi-mini));//最后判断a+b大,还是a-c大,然后输出。
}
P1124 文件压缩
该题题型为模拟题,由题意可知给出的字符串我们已经知道每一个字符在原字符串中的前一个字符是哪一个,但是拼凑原字符串的时候不能从头到尾拼,而应该从后向前,这样才能保证顺序不乱。
#include<iostream>
#include<algorithm>
#include<map>
#include<cstdio>
#include<vector>
using namespace std;
int n,p;
string x,t;
int pd;
map<int,int>check;
vector<int>cz[50];
void dfs(string ans,int p,int js)
{
if(pd==1||js>ans.size())
return;
if(ans.size()==n)
{
pd=1;
for(int j=ans.size()-1;j>=0;j--)
cout<<ans[j];
cout<<endl;
return;
}
else if(ans.size()>n)
return;
int zh=x[p]-'a';
for(int j=0; j<cz[zh].size(); j++)
{
p=cz[zh][j];
if(check[p]==0)
{
check[p]=1;
dfs(ans+x[p],p,js+1);
check[p]=0;
}
if(pd)
return;
}
}
int main()
{
ios::sync_with_stdio(false);
//freopen("ii.txt","r",stdin);
cin>>n>>x>>p;
if(n==1)
{
cout<<x<<endl;
return 0;
}
t=x;
sort(t.begin(),t.end());
string ans="";
p-=1;
for(int j=0;j<t.size();j++)
if(t[j]==x[p])
{
p=j;
break;
}
ans+=x[p];
check[p]=1;
for(int j=t.size()-1; j>=0; j--)
if(j!=p)
cz[int(t[j]-'a')].push_back(j);
dfs(ans,p,1);
}
/*
5
daace
1
*/
本周在codeforce上打了几场比赛,结果发现比之前相比出题数没有变化,看来自从上一次省赛之后没有什么太大进步。