2017中国大学生程序设计竞赛-哈尔滨站

大力施工......(10/13)剩下的三个题好像不太能写啊?


A.Palindrome(hdu6230)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6230


首先用Manacher处理出所有的回文半径,然后可以得到每个点覆盖的范围,问题转换成了,有多少对点,满足,i能覆盖j而且j也能覆盖i,我们按照回文半径从大到小进行排序,然后用线段树进行维护,然后每次查询当前点能覆盖的范围有多少点即可,更新答案,最后将该点更新到线段树上。


代码:

#include
using namespace std;
typedef long long ll;
const int MAXN=5e5+5;
char Ma[MAXN];
int Mp[MAXN];
void Manacher(int len)
{
	int l=len;
	Ma[0]='$';
	Ma[l]=0;
	int mx=0,id=0;
	for(int i=0;ii?min(Mp[2*id-i],mx-i):1;
		while(Ma[i+Mp[i]]==Ma[i-Mp[i]])Mp[i]++;
		if(i+Mp[i]>mx)
		{
			mx=i+Mp[i];
			id=i;
		}
	}
}
struct node
{
	int id,r;
	node(int _id=0,int _r=0):id(_id),r(_r){}
	bool operator <(const node &a)const
	{
		return r>a.r;
	}
}sv[MAXN];
struct seg
{
	#define lson l,mid,rt<<1
	#define rson mid+1,r,rt<<1|1
	int sum[MAXN<<2];
	inline void push_up(int rt)
	{
		sum[rt]=sum[rt<<1]+sum[rt<<1|1];
	}
	void build(int l,int r,int rt)
	{
		sum[rt]=0;
		if(l==r)
			return ;
		int mid=(l+r)>>1;
		build(lson);
		build(rson);
	}
	void update(int pos,int val,int l,int r,int rt)
	{
		if(l==r)
		{
			sum[rt]=val;
			return ;
		}
		int mid=(l+r)>>1;
		if(pos<=mid)
			update(pos,val,lson);
		if(pos>mid)
			update(pos,val,rson);
		push_up(rt);
	}
	int query(int L,int R,int l,int r,int rt)
	{
		if(L<=l&&r<=R)
			return sum[rt];
		int ret=0;
		int mid=(l+r)>>1;
		if(L<=mid)
			ret+=query(L,R,lson);
		if(mid


B.K-th Number(hdu6231)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6231


二分答案,统计有多少个区间的第k大的值大于等于我们二分的答案,如果区间数大于m,就提高下界继续二分,否则降低上界。计数的时候可以采用双指针。


代码:

#include
using namespace std;
typedef long long ll;
const int MAXN=1e5+5;
int a[MAXN],HASH[MAXN],tot,n,k;
ll m;
vector sv;
int getid(int x)
{
	int ret=lower_bound(HASH+1,HASH+1+tot,x)-HASH;
	return ret;
}
bool judge(int mid)
{
	ll ret=0;
	sv.clear();
	sv.push_back(0);
	for(int i=1;i<=n;i++)
	{
		if(a[i]>=mid)
		{
			sv.push_back(i);
		}
	}
	for(int i=0;i=m;
}
void solve()
{
	tot=0;
	scanf("%d%d%lld",&n,&k,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		HASH[++tot]=a[i];
	}
	sort(HASH+1,HASH+1+tot);
	tot=unique(HASH+1,HASH+1+tot)-HASH-1;
	for(int i=1;i<=n;i++)
	{
		a[i]=getid(a[i]);
	}
	int l=1,r=tot;
	int ans=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(judge(mid))
		{
			l=mid+1;
			ans=mid;
		}
		else
		{
			r=mid-1;
		}
	}
	printf("%d\n",HASH[ans]);
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		solve();
	}
	return 0;
}


C.Confliction(hdu6232)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6232


将运动转换为B对于A的相对运动,然后答案就是经过次数最多的那个点,然后离散化时间和坐标,进行差分求解出现的次数最多的那个点就好了,不过挺难写的啊?


代码:

#include
using namespace std;
typedef long long ll;
const int MAXN=2e5+5;
int Na,Nb,Ac[MAXN],Bc[MAXN];
ll At[MAXN],Bt[MAXN],Hash[MAXN*2],sz;
vector > seg;
void solve()
{
	ll pos=0;
	seg.clear();
	scanf("%d",&Na);
	for(int i=1;i<=Na;i++)
	{
		scanf("%d%lld",&Ac[i],&At[i]);
		if(At[i]==0) i--,Na--;
	}
	scanf("%d",&Nb);
	for(int i=1;i<=Nb;i++)
	{
		scanf("%d%lld",&Bc[i],&Bt[i]);
		if(Bt[i]==0) i--,Nb--;
	}
	sz=0;
	for(int i=1;i<=Na;i++)
	{
		At[i]+=At[i-1];Hash[++sz]=At[i];
	}
	for(int i=1;i<=Nb;i++)
	{
		Bt[i]+=Bt[i-1];Hash[++sz]=Bt[i];
	}
	sort(Hash+1,Hash+1+sz);
	sz=unique(Hash+1,Hash+1+sz)-Hash-1;
	seg.push_back(make_tuple(0,3,1));
	seg.push_back(make_tuple(1,3,-1));
	for(int i=1,pa=1,pb=1;i<=sz;i++)
	{
		while(At[pa]r) swap(l,r);
			int k=3;
			if(delta==2||delta==-2) k=(l&1)?1:2;
			seg.push_back(make_tuple(l,k,1));
			seg.push_back(make_tuple(r+1,k,-1));
		}
		pos+=t*delta;
	}
	sort(seg.begin(),seg.end());
	ll mx=0,cnt0=0,cnt1=0;
	for(int i=0,j=0;i(seg[j])==get<0>(seg[i]))
		{
			if(get<1>(seg[j])&1) cnt1+=get<2>(seg[j]);
			if(get<1>(seg[j])&2) cnt0+=get<2>(seg[j]);
			j++;
		}
		if(get<0>(seg[i])&1) mx=max(mx,cnt1);
		else mx=max(mx,cnt0);
	}
	printf("%lld\n",mx);
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		solve();
	}
	return 0;
}


