测试赛题解

A Codeforces-1555C Coin Rows

(签到题)这个题其实是一个比较简单的贪心题。题中主要题意为两个人要在一个2*N的地图上从左上角走到右下角,每个人的位移只有两种操作,向右或向下走下一步,每个格子上都有金币且只能被取一次,若第一个人希望第二个人的金币获取量最小情况下,第二个人最多能获取多少金币。
题解:可以考虑到当第一个人走过后,其实是把整个地图分为了两份,左下和右上,因为第二个人为了让金币尽可能多,就要尽可能减少和第一个人路径的重复部分。所以这道题就可以通过枚举第一个人的向下行走的转折点,对每个情况下的左下部分和右上部分比较出最大值,再比较出所有情况中的第二个人所能获取金币的最大值。

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+7;
long long mod=1e9+7;
long long a[maxn],b[maxn],cnt1[maxn],cnt2[maxn];

void solve(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		cin>>b[i];
	}
	cnt1[n+1]=cnt2[0]=0;
	for(int i=n;i>=1;i--){
		cnt1[i]=cnt1[i+1]+a[i];
	}
	for(int i=1;i<=n;i++){
		cnt2[i]=cnt2[i-1]+b[i];
	}
	long long m=1e18;
	for(int i=1;i<=n;i++){
		m=min(m,max(cnt1[i+1],cnt2[i-1]));
	}
	cout<<m;
}

int main() {
	std::ios::sync_with_stdio(false);
	int m=1;
	cin>>m;
	while(m--){
		solve();
		cout<<"\n";
	}
}

B POJ-1988 Cube Stacking

这个题是一道加权并查集的题。题的大意是现在给N个立方体堆,每堆一个立方体,现有两个操作,M a b:把a的立方体堆放在b立方体堆的顶部,C a:查找a立方体下面有几个立方体。
题解:这个题是一个加权并查集的板子题,不同的地方在于并不是问一个点到顶部的距离,而是问一个点到底部的距离,这是就需要做一点变通。建立up[]和down[]数组,up数组用来存这个点到顶点的距离,down数组用来存每个顶点到底部的距离,则查找的答案就是down[顶点]-up[查找点]。up的数组在每次find顶点时更新,先进性find操作,自身up值加上原先顶点的up值即可。down数组在每次合并操作时更新,只要顶点的down值加上被合并顶点的down值即可,这时要对被合并的顶点的up值进行赋值,直接赋值为合并顶点的原始down值。

#include <iostream>
#include <string.h>
#include <stdio.h> 
#include <algorithm>
#include <math.h>
using namespace std;
int f[30010],root[30010],depth[30010];
int find(int x){
	if(f[x]==x){
		return x;
	}
	int t=f[x];
	f[x]=find(f[x]);
	root[x]+=root[t];
	return f[x];
}
void un(int a,int b){
	int fa=find(a);
	int fb=find(b);
	f[fb]=fa;
	root[fb]=depth[fa];
	depth[fa]+=depth[fb];
}
 
int main(){
	int n;
	scanf("%d",&n);
	char a;
	int b,c;
	for(int i=1;i<30010;i++){
		f[i]=i;
		root[i]=0;
		depth[i]=1;
	}
	for(int i=0;i<n;i++){
		getchar();
		a=getchar();
		if(a=='M'){
			scanf("%d%d",&b,&c);
			un(b,c);
		}else{
			scanf("%d",&b);
			int fb=find(b);
			printf("%d\n",depth[fb]-root[b]-1);
		}
	}
}

C POJ - 2182 Lost Cows

这个题是一道线段树的题。题目大意是有串没有排序的数字(1-N),告诉你每个数字前面有几个比自己小的数字,问你每个位置是什么数字。
题解:这个题可以用线段树或者树状数组来写都可以,主要就是为了实现在有删除的情况下知道有序数组的第几位是哪个数。并且因为这个题是知道前面有几个比自己小的数,所以这个题的求解顺序应该是从后向前求解,因为后面的数删除不会对前面数的答案产生影响,所以这是最优求解方式。一般来说给前面就从后面开始求,给后面就从前面开始求。

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=8010;
struct node{
	int v;
	int l,r;
}tree[maxn<<2];
int a[maxn];
void updata(int root){
	tree[root].v=tree[root<<1].v+tree[root<<1|1].v;
}
void build(int root,int l,int r){
	tree[root].l=l,tree[root].r=r;
	if(l==r){
		tree[root].v=1;
		return;
	}
	int mid=(l+r)>>1;
	build(root<<1,l,mid);
	build(root<<1|1,mid+1,r);
	updata(root);
}
int query(int root,int k){
	tree[root].v--;
	if(tree[root].l==tree[root].r){
		return tree[root].l;
	}
	if(tree[root<<1].v>=k) return query(root<<1,k);
	else return query(root<<1|1,k-tree[root<<1].v);
}

