T1 数鸭子
题目描述
题目分析
题目问给定的N个数,假设每个数a位,问这a位中不重复的小于K,这样的数有多少个。
解决方法是取模+右移,直到移没为止。
注意最后几个数据点的k小于1e6是唬人的!k不超过10。
长长记性
- 若使用STL的set去重,复杂度是alogaN,而使用数组做桶只有a*N,很容易卡长而掉分(我就这样白白损失了40分)
- 对于此题,STL关同步904ms,数组关同步249ms,不关同步都会T,所以大量数据记得关同步~
代码
#define _ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include <bits/stdc++.h>
using namespace std;
int main()
{
_;//STL关同步904ms,数组关同步249ms,不关同步都会T
int n,k;
cin>>n>>k;
long long ai;
// set<int> tong; 会TLE,不要卡常,吸取教训,不要超过1e7
int tong[10];
int sum=0;
for(int i=1;i<=n;i++)
{
// tong.clear();
memset(tong,0,sizeof tong);
cin>>ai;
while (ai!=0)
{
// tong.insert(ai%10);
tong[ai%10]++;
ai/=10;
}
int temp=0;
for(int i=0;i<=9;i++)
{
if(tong[i]!=0)temp++;
}
if(temp<k)sum++;
}
cout<<sum;
return 0;
}
T2 ZJM要抵御宇宙射线
题目描述
题目分析
这道题是说给定平面上一堆整数点,从里面选一个作为圆心,使得所有点都被包住,使得半径平方最小。观察数据最大的才1k,完全可以n^2暴力去做(T2不要难为自己),外循环找圆心,维护内循环输出的最小的;内循环找该圆心距离所有点最远值,维护一个最大量。即最大值最小问题。
长长记性
慎用pow,他返回值是double容易掉精度,比如int(pow(3,2)+pow(4,2))
可能为4.9999999=4。
代码
#define _ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const long long inf=0x3f3f3f3f3f3f3f3f;
struct p
{
ll x,y;
}pointt[1009];
int main()
{
int n;
cin>>n;
ll dis;
ll X,Y;
ll temp;
for(int i=1;i<=n;i++)
cin>>pointt[i].x>>pointt[i].y;
//慎用pow,他返回值是double容易掉精度
dis = inf;
X=pointt[1].x;
Y=pointt[1].y;
for(int i=1;i<=n;i++)
{
ll thiss = (pointt[i].x-pointt[1].x)*(pointt[i].x-pointt[1].x)+(pointt[i].y-pointt[1].y)*(pointt[i].y-pointt[1].y);
ll thissX=pointt[i].x;
ll thissY=pointt[i].y;
for(int j=1;j<=n;j++)
{
if(i==j)continue;
temp=(pointt[i].x-pointt[j].x)*(pointt[i].x-pointt[j].x)+(pointt[i].y-pointt[j].y)*(pointt[i].y-pointt[j].y);
if(temp > thiss)
{
thiss=temp;
thissX=pointt[i].x;
thissY=pointt[i].y;
continue;
}
if(temp == thiss)
{
if(pointt[i].x<thissX)
{
thissX=pointt[i].x;
thissY=pointt[i].y;
continue;
}
if(pointt[i].x==thissX&&pointt[i].y<thissY)
{
thissX=pointt[i].x;
thissY=pointt[i].y;
continue;
}
}
}
if(thiss < dis)
{
dis=thiss;
X=thissX;
Y=thissY;
continue;
}
if(dis == thiss)
{
if(thissX<X)
{
X=thissX;
Y=thissY;
continue;
}
if(X==thissX&&thissY<Y)
{
X=thissX;
Y=thissY;
continue;
}
}
}
printf("%.2lf",(double)X);
printf(" ");
printf("%.2lf",(double)Y);
printf("\n");
printf("%.2lf",(double)dis);
return 0;
}
T4 宇宙狗的危机(区间dp)
题目描述
题目分析
题目问给定的一堆升序数是否可以构成一棵二叉搜素数,使得父子之间是gcd关系。
这是一道区间dp的问题,藏得很深(也可能是我太菜)。二叉搜素树的性质是根左侧比根小,根右侧比根大。令f[i][j]为1表示区间i~j可以构成一棵二叉搜索树,为0表示不能形成二叉搜索树,f[i][k]和f[k][j]能构成二叉搜索树,则f[i][j]能构成二叉搜索树。第一层循环枚举区间长度,然后枚举区间的起始位置,根据区间长度得到区间的结束位置,最后枚举区间中的根节点。
p.s.本题需要记忆化剪枝,首先算出所有数之间是否存在gcd关系,存在cangcd的二维数组中。
代码
#define _ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#include <bits/stdc++.h>
using namespace std;
int gcd(int a,int b){return b == 0 ? a : gcd(b,a%b);}
int node[709];
bool f[709][709];//f[i][j]为1表示区间i~j可以构成一棵二叉搜索树,为0表示不能形成二叉搜索树,信息存在cangcd中
// f[i][k]和f[k][j]能构成二叉搜索树,则f[i][j]能构成二叉搜索树
bool cangcd[709][709];//记忆化,防止重复递归计算
bool l[709][709],r[709][709];//令l[i][j]表示以j为根,j的左子树可到i这样的BST是否存在,r[i][j]表示以i为根,i的右子树可到j这样的BST是否存在
int main()
{
int T;
cin>>T;
while(T--)
{
memset(f,0,sizeof f);
memset(cangcd,0,sizeof cangcd);
memset(l,0,sizeof l);
memset(r,0,sizeof r);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>node[i];
l[i][i]=r[i][i]=1;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j) continue;
else if(gcd(node[i],node[j])>1) cangcd[i][j]=true;
for(int len=0;len<n;len++)
for(int i=1;i+len<=n;i++)
{
int j=i+len;
for(int k=i;k<=j;k++)
if(l[i][k]&&r[k][j])
{
f[i][j]=1;
if(cangcd[i-1][k]) r[i-1][j]=1;//i-1为根最远可达j
if(cangcd[k][j+1]) l[i][j+1]=1;//j+1为根最远可达i
}
}
if(f[1][n]) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
return 0;
}