链接:https://ac.nowcoder.com/acm/contest/911/A
来源:牛客网
A Good 的集合
题目描述
平面上给 n(3≤n≤1000) 个点,保证不存在 3 点共线,保证这些点两两不重合,对于一个点集 S ,如果从 S 中任意选出三个不同的点,构成的三角形重心都不是整点(横坐标,纵坐标都是整数的点,称为整点),那么这个点集是 good 的,输出最大的 good 的点集大小。(注:所有点数小于等于 2 的点集都是 good 的)。
解:由重心坐标((x1+x2+x3)/3,(y1+y2+y3)/3)可得点的类型只能分为9种:(0-3,0-3)。
并且同一种点最多有两个,因此统计点的种类数并DFS求解。
#include
using namespace std;
#define LL long long
#define FI first
#define SE second
#define PII pair
#define endl '\n'
#define PB push_back
const double INF = 1e17+7;
const int N = 1e5 + 7;
int n,k;
PII a[9]={PII(0,0),PII(0,1),PII(0,2),PII(1,0),PII(1,1),PII(1,2),PII(2,0),PII(2,1),PII(2,2)};
map<PII,int>mp;
int ans;
int check(int x){
for(int i=0;i<9;i++){
if((x>>i)&1)
for(int j=i+1;j<9;j++){
if((x>>j)&1)
for(int c=j+1;c<9;c++){
if((x>>c)&1){
if((a[i].FI+a[j].FI+a[c].FI)%3==0&&(a[i].SE+a[j].SE+a[c].SE)%3==0)return 0;
}
}
}
}
return 1;
}
void dfs(int x,int y,int z){
if(z==9)return;
if(check(x|(1<<z))&&mp[a[z]]){
int cnt=min(mp[a[z]],2);
ans=max(ans,y+cnt);
dfs(x|(1<<z),y+cnt,z+1);
}
dfs(x,y,z+1);
}
int main()
{
for(int i=0;i<9;i++){
mp[a[i]]=0;
}
cin>>n;
int x,y;
for(int i=1;i<=n;i++){
scanf("%d%d",&x,&y);
mp[PII(x%3,y%3)]++;
}
dfs(0,0,0);
cout<<ans;
return 0;
}
B 狂赌之渊
链接:https://ac.nowcoder.com/acm/contest/911/B
来源:牛客网
题目描述
有 n 堆石头,第 i 堆石头有 ai 个石子,两人轮流操作,每次操作先选择一堆石头,再从这堆石头中取走一个石子,如果此次操作取完了被选择的这堆石头的最后一个石子,操作者得一分。当所有石子被取走时,游戏结束。输出先手最大得分。
思路:偶数堆先取必输,如果我当前取偶数堆,那对手再取一次该堆就能使局势保持不变,因此只能先取奇数堆,判断一下谁会先将奇数堆取完谁就输,并且要么全胜要么全败。
#include
using namespace std;
#define LL long long
#define FI first
#define SE second
#define PII pair
#define endl '\n'
#define PB push_back
const int INF = 1e9+7;
const int N = 1e5 + 7;
int n,m;
int main()
{
cin>>n;
int cnt=0;
int x;
for(int i=1;i<=n;i++){
scanf("%d",&x);
if(x%2)cnt++;
}
if(cnt%2)cout<<n;
else cout<<0;
return 0;
}
D O(n!)
链接:https://ac.nowcoder.com/acm/contest/911/D
来源:牛客网
题目描述
有 n件商品,第 i件商品价格为 a[i],购买后,其它所有未购买的商品价格乘上 p[i],现在要买下所有商品,输出最小耗费。
思路:将相邻物品按对对方造成的优惠排序,每一次交换都不会影响到其他物品的价格。
#include
using namespace std;
#define LL long long
#define FI first
#define SE second
#define PII pair
#define endl '\n'
#define PB push_back
const double INF = 1e17+7;
const int N = 1e5 + 7;
int n,m;
struct s{
double x,y,z;
}a[N];
bool cmp(s p,s q){
return p.x*(1-q.y)<(1-p.y)*q.x;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
scanf("%lf%lf",&a[i].x,&a[i].y);
}
double ans=INF;
sort(a+1,a+1+n,cmp);
double k=1;
ans=0;
for(int i=1;i<=n;i++){
ans+=a[i].x*k;
k*=a[i].y;
}
printf("%.6lf\n",ans);
return 0;
}
E.斐波那契串
链接:https://ac.nowcoder.com/acm/contest/911/E
来源:牛客网
题目描述
str1,str2为给定的字符串,定义 F[1] = str1, F[2] = str2,F[i]=F[i-1]+F[i-2],其中 “+” 表示字符串拼接,字符串由小写英文字母组成。多组查询,每组查询输入 x,y,输出F[x]的第 y 位,x,y≤1018(下标从 1 开始)。
思路:y不超过1e18,则存前log1e18 的长度即可,然后递归处理,如果x很大就二分减小范围。
#include
using namespace std;
#define LL long long
#define FI first
#define SE second
#define PII pair
#define endl '\n'
#define PB push_back
const LL INF = 1e18,mod=1e9+7;
const int N = 1e5 + 7;
int n,k;
char a[N],b[N];
LL s[N];
int main()
{
scanf("%s%s",a,b);
int l=strlen(a),ll=strlen(b);
s[1]=l,s[2]=ll;
s[3]=l+ll;
int cnt=3;
while(s[cnt]<INF){
cnt++;
s[cnt]=s[cnt-1]+s[cnt-2];
}
int q;
cin>>q;
LL x;
LL y;
while(q--){
scanf("%lld%lld",&x,&y);
if(y>=s[3])
x=lower_bound(s+1,s+1+cnt,y)-s;
while(x>2){
if(y>s[x-1]){
y-=s[x-1];
x-=2;
}
else{
x-=1;
}
}
if(x==2){
printf("%c\n",b[y-1]);
}
else printf("%c\n",a[y-1]);
}
return 0;
}
F.无交集的圆
题目描述:
Y轴上有许多圆,所有圆的圆心位于Y轴,求有多少对圆是没有交集的(不包含、不相交、不相切)。
思路:将与y轴的上交点排序,然后对于每个圆与y轴的下交点二分查找有多少上交点在其下面
#include
using namespace std;
#define LL long long
#define FI first
#define SE second
#define PII pair
#define endl '\n'
#define PB push_back
const double INF = 1e17+7;
const int N = 1e5 + 7;
int n,m;
int a[N],b[N];
int main()
{
cin>>n;
double x,y;
for(int i=1;i<=n;i++){
scanf("%lf%lf",&x,&y);
a[i]=x-y,b[i]=x+y;
}
sort(a+1,a+1+n);
LL ans=0;
for(int i=1;i<=n;i++){
int k=upper_bound(a+1,a+1+n,b[i])-a;
ans+=(n-k+1);
}
cout<<ans;
return 0;
}
G-最长递增长度
题目描述
给定一个长度为n的整数序列S,求这个序列中最长的严格递增子序列的长度。
最长上升子序列模板题
#include
using namespace std;
#define LL long long
#define FI first
#define SE second
#define PII pair
#define endl '\n'
#define PB push_back
const int INF = 1e9+7;
const int N = 1e6 + 7;
int n,m;
int a[N];
int s[N];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)s[i]=INF;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
*lower_bound(s+1,s+1+n,a[i])=a[i];
}
int ans=lower_bound(s+1,s+1+n,INF)-s-1;
cout<<ans;
return 0;
}
H 虚无的后缀
链接:https://ac.nowcoder.com/acm/contest/911/H
来源:牛客网
题目描述
给出 n 个数字,第 i 个数字为 a[i],我们从中选出 k 个数字,使得乘积后缀 0 的个数最多。
思路:对于每个数字求出整除2和5的次数,然后以5为空间2为价格进行二维背包
#include
using namespace std;
#define LL long long
#define FI first
#define SE second
#define PII pair
#define endl '\n'
#define PB push_back
const int INF = 1e17+7,mod=1e9+7;
const int N = 1e5 + 7;
int n,k;
int s[201][5202];
int q[201][5202];
int main()
{
cin>>n>>k;
LL x;
q[0][0]=1;
for(int i=1;i<=n;i++){
scanf("%lld",&x);
int t=0,f=0;
while(x%5==0){
f++;
x/=5;
}
while(x%2==0){
t++;
x/=2;
}
for(int j=k;j>=1;j--){
for(int c=n*26;c>=f;c--){
if(q[j-1][c-f]){
q[j][c]=1;
s[j][c]=max(s[j][c],s[j-1][c-f]+t);
}
}
}
}
int ans=0;
for(int i=0;i<=n*26;i++){
ans=max(ans,min(i,s[k][i]));
}
cout<<ans;
return 0;
}
I 大吉大利
链接:https://ac.nowcoder.com/acm/contest/911/I
来源:牛客网
题目描述
有 n 个人,编号为 1 ~ n,第 i 个人有 a[i] 枚金币,若第一个人金币数大于 0,则可以选择一个i(2≤i≤n) 然后,弃置 1 枚金币,让第 i 个人弃置 b[i] 枚金币,若第 i 个人金币数少于 b[i] 则弃置所有金币。现需要让第 1 个⼈人弃置最少的⾦金金币,成为唯⼀的金币数最多的人。
思路:二分答案检查是否合理
#include
using namespace std;
#define LL long long
#define FI first
#define SE second
#define PII pair
#define endl '\n'
#define PB push_back
const int INF = 1e9+7;
const int N = 1e5 + 7;
int n,m;
int a[N];
int b[N];
int check(int x){
int re=0;
int num=a[1]-x-1;
for(int i=2;i<=n;i++){
if(a[i]>=num){
int k=(a[i]-num)/b[i];
if((a[i]-num)%b[i])k++;
re+=k;
}
}
return re<=x;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=2;i<=n;i++){
scanf("%d",&b[i]);
}
int ans=a[1]+1;
int l=0,r=a[1];
while(l<=r){
int mid=l+r>>1;
if(check(mid)){
ans=min(ans,mid);
r=mid-1;
}
else l=mid+1;
}
if(ans>a[1])cout<<-1;
else cout<<ans;
return 0;
}
J 异或的路径
链接:https://ac.nowcoder.com/acm/contest/911/J
来源:牛客网
题目描述
给一棵 n 个点的树,1 号节点为根,边有边权,令 f(u,v) 表示 u 节点到 v 节点,路径上边权异或值。求 ∑i=1n∑j=1nf(i,j),结果对 1000000007 取模。
思路:跑一遍dfs求出每个点到根的路径异或和,然后按二进制位数个数算贡献
#include
using namespace std;
#define LL long long
#define FI first
#define SE second
#define PII pair
#define endl '\n'
#define PB push_back
const int INF = 1e17+7,mod=1e9+7;
const int N = 1e5 + 7;
int n,k;
int a[N];
int cct[62];
int d[N];
vector<PII>v[N];
void dfs(int x,int y){
d[x]=y;
int z=y;
int cnt=0;
while(z){
if(z&1)cct[cnt]++;
cnt++;
z>>=1;
}
for(int i=0;i<v[x].size();i++){
dfs(v[x][i].FI,y^v[x][i].SE);
}
}
int main()
{
cin>>n;
int x,y;
for(int i=2;i<=n;i++){
scanf("%d%d",&x,&y);
v[x].PB(PII(i,y));
}
dfs(1,0);
LL ans=0;
for(int i=1;i<=n;i++){
for(int j=0;j<20;j++){
if((d[i]>>j)&1){
ans+=1ll*(1ll<<j)%mod*(n-cct[j]);
}
else ans+=1ll*(1ll<<j)%mod*cct[j];
ans%=mod;
}
}
cout<<ans%mod;
return 0;
}