D.X-Men(hdu6233)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6233


这可能是本场比赛第二简单的题目???现场榜被带歪了啊???其实理性分析一下,可以发现,每次最远的两个人的距离一定会减少2,所以直接直径除二向下取整数就好了。


代码:

#include
using namespace std;
const int MAXN=1005;
struct edge
{
	int to,nxt;
}E[MAXN*2];
int head[MAXN],tot,root,mx;
bool book[MAXN];
void addedge(int u,int v)
{
	E[tot].to=v;E[tot].nxt=head[u];head[u]=tot++;
	E[tot].to=u;E[tot].nxt=head[v];head[v]=tot++;
}
void init(int n)
{
	tot=0;
	memset(book,0,sizeof(book));
	for(int i=1;i<=n;i++)
	{
		head[i]=-1;
	}
}
void dfs(int now,int fa,int dp)
{
	if(book[now]&&dp>mx)
	{
		mx=dp;
		root=now;
	}
	for(int i=head[now];~i;i=E[i].nxt)
	{
		int v=E[i].to;
		if(v==fa)
			continue;
		dfs(v,now,dp+1);
	}
}
void solve()
{
	int n,m;
	scanf("%d%d",&n,&m);
	init(n);
	for(int i=1;i<=m;i++)
	{
		int x;
		scanf("%d",&x);
		book[x]=true;
		root=x;
	}
	for(int i=1;i


F.Permutation(hdu6235)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6235


水题,随便构造一下就好。。。


代码:

#include
using namespace std;
const int MAXN=1e5+5;
int a[MAXN];
void solve()
{
	int n;
	scanf("%d",&n);
	int cnt=1;
	for(int i=1;i<=n;i+=2)
	{
		a[i]=cnt++;
	}
	for(int i=2;i<=n;i+=2)
	{
		a[i]=cnt++;
	}
	printf("%d",a[1]);
	for(int i=2;i<=n;i++)
	{
		printf(" %d",a[i]);
	}
	printf("\n");
}

int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		solve();
	}
	return 0;
}


