2018.07.18【2018提高组】模拟C组

111 篇文章 0 订阅
99 篇文章 1 订阅

前言:再一次OTL


JZOJ 3508 好元素

题目

求满足 a [ m ] + a [ n ] + a [ p ] = a [ i ] a[m]+a[n]+a[p]=a[i] a[m]+a[n]+a[p]=a[i]的i的个数


分析

移项后得到 a [ m ] + a [ n ] = a [ i ] − a [ p ] a[m]+a[n]=a[i]-a[p] a[m]+a[n]=a[i]a[p],所以用 O ( n 2 ) O(n^2) O(n2)哈希存储,再用 O ( n 2 ) O(n^2) On2找到答案。


代码

#include <cstdio>
#include <cctype>
#define p 19996001(抠门的哈希函数)
int n,a[5001],hash[p],ans;
inline int in(){
	int ans=0,f=1; char c=getchar();
	while (!isdigit(c)&&c!='-') c=getchar();
	if (c=='-') c=getchar(),f=-f;
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans*f;
}
int locate(int x){//找哈希表的位置
	int pos=(x%p+p)%p,i=0; 
	while (i<p&&hash[(pos+i)%p]!=p+1&&hash[(pos+i)%p]!=x) i++;
    return (pos+i)%p;
}
signed main(){
	freopen("good.in","r",stdin);
	freopen("good.out","w",stdout);
	n=in(); for (int i=0;i<p;i++) hash[i]=p+1;
	for (int i=1;i<=n;i++){
		a[i]=in(); bool flag=0; int x;
		for (int j=1;j<i&&!flag;j++) if (hash[locate(a[i]-a[j])]==a[i]-a[j]) ans++,flag=1;//查询  
		if (i<n) for (int j=1;j<=i;j++) hash[locate(a[i]+a[j])]=a[i]+a[j];//补充哈希表
	}
    return !printf("%d",ans);	
}

JZOJ 3509 倒霉的小C

题目

1 + ∑ i = 1 n g c d ( n , i ) 1+\sum_{i=1}^ngcd(n,i) 1+i=1ngcd(n,i)


分析

这个问题可以改变成 1 + ∑ d ∣ n d ∗ φ ( n / d ) , φ ( n ) 指 1 到 n 中 与 n 互 质 的 数 1+\sum_{d|n}d*\varphi(n/d),\varphi(n)指1到n中与n互质的数 1+dndφ(n/d)φ(n)1nn,证明不会遗漏,不会重复比较简单,算出 φ \varphi φ说明了每个数只会出现一次,时间复杂度 O ( n 的 约 数 log ⁡ n 的 约 数 ) O(n的约数\log n的约数) O(nlogn)


代码

#include <cstdio>
using namespace std;
typedef long long ll;
ll ans,n;
ll phi(ll n){
	ll ans=n;
	for (ll i=2;i*i<=n;i++)
	if (n%i==0){
		ans=ans/i*(i-1);
		while (n%i==0) n/=i;
	}
	if (n>1) ans=ans/n*(n-1);
	return ans;
}
int main(){
	freopen("beats.in","r",stdin);
	freopen("beats.out","w",stdout);
	scanf("%lld",&n); ans=1;
	for (ll i=1;i*i<=n;i++)
	if (n%i==0){
		ans+=i*phi(n/i);
		if (i*i!=n) ans+=n/i*phi(i);
	}
	return !printf("%lld",ans);
}

JZOJ 3510 最短路径

题目

在每个点经过一次的情况下从 A 走到 B,再回到 A(A点两次) 的最短路径。并且到B的路上横坐标要从小到大走, b 1 b_1 b1只能在到B的路上走, b 2 b_2 b2只能在回A的路上走,回A时横坐标要从大到小走。


分析

题目满足无后效性,所以是动态规划,两点的直线距离比较容易,但问题是如何dp
f [ i ] [ j ] f[i][j] f[i][j]表示去B的路经过i,回A的路经过j的最短路径,显然得到( k = m a x ( i , j ) + 1 k=max(i,j)+1 k=max(i,j)+1)
f [ k ] [ j ] = m i n ( f [ k ] [ j ] , f [ i ] [ j ] + d i s [ i ] [ k ] ) 当 k 未 经 过 b 2 f[k][j]=min(f[k][j],f[i][j]+dis[i][k])当k未经过b2 f[k][j]=min(f[k][j],f[i][j]+dis[i][k])kb2
f [ i ] [ k ] = m i n ( f [ i ] [ k ] , f [ i ] [ j ] + d i s [ j ] [ k ] ) 当 k 未 经 过 b 1 f[i][k]=min(f[i][k],f[i][j]+dis[j][k])当k未经过b1 f[i][k]=min(f[i][k],f[i][j]+dis[j][k])kb1
注意 i = j 和 f [ n ] [ n ] i=j和f[n][n] i=jf[n][n]要特判


代码

