B题
题意:第一行给出n和m,接下来输入n个数字,问能不能在这n个数字中选出一些数字,使他们的和等于m的倍数
分两种情况:
若n>m:
对这n个数字求前缀和%m,根据鸽巢原理,一定至少有一对sl%m=sr%m。因此[l+1,r]数的和%m等于0符合条件,即此时一定输出"YES"
若n<=m:
动态规划,设dp[i][j]表示从前i个数字中选某些数字能否使得他们的和%m等于j,能则dp[i][j]=1,否则dp[i][j]=0。若现在已知一个dp[i][j]=1,则它可以转移到dp[i+1][j](表示不选第i+1个数)或者dp[i+1][(j+a[i+1])%m](表示选择第i+1个数)。
复杂度O(m^2).
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[1005][1005],a[1000005];
int main()
{
ll n,m,i,j;
bool f=false;
scanf("%I64d%I64d",&n,&m);
for(i=1;i<=n;i++) scanf("%I64d",&a[i]);
if(n>m) f=true;
else
{
for(i=1;i<=n;i++) dp[i][a[i]%m]=1;
for(i=1;i<n;i++)
for(j=0;j<m;j++)
if(dp[i][j])
{
dp[i+1][(j+a[i+1])%m]=dp[i][j];
dp[i+1][j]=dp[i][j];
}
if(dp[n][0]) f=true;
}
if(f)printf("YES\n");
else printf("NO\n");
return 0;
}
C题
输入一个数字n,A心里想一个范围在1到n的数字,B给出x个询问,每个询问包 含一个数字,A回答他想的数字能否整除这个询问的数字,问最少问几个数字一定能知道A想的数字是多少,并输出这些数字
可以知道,如果只询问了素数p^k,那么没办法区分p^k和p^(k+1).答案就是在n的范围内所有的素数以及他们的幂次
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int prime[170],k,ans[10000005];
bool is_prime[1005];
void get_prime()
{
k=0;
int i,j;
memset(is_prime,true,sizeof(is_prime));
for(i=2;i<=1000;i++)
{
if(is_prime[i])
{
for(j=i+i;j<=1000;j+=i) is_prime[j]=false;
}
}
for(i=2;i<=1000;i++) if(is_prime[i]) prime[k++]=i;
}
int main()
{
int n,i,t,cnt=0,tmp;
scanf("%d",&n);
get_prime();
for(i=0;i<k;i++)
{
if(prime[i]>n) break;
tmp=prime[i];
while(tmp<=n)
{
ans[cnt++]=tmp;
tmp*=prime[i];
}
}
printf("%d\n",cnt);
for(i=0;i<cnt;i++) printf("%d ",ans[i]);
printf("\n");
return 0;
}
D题
题意:第一行给出数字n,接下来输入n个数,范围是1到n,表示从下标1到n能对应到的数字。求一棵以这1到n为节点的树,使得所有树上的边上的两个点替换成对应的点后得到的边还在这棵树上
可以知道,n个点能分成若干组,每组中的点构成一个循环。在这些循环中找到最小的,作为中心,且这个中心不能超过两个点,否则会自环不能构成树。若这个环只有一个点,即这个点对应的点就是它本身,那么满足条件的树就是把每个点都连到这个点上。若这个环有两个点,这两个点两边必须是对称的否则虽然这条边在替换之后不变也不能满足条件。然后剩下的环上偶数位的点连到第一个中心点上,奇数位的点连到第二个中心点上,(环上不能是奇数个点否则不满足题意)。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=100005;
bool vis[maxn];
int m[maxn];
struct node
{
int start,num;
}loop[maxn];
int main()
{
int n,i,j,k,minn=maxn,center,cnt=0;
bool f=true;
memset(vis,false,sizeof(vis));
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&m[i]);
k=0;
for(i=1;i<=n;i++)
{
if(vis[i])continue;
loop[k].start=i;
loop[k].num=0;
while(!vis[i])
{
vis[i]=true;
i=m[i];
loop[k].num++;
}
if(loop[k].num<minn)
{
minn=loop[k].num;
center=k;
}
if(loop[k].num&1)
{
if(loop[k].num==1)
{
cnt++;
if(cnt>1)f=false;
}
else f=false;
}
k++;
}
if(minn>2)printf("NO\n");
else
{
if(minn==1)
{
printf("YES\n");
for(i=1;i<=n;i++)
{
if(i==loop[center].start) continue;
printf("%d %d\n",loop[center].start,i);
}
}
else
{
if(!f) printf("NO\n");
else
{
printf("YES\n");
printf("%d %d\n",loop[center].start,m[loop[center].start]);
for(i=0;i<k;i++)
{
if(i==center) continue;
j=loop[i].start;
cnt=0;
while(m[j]!=loop[i].start)
{
if(cnt&1)printf("%d %d\n",j,loop[center].start);
else printf("%d %d\n",j,m[loop[center].start]);
j=m[j];
cnt++;
}
if(cnt&1)printf("%d %d\n",j,loop[center].start);
else printf("%d %d\n",j,m[loop[center].start]);
}
}
}
}
return 0;
}
E题
题意:第一行输入一个数n,接下来n行输入n个坐标x,y,求一种从1到n的排列,使得从这个排列中的第一个点走到第n个点的路径长度不超过25*10^8。(两个点间的路径长度定义为曼哈顿距离)
数据范围:1<=n<=10^6
注意数据范围啊...
将10^6*10*6的矩阵从左到右划分成1000个10^6*10^3的矩阵,第奇数个矩阵的点按照y从小往大走,偶数个矩阵的点按照y从大往小走。这样最多也不会超过25*10^8
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1000005;
struct node
{
int x,y,num;
}s[maxn];
bool cmp1(node a,node b)
{
return a.x<b.x;
}
bool cmp2(node a,node b)
{
return a.y<b.y;
}
bool cmp3(node a,node b)
{
return a.y>b.y;
}
int main()
{
int n,i,m;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d%d",&s[i].x,&s[i].y);
s[i].num=i;
}
sort(s+1,s+n+1,cmp1);
m=(n%1000==0 ? n/1000 : n/1000+1);
for(i=1;i<=m;i++)
{
if(i&1) sort(s+(i-1)*1000+1,s+min(i*1000,n)+1,cmp2);
else sort(s+(i-1)*1000+1,s+min(i*1000,n)+1,cmp3);
}
for(i=1;i<=n;i++) printf("%d ",s[i].num);
printf("\n");
return 0;
}