H.A Simple Stone Game(hdu6237)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6237


首先我们将数组a的所有值加起来得到sum,然后求sum的所有的素因子,然后直接枚举所有的素因子就好了,设我们当前的素因子为p,我们对a数组所有的值对p取模,然后按照从大到小排序,然后贪心的移动就好了(每次把最大的填到p),存素因子的数组不用long long wa的莫名其妙?


代码:

#include
using namespace std;
typedef long long ll;
const int MAXN=1e5+5;
int a[MAXN],b[MAXN];
ll pre[MAXN];
vector sv;
void Prime(ll x)
{
	sv.clear();
	for(ll i=2;i*i<=x;i++)
	{
		if(x%i==0) sv.push_back(i);
		while(x%i==0) x/=i;
	}
	if(x!=1) sv.push_back(x);
}
void solve()
{
	int n;
	ll sum=0,ans=1e18;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		sum+=a[i];
	}
	Prime(sum);
	for(int i=0;i=1;j--)
		{
			if(tot==pre[j]) break;
			tot+=p-b[j];
		}
		ans=min(ans,tot);
	}
	printf("%lld\n",ans);
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		solve();
	}
	return 0;
}


J.Interview(hdu6239)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6239


我们队猜出了个线性的式子...可能正解要用到i^k (k<=3)的求和公式,不知道我们这个式子为啥是对的...可能最后化简出来就是我们的式子吧,具体看代码


代码:

#include
using namespace std;
typedef long long ll;
const int MOD=1e9+7;
ll qpow(ll a,ll b)
{
	ll ret=1;
	while(b)
	{
		if(b&1)
		{
			ret*=a;
			ret%=MOD;
		}
		a=(a*a)%MOD;
		b>>=1;
	}
	return ret;
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n,d;
		scanf("%d%d",&n,&d);
		if(d==1)
			printf("%lld\n",(2LL*n+4LL)%MOD*qpow(8,MOD-2)%MOD);
		else
			printf("%lld\n",(3LL*n+6LL)%MOD*qpow(8,MOD-2)%MOD);
	}
	return 0;
}


K.Server(hdu6240)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6240


分数规划经典问题哦,感觉自己分数规划还是掌握的不太好啊,看了zzq神犇的博客重新回忆了一下,发现自己为啥在求最大值啊?首先肯定是要二分答案的,然后,就是转化为最小值是否大于0的问题,对于最小值的问题,可以直接进行dp,我们首先按照右端点进行排序,对于将所有花费变为a-mid*b,然后对于所有花费为负的线段,我们一定会选,所以我们首先把花费为负的线段花费变成0,然后进行dp,最后将所有的为负的花费的权值加入,即可得到最小花费,dp的过程要用线段树来维护。好像跑最短路也可以?


代码:

#include
using namespace std;
const int MAXN=1e5+5;
const double INF=1e9;
const double eps=1e-6; 
int n,t;
struct monitor
{
	int s,t,a,b;
	double val;
	bool operator < (const monitor &o)const
	{
		return t>1;
		build(lson);
		build(rson);
	}
	void update(int pos,double val,int l,int r,int rt)
	{
		if(l==r)
		{
			mi[rt]=min(val,mi[rt]);
			return ;
		}
		int mid=(l+r)>>1;
		if(mid>=pos)
			update(pos,val,lson);
		if(mid>1;
		double ret=INF;
		if(L<=mid)
			ret=min(ret,query(L,R,lson));
		if(mid0)
		{
			l=mid;
			ans=mid;
		}
		else
		{
			r=mid;
		}
	}
	printf("%.3lf\n",ans);
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		solve();
	}
	return 0;
}