#include <bits/stdc++.h>
#define N 1001
using namespace std;
double f[N][N],dis[N][N]; int n,b1,b2,x[N],y[N];
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int main(){
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
	n=in(); b1=in(); b2=in();
	for (int i=1;i<=n;i++){
		x[i]=in(); y[i]=in();
		for (int j=1;j<i;j++) dis[i][j]=dis[j][i]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));//求距离
		for (int j=1;j<=n;j++) f[i][j]=2147483647;
	}
	f[1][1]=0;
	for (int i=1;i<=n;i++)
	for (int j=1;j<=n;j++){
		if (i==j&&i>1) continue;
		int k=max(i,j)+1;//下一个点
		if (k>n){//走到终点
			if (i<n) f[n][n]=min(f[n][n],f[i][j]+dis[i][n]);
			if (j<n) f[n][n]=min(f[n][n],f[i][j]+dis[j][n]);
		}
		else{
			if (k!=b2+1) f[k][j]=min(f[k][j],f[i][j]+dis[i][k]);//没有经过b2
			if (k!=b1+1) f[i][k]=min(f[i][k],f[i][j]+dis[j][k]);//没有经过b1
		}
	}
	return !printf("%.2lf",f[n][n]);
}

JZOJ 3511 游戏节目

题目

队伍A,B,C有n个游戏节目,玩第i个游戏,分别可得A[i],B[i],C[i]的分数。从n个游戏节目里面挑选至少k个节目出来(被选中的节目不分次序),使得队伍A成为赢家。队伍A能成为赢家的条件是队伍A的总得分要比队伍B的总得分要高,同时也要比队伍C的总得分要高。求有多少种不同的选取方案。


分析

这道题直接求会TLE(要开long long),所以可以用总方案-不够k个节目的方案,而由于 k ≤ 7 k\leq7 k7,所以比较容易深搜,那问题是怎样求总方案,可以双向搜索,把1n/2和n/2+1n中的所有可能存下来,离散后用树状数组维护,那具体如何排序 s u m a − b + s u m a − c &gt; 0 sum_{a-b}+sum_{a-c}&gt;0 sumab+sumac>0,移项后得到 s u m a − b &gt; − s u m a − c sum_{a-b}&gt;-sum_{a-c} sumab>sumac,所以最后时间复杂度
O ( 2 17 l o g 2 17 + ∑ i = 0 6 C 34 i ) ≈ O ( 1544488 + 1676116 ) = O ( 3220604 ) ≈ O ( 3.22 ∗ 1 0 6 ) O(2^{17}log2^{17}+\sum_{i=0}^{6} C_{34}^i)\approx O(1544488+1676116)=O(3220604)\approx O(3.22*10^6) O(217log217+i=06C34i)O(1544488+1676116)=O(3220604)O(3.22106)


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rep(i,a,b) for (ll i=a;i<=b;i++)
using namespace std;
typedef long long ll; ll ab,ac,j=1,ans,ans1,num1,num2,t;
struct spd{ll ab,ix; bool mark;}k3[262145];
struct rec{ll ab,ac;}k1[131073],k2[131073];
int n,k,a[35],b[35],c[35],s[262145]; 
ll in(){
	ll ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
void dfs(ll dep,ll now){
	if (dep>=k) return; if (ab>0&&ac>0) ans++;
	rep(i,now+1,n) ab+=a[i]-b[i],ac+=a[i]-c[i],dfs(dep+1,i),ab-=a[i]-b[i],ac-=a[i]-c[i];
}
void dfs1(ll now){k1[++num1]=(rec){ab,ac}; rep(i,now+1,n>>1) ab+=a[i]-b[i],ac+=a[i]-c[i],dfs1(i),ab-=a[i]-b[i],ac-=a[i]-c[i];}
void dfs2(ll now){k2[++num2]=(rec){ab,ac}; rep(i,now+1,n) ab+=a[i]-b[i],ac+=a[i]-c[i],dfs2(i),ab-=a[i]-b[i],ac-=a[i]-c[i];}
bool cmp1(spd x,spd y){return x.ab<y.ab;} bool cmp2(rec x,rec y){return x.ac<y.ac;} bool cmp3(rec x,rec y){return x.ac>y.ac;}
void add(ll x){while (x<=t) s[x]++,x+=-x&x;} ll answer(ll x){ll ans=0; while (x) ans+=s[x],x-=-x&x; return ans;}
int main(){
	freopen("show.in","r",stdin);
	freopen("show.out","w",stdout);
	n=in(); k=in(); t=1;
	rep(i,1,n) a[i]=in(); rep(i,1,n) b[i]=in(); rep(i,1,n) c[i]=in();
	dfs(0,0); dfs1(0); dfs2(n>>1);//三次深搜
	rep(i,1,num1) k3[i]=(spd){k1[i].ab,i,0};
	rep(i,1,num2) k3[i+num1]=(spd){-k2[i].ab,i,1};
	stable_sort(k3+1,k3+1+num1+num2,cmp1);//第一次快排
	if (!k3[1].mark) k1[k3[1].ix].ab=1; else k2[k3[1].ix].ab=1;
	rep(i,2,num1+num2){//离散
		if (k3[i].ab!=k3[i-1].ab) t++;
		if (!k3[i].mark) k1[k3[i].ix].ab=t; else k2[k3[i].ix].ab=t;
	}
	stable_sort(k1+1,k1+1+num1,cmp2); stable_sort(k2+1,k2+1+num2,cmp3);//第二次快排
	rep(i,1,num1){
		while (j<=num2&&k1[i].ac+k2[j].ac>0) add(k2[j].ab),j++;//树状数组维护
		ans1+=answer(k1[i].ab-1);//找答案
	}
	return !printf("%lld",ans1-ans);//输出总方案-不合法的方案
}

后续

向出题人orz

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值