int main(){
	int n;
	cin>>n;
	build(1,1,n);
	a[0]=0;
	for(int i=1;i<n;i++){
		cin>>a[i];
	}
	for(int i=n-1;i>=0;i--){
		a[i]+=1;
		a[i]=query(1,a[i]);
	}
	for(int i=0;i<n;i++){
		cout<<a[i]<<endl;
	}
} 

D POJ - 2356 Find a multiple

这个题是一个数论题,用的是鸽巢原理。题目大意是给定一个N和N个数,要求你选择其中几个数,使他们的和是N的倍数。
题解:这个题就是一道鸽巢原理的入门应用题。鸽巢原理是指,如果有n-1个格子,放入n个物体,至少会有一个格子放了两个物体。对应这道题,可先对数组进行前缀和运算,再对每个前缀和对N取模,如果有取模结果为0的前缀和,那就直接从1到这个数输出即可,如果没有0出现,那么对于这个前缀和数列来说,值就是(1-(N-1)),因为这个数列有n个数,但是只有n-1种值,所有通过鸽巢原理可得,其中一定至少有一对相同的数,若两个数相同,说明两个位置之间的数相加为N的倍数,所以直接输出这一段数字即可。

#include<iostream>
using namespace std;
const int maxn=1e6+7;
long long mod=998244353;
int a[maxn],b[maxn],l=0,r,cnt[maxn];

int main() {
    std::ios::sync_with_stdio(false);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
    	cin>>a[i];
    	b[i]=a[i]+b[i-1];
	}
	for(int i=1;i<=n;i++){
		b[i]%=n;
		if(b[i]==0){
			r=i;
			break;
		}
	}
	if(r){
		cout<<r-l<<"\n";
		for(int i=l+1;i<=r;i++) cout<<a[i]<<"\n";
		return 0;
	}
	for(int i=1;i<=n;i++){
		if(cnt[b[i]]==0) cnt[b[i]]=i;
		else{
			l=cnt[b[i]];
			r=i;
			break;
		}
	}
	cout<<r-l<<"\n";
	for(int i=l+1;i<=r;i++) cout<<a[i]<<"\n";
}

E HDU - 3826 Squarefree number

这个题是一个数论题,主要是对素数的处理。题目大意是给你一个数,问你这个数是不是素数平方的倍数。
题解:这个题主要是对素数的处理,因为数据范围给的比较大,是1e18,所以直接找出所有的素数再进行遍历这是不可能的。所以我们应该想办法减小查找的区间。因为我们能看出来1e18是1e6的三次方,所以对于任意一个不超过1e18的数来说,最多只能为两个超过1e6的数的乘积。所以我们就可以通过这个判断,把素数分为小于1e6部分和大于1e6部分。对小于1e6部分我们能直接筛出所有的素数,然后遍历对这个N进行查找(查找素因子),如果有一个素因子可消去两遍,则可以出结果,否则就继续消去素因子。当所有小于1e6的素因子都消去后,还未出结果,那就看N的值是否还大于1,如果大于1说明它至少还有一个大于1e6的素数,这时可以直接判断剩下的N是否是一个素数的平方值。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+7;
priority_queue<int> q;
long long mod=1e9+7;
long long su[maxn];
int vis[maxn];
int num=1;