L.Color a Tree(hdu6241)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6241


二分答案,然后将子树外的问题转换成子树内最多有多少点的问题,然后从叶子开始合并区间,判断每个节点是否可行,最后还要判断二分的值是否在根的可行区间当中。


代码:

#include
using namespace std;
const int MAXN=1e5+5;
struct edge
{
	int v,nxt;
}E[MAXN*2];
struct node
{
	int up,down;
}tr[MAXN];
struct lim
{
	int id,num;
}A[MAXN],B[MAXN];
int head[MAXN],sz[MAXN],tot,n,na,nb;
bool flag;
void init()
{
	tot=0;
	memset(head,-1,sizeof(head));
}
void addedge(int u,int v)
{
	E[tot].v=v;E[tot].nxt=head[u];head[u]=tot++;
	E[tot].v=u;E[tot].nxt=head[v];head[v]=tot++;
}
void getlim(int mid)
{
	for(int i=1;i<=n;i++)
	{
		tr[i].down=0,tr[i].up=sz[i];
	}
	for(int i=1;i<=na;i++)
	{
		int id=A[i].id,num=A[i].num;
		tr[id].down=max(tr[id].down,num);
	}
	for(int i=1;i<=nb;i++)
	{
		int id=B[i].id,num=B[i].num;
		tr[id].up=min(tr[id].up,mid-num);
	}
	for(int i=1;i<=n&&flag;i++)
	{
		if(tr[i].down>tr[i].up)
			flag=false;
	}
}
void dfssz(int now,int fa)
{
	sz[now]=1;
	for(int i=head[now];~i;i=E[i].nxt)
	{
		int v=E[i].v;
		if(v==fa)
			continue;
		dfssz(v,now);
		sz[now]+=sz[v];
	}
}
void dfslim(int now,int fa)
{
	int down=0,up=0;
	for(int i=head[now];(~i)&&flag;i=E[i].nxt)
	{
		int v=E[i].v;
		if(v==fa)
			continue;
		dfslim(v,now);
		down+=tr[v].down,up+=tr[v].up;
	}
	tr[now].down=max(tr[now].down,down);
	tr[now].up=min(tr[now].up,up+1);
	//printf("%d:%d %d\n",now,tr[now].down,tr[now].up);
	if(tr[now].down>tr[now].up)
		flag=false;
}
void judge(int mid)
{
	getlim(mid);
	dfslim(1,0);
	if(!(tr[1].down<=mid&&mid<=tr[1].up))
		flag=false;
}
void solve()
{
	init();
	scanf("%d",&n);
	for(int i=1;i>1;
		judge(mid);
		if(flag)
		{
			r=mid-1;
			ans=mid;
		}
		else
		{
			l=mid+1;
		}
	}
	printf("%d\n",ans);
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		solve();
	}
	return 0;
}


M.Geometry Problem(hdu6242)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6242


理性分析一下,就会发现,随机三个点然后求圆心即可,期望是8次就能随中。


代码:

#include
using namespace std;
const int MAXN=1e5+5;
const double eps=1e-6;
int sgn(double x) 
{
	if(fabs(x)=2&&n<=4)
	{
		gettwo(P[1],P[2]);
		return ;
	}
	while(1)
	{
		memset(id,0,sizeof(id));
		getid();
		if(!judge(P[id[0]],P[id[1]],P[id[2]]))
			continue;
		Circle res=CircumCircle(Traingle(P[id[0]],P[id[1]],P[id[2]]));
		int cnt=0;
		for(int i=1;i<=n;i++)
		{
			double dis=Dis(P[i],res.center);
			if(sgn(dis-res.r)==0)
				cnt++;
		}
		if(cnt>=(n+1)/2)
		{
			printf("%lf %lf %lf\n",res.center.x,res.center.y,res.r);
			return ;
		}
	}
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int T;
	srand(0);
	scanf("%d",&T);
	while(T--)
	{
		solve();
	}
	return 0;
}


你可能感兴趣的:(套题)