比赛链接:https://vjudge.net/contest/357216
说明: 比赛难度正好符合寒假训练的同学。(有一两道可能一些同学做过,我们出题没考虑到sorry)
下面对题目进行解答一下。(题目方法不唯一,给出的解法仅供参考)
A题
硬核签到不多说了。
B:
题意:在一个n*m的图中找到
1.长度不是零。
2.线段的两个端点是网格点。
3.线段的中点是网格点。
如此的线段数量。
思路
可以先考虑一段的时候可以发现以一个点为中点做延伸。实质上是n-2+n-4+n-6+…
横竖本质相同,可以组成一个矩形(包括正方形)。可以在横中选择一个竖中选择一个做组合(C(n,1)*C(m,1))产生两个对角线。
typedef long long ll;
int main()
{
ll n,m;
cin>>n>>m;
ll sum1=0,sum2=0;
ll temp1=n+1,temp2=m+1,temp=2;
while(temp1-temp>0)
{
sum1+=temp1-temp;
temp+=2;
}
temp=2;
while(temp2-temp>0)
{
sum2+=temp2-temp;
temp+=2;
}
cout<<(n+1)*sum2+(m+1)*sum1+sum1*sum2*2;
return 0;
}
C题
题意:题意就是输入一个字符输出他的键盘前一位。
思路: 可以预先一个数组存下所有的字符,之后按题意输出即可。
D题
题意:找到丢失的数字。
思路:直接遍历一遍找到最大值和最小值,由于题目中说明了是不一样的,相减即可。
typedef long long ll;
int main()
{
ll MIN=99999999999;
ll MAX=-99999999999;
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
ll temp;
cin>>temp;
MAX=max(temp,MAX);
MIN=min(MIN,temp);
}
cout<<MAX-MIN+1-n;
return 0;
}
E题
直接矩阵相加不多说了。
F题
题意:问01组成的数字是否满足条件。
*思路: dfs尝试每一位放0或者1,符合条件退出,注意用long long 和long long 的范围
int flag,n;
void dfs(long long int a,int x)
{
if(x>19||flag==1)
return;
if(a%n==0)
{
flag=1;
cout<<a<<endl;
return;
}
dfs(a*10,x+1);
dfs(a*10+1,x+1);
}
int main()
{
while(scanf("%d",&n),n!=0)
{
flag=0;
dfs(1,1);
}
}
G题
题意:变换每一位上的数字,变完的数字也要是素数。
思路:直接队每一位上的数字进行搜索即可。
set<int> s;
typedef pair<int,int> P;
bool isPrime(int x)
{
if(x==2||x==3)
return true;
if(x%6!=1&&x%6!=5)
return false;
int temp=sqrt(x);
for(int i=5;i<=temp;i+=6)
if(x%i==0||x%(i+2)==0)
return false;
return true;
}
int main()
{
for(int i=1000;i<=9999;i++)
if(isPrime(i))
s.insert(i);
int T;
scanf("%d",&T);
while(T--)
{
int n,m;
cin>>n>>m;
queue<pair<int,int> > q;
set<int> book;
q.push(P(n,0));
book.insert(n);
while(!q.empty())
{
P temp=q.front();
int l=temp.first;
int r=temp.second;
q.pop();
if(l==m)
{
cout<<r<<endl;
break;
}
//printf("%d\n",l);
for(int i=0;i<=9;i++)
{
int ge,sh,ba,qi;
ge=l%10,sh=l/10%10,ba=l/100%10,qi=l/1000;
if(s.find(qi*1000+ba*100+sh*10+i)!=s.end()&&book.find(qi*1000+ba*100+sh*10+i)==book.end())
q.push(P(qi*1000+ba*100+sh*10+i,r+1)),book.insert(qi*1000+ba*100+sh*10+i);
if(s.find(qi*1000+ba*100+i*10+ge)!=s.end()&&book.find(qi*1000+ba*100+i*10+ge)==book.end())
q.push(P(qi*1000+ba*100+i*10+ge,r+1)),book.insert(qi*1000+ba*100+i*10+ge);
if(s.find(qi*1000+i*100+sh*10+ge)!=s.end()&&book.find(qi*1000+i*100+sh*10+ge)==book.end())
q.push(P(qi*1000+i*100+sh*10+ge,r+1)),book.insert(qi*1000+i*100+sh*10+ge);
if(s.find(i*1000+ba*100+sh*10+ge)!=s.end()&&book.find(i*1000+ba*100+sh*10+ge)==book.end())
q.push(P(i*1000+ba*100+sh*10+ge,r+1)),book.insert(i*1000+ba*100+sh*10+ge);
}
}
}
}
H题
题意:给定一个素数,找出有多少种连续素数和等于他。
思路:由于题目中要求的是连续的素数和,直接尺取进行判断即可,预处理出10000内的素数(筛法有多种,本质就是利用素因数做倍数筛即可)。
typedef long long ll;
bool vis[10005];
int prime[10005];
int tot=0;
void init()
{
memset(vis,false,sizeof(vis));
ll i,j;
for(i=2;i<=10005;i++)
{
if(!vis[i])
{
prime[tot++]=i;
}
for(int j=0;j<tot;j++)
{
if(i*prime[j]>10005)
break;
vis[i*prime[j]]=1;
if(i%prime[j]==0)
break;
}
}
}
int main()
{
int n;
init();
while(scanf("%d",&n)&&n)
{
int sum=0;
int h=0,t=0;
int cnt=0;
while(1)
{
while(prime[t]<=n&&sum<n)
{
sum+=prime[t++];
}
if(sum==n)
cnt++;
sum-=prime[h++];
if(sum<=0)
break;
}
printf("%d\n",cnt);
}
return 0;
}
I题
题意:求最长的公共子序列的个数。
思路: 动态规划求解。dp[i][j]表示的是前i个s1字符串 前j个s2字符串 的最长公共子序列。由最优子结构推导。
char s1[1010],s2[1010];
int dp[1010][1010];
int main()
{
while(scanf("%s",s1+1)!=EOF)
{
int i,j,k;
scanf("%s",s2+1);
memset(dp,0,sizeof(dp));
int len1=strlen(s1+1);
int len2=strlen(s2+1);
for(i=1;i<=len1;i++)
{
for(j=1;j<=len2;j++)
{
if(s1[i]==s2[j])
{
dp[i][j]=max(dp[i-1][j-1]+1,dp[i][j]);
}
dp[i][j]=max(dp[i][j],max(dp[i][j-1],dp[i-1][j]));
}
}
/* for(i=1;i<=len1;i++)
{
for(j=1;j<=len2;j++)
{
printf("%d ",dp[i][j]);
}
printf("\n");
}*/
printf("%d\n",dp[len1][len2]);
}
return 0;
}
J题
直接按照题意给定的具体步骤进行简单模拟即可。
K题
题意:询问最少数量的大炮能够覆盖到小岛,大炮的范围以d为半径的圆。
思路:可以转化一下,把小岛看做中心,d看做半径画圆,能够覆盖的话将与x轴产生交点,也就是大炮在该区间必须要有点,这样就转化到x轴上,之后进行贪心。
struct A {
double left,rigth;
}a[1010];
bool cmp(A t1,A t2)
{
return t1.left<t2.left;
}
int main()
{
int n,d,i,j,k;
int yy=1;
while(scanf("%d %d",&n,&d)&&(n||d))
{
bool flag=true;
for(i=0;i<n;i++)
{
int x,y;
cin>>x>>y;
if(d<y)
{
flag=false;
continue;
}
a[i].left=1.0*x-sqrt(d*d*1.0-y*y*1.0);
a[i].rigth=x*1.0+sqrt(d*d*1.0-y*y*1.0);
}
int cnt=-1;
if(flag)
{
sort(a,a+n,cmp);//从小到大排序
cnt=1;
double temp=a[0].rigth;
for(i=1;i<n;i++)
{
if(a[i].left>temp)
{
temp=a[i].rigth;
cnt++;
}
else if(a[i].rigth<temp)
{
temp=a[i].rigth;
}
}
printf("Case %d: %d\n",yy++,cnt);
continue;
}
printf("Case %d: %d\n",yy++,cnt);
}
return 0;
}
L题
这道题目有点超出同学们寒假学习的内容。
考虑到题目的范围,需要用到线段树去进行更新。
利用二分去枚举了区间位置。
#define lson (q<<1)
#define rson (q<<1|1)
#define mid ((l+r)>>1)
const int maxn = 100000+10;
int segt[maxn<<2];
int book[maxn];
int len[maxn<<2];
int lazy[maxn<<2];
int T,m,n,A,F,B;
int init(){
memset(segt,0,sizeof(segt));
memset(book,0,sizeof(book));
memset(len,0,sizeof(len));
memset(lazy,0,sizeof(lazy));
}
void push_up(int q){
segt[q] = segt[lson] + segt[rson];
}
void build(int q,int l,int r){
len[q] = r-l+1;
lazy[q] = -1;
if(l == r){
segt[q] = 1;
return;
}
int m = mid;
build(lson,l,m);
build(rson,m+1,r);
push_up(q);
}
void push_down(int q){
if(lazy[q] != -1){
lazy[lson] = lazy[rson] = lazy[q];
segt[lson] = len[lson]*lazy[q];
segt[rson] = len[rson]*lazy[q];
lazy[q] = -1;
}
}
void update(int q,int l,int r,int lhs,int rhs,int k){
if(l>=lhs && r<=rhs){
lazy[q] = k;
segt[q] = len[q]*k;
return;
}
push_down(q);
int m = mid;
if(lhs <= m) update(lson,l,m,lhs,rhs,k);
if(rhs > m) update(rson,m+1,r,lhs,rhs,k);
push_up(q);
}
int query(int q,int l,int r,int lhs,int rhs){
if(l>=lhs && r<=rhs){
return segt[q];
}
int m = mid;
int ans = 0;
push_down(q);
if(lhs <= m) ans+=query(lson,l,m,lhs,rhs);
if(rhs > m) ans+=query(rson,m+1,r,lhs,rhs);
return ans;
}
int find_lift(int l){
int r = m;
while(l < r){
int MID = (l+r)>>1;
if(query(1,1,m,l,MID) >= 1) r = MID;
else l = MID+1;
}
return r;
}
int find_right(int ll,int B){
int l = ll, r = m;
while(l < r){
int MID = (l+r)>>1;
if(query(1,1,m,ll,MID) >= B) r = MID; //二分这里面的其实参数不变是精髓 这很重要
else l = MID + 1;
}
return r;
}
int main(){
std::ios::sync_with_stdio(false);
cin >> T;
while(T--){
init();
cin >> m >> n;
build(1,1,m);
for(int i=0;i<n;++i){
cin >> F >> A >> B;
if(F == 1){
++A;
int sum = query(1,1,m,A,m);
if(sum == 0){
cout << "Can not put any one.\n";
continue;
}
int lift = find_lift(A);
int right = find_right(lift,min(sum,B));
cout << lift-1 << " " << right-1 << endl;
update(1,1,m,lift,right,0);
}else{
++A,++B;
cout << (B-A+1) - query(1,1,m,A,B) << endl;
update(1,1,m,A,B,1);
}
}
cout << endl;
}
return 0;
}