void find(){
	for(int i=2;i<maxn;i++){
		if(!vis[i]) su[num++]=i;
		for(int j=1;j<num;j++){
			if(1ll*i*su[j]>=maxn) break;
			vis[i*su[j]]=1;
			if(i%su[j]==0) break;
		}
	}
}
int main() {
    std::ios::sync_with_stdio(false);
    int t;
    find();
    cin>>t;
    for(int ti=1;ti<=t;ti++){
    	cout<<"Case "<<ti<<": ";
    	long long n;
    	int f=0;
    	cin>>n;
    	for(int i=1;i<num;i++){
    		if(n%su[i]==0){
    			int cnt=0;
    			while(n%su[i]==0) n/=su[i],cnt++;
    			if(cnt>=2){
    				cout<<"No\n";
    				f=1;
    				break;
				}
			}
		}
		if(f) continue;
		if(n>1){
			long long k=sqrt(n);
			if(k*k==n) cout<<"No\n";
			else cout<<"Yes\n";
		}else{
			cout<<"Yes\n";
		}
	}
}

F HDU - 1907 John

这个题是一个尼姆博弈题。题目大意是两个小孩吃糖豆,每次至少吃一个糖豆,并且每次吃的糖豆颜色都要是相同的。谁要是最后吃完了所有糖,谁就败。
题解:尼姆博弈中若最后取的输,面对奇数个1必输 或 存在有数大于1且面对xor和为0时必输。这个题本来不该拉的,但是我没看清题以为就是普通的尼姆博弈就直接拉过来了,写题解的的时候才发现,就挺离谱的。。。。。进一步证明

#include<bits/stdc++.h>

using namespace std;

const int maxn=2e6+7;

typedef pair<int,int>iit;

int a[maxn];


int main(){
    int t;
    scanf("%d",&t);
    while(t--){
    	int n;
    	cin>>n;
    	for(int i=0;i<n;i++) cin>>a[i];
    	int ans=0,f=0;
    	for(int i=0;i<n;i++){
    		ans^=a[i];
    		if(a[i]>1) f=1;
		}
    	if((ans==0&&f)||(ans&&f==0)) cout<<"Brother\n";
    	else cout<<"John\n";
    }
}

G HDU - 1520 Anniversary party

这个题是一个树形dp的题。题目大意是要举行排队,不让雇员和直接上司同时出席,每个人都有一个快乐值,则在该情况下,总快乐值最大是多少。
题解:这道题就是一道很普通的树形dp的题,通过对每个雇员和其直接上司之间建边,形成一个树,对每个结点进行dp操作,dp[i][0] 表示i不在场,dp[i][1] 表示i在场,dp[i][0]= ∑ max(dp[j][0],dp[j][1]) j为i的子结点,dp[i][1] = v[i]+ ∑ dp[j][0]。然后求解max(dp[领导][0],dp[领导][1]) 即可。

#include<iostream>
#include<cstdio>
#include<algorithm> 
#include<cstring>
#include<string>
#include<vector>
using namespace std; 
const int maxn=6010;
int dp[maxn][2]; 
int vv[maxn];
int fa[maxn];
vector<int>v[maxn];
int n;
void dfs(int now){
	dp[now][0]=0;
	dp[now][1]=vv[now];
	for(int i:v[now]){
		dfs(i);
		dp[now][0]+=max(dp[i][1],dp[i][0]);
		dp[now][1]+=dp[i][0];
	}
}

int main(){
	while(cin>>n){
		for(int i=1;i<=n;i++){
			cin>>vv[i];
			v[i].clear();
			fa[i]=-1;
		}
		int a,b;
		while(cin>>a>>b&&a){
			v[b].push_back(a);
			fa[a]=b;
		}
		int t=1;
		while(fa[t]!=-1) t=fa[t];
		dfs(t);
		cout<<max(dp[t][0],dp[t][1])<<endl;
	}
}

H HDU - 1686 Oulipo

(签到题)这是一道kmp的板子题。题目的大意就是给你一个模板字符串,再给你一行文本,问这个字符串在文本中出现的次数。
题解:kmp的板子题,跑一遍就可以出结果

#include<bits/stdc++.h>

using namespace std;

const int maxn=2e6+7;

typedef pair<int,int>iit;

char s[maxn],ss[maxn];
int nxt[maxn];

void get(int len){
	nxt[0]=-1;
	nxt[1]=0;
	for(int i=1;i<len;i++){
		int k=nxt[i];
		while(k!=-1&&s[k]!=s[i]) k=nxt[k];
		nxt[i+1]=k+1;
	}
}


int main(){
    int t;
    scanf("%d",&t);
    while(t--){
    	scanf("%s",&s);
		scanf("%s",&ss);
		int len1=strlen(s),len2=strlen(ss);
		get(len1);
		int ans=0,k=0;
		for(int i=0;i<=len2;i++){
			if(k==len1) ans++;
			while(s[k]!=ss[i]&&k!=-1) k=nxt[k];
			k++;
		}
		printf("%d\n",ans);
    }
}

I HDU - 2544 最短路

(签到题)这个题是一个最短路的板子题。题目内容和题名一样

#include <iostream>
#include <string.h>
#include<algorithm>
using namespace std;
#define inf 2147483647
int head[1000000];
int m,n;
int cnt=0;
struct node{
	int to;
	int next;
	int v;
}edge[1000000];
bool vis[1000000];
int d[100000];
void addedge(int a,int b,int c){
	edge[cnt]={b,head[a],c};
	head[a]=cnt++;
}
void diji(int x){
	d[x]=0;
	for(int i=head[x];i!=-1;i=edge[i].next){
		int t=edge[i].to;
		d[t]=min(d[t],edge[i].v);
	}
	vis[x]=true;
	for(int i=1;i<m;i++){
		int u=-1,min=inf;
		for(int j=1;j<=m;j++){
			if(d[j]<min&&vis[j]==false){
				u=j;
				min=d[j];
			}
		}
		vis[u]=true;
		for(int j=head[u];j!=-1;j=edge[j].next){
			int t=edge[j].to;
			if(vis[t]==false&&d[t]>d[u]+edge[j].v){
				d[t]=d[u]+edge[j].v;
			}
		}
	}
}
int main(){
	while(~scanf("%d%d",&m,&n)){
		if(m==n&&n==0){
			break;
		}
		memset(head,-1,sizeof(head));
		int a,b,c;
		cnt=0;
		for(int i=0;i<n;i++){
			cin>>a>>b>>c;
			addedge(a,b,c);
			addedge(b,a,c);
		}
		for(int i=1;i<=m;i++){
			d[i]=inf;
			vis[i]=false;
		}
		diji(1);
		if(d[m]==inf){
			cout<<-1<<endl;
		}else{
			cout<<d[m]<<endl;
		}
	}
}
/*
5 6
0 1 1
1 2 2
2 3 3
3 4 4
0 2 5
1 4 6
0 3
*/

J HDU - 2819 Swap

这个题是一个二分图的题。题目大意是给一个矩阵,矩阵由1和0组成,问是否能通过交换行或列实现对角线上数字为1。
题解:这个题可以把每个x和y看成两组数,若a[x][y]=1,则在x和y之间建立一条边,然后再用匈牙利算法为每个x配对一个y,如果无法配上,则这个矩阵无法转换成要求的样子,则输出-1,否者就可以实现,只需要把每个匹配上的(x,y),把x行和y行进行交换即可,最后的结果一定是一一对应。

#include<iostream>
#include<algorithm>
#include<string.h>
#include<stack>
#include<set>

using namespace std;
int inf=0x3f3f3f3f;
const int maxn=110;
int n;
int match[maxn];
int vis[maxn];
int x[maxn],y[maxn];
int mp[maxn][maxn];

void init(){
	memset(match,0,sizeof(match));
}

bool getit(int now){
	for(int i=1;i<=n;i++){
		if(vis[i]||mp[now][i]==0) continue;
		vis[i]=1;
		if(match[i]==0||getit(match[i])){
			match[i]=now;
			return 1;
		}
	}
	return 0;
}

void getans(){
	int ans=0;
	for(int i=1;i<=n;i++){
		memset(vis,0,sizeof(vis));
		if(getit(i)){
			ans++;
		}
	}
	if(ans<n) cout<<-1<<endl;
	else{
		int k=0;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(match[j]==i){
					if(i!=j){
						x[k]=i;
						y[k++]=j;
						swap(match[i],match[j]);
						break;
					}
				}
			}
		}
		cout<<k<<endl;
		for(int i=0;i<k;i++) cout<<"C "<<x[i]<<" "<<y[i]<<endl;
	}
}

int main(){
	while(cin>>n){
		init();
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				cin>>mp[i][j];
			}
		}
		getans();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值