ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

AtCoder Beginner Contest 190 题解

2021-01-31 09:33:22  阅读:163  来源: 互联网

标签:AtCoder forn int 题解 ll 190 long 石头 scanf


闲话

感觉这场最难的就是D,到现在都不太理解

A - Very Very Primitive Game

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	


int main() 
{
	int a,b,c;cin >> a >> b >> c;
	while(1)
	{
		if(c == 0)
		{
			if(a == 0)	return cout << "Aoki",0;
			--a;
		}
		else
		{
			if(b == 0)	return cout << "Takahashi",0;
			--b;
		}
		c ^= 1;
	}
	return 0;
}

B - Magic 3

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	


int main() 
{
	ios::sync_with_stdio(0);cin.tie(0);
	int n,s,d;cin >> n >> s >> d;
	forn(i,1,n)
	{
		int x,y;cin >> x >> y;
		if(x < s && y > d)	return cout << "Yes",0;
	}
	cout << "No";
	return 0;
}

C - Bowls and Dishes

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
typedef pair<int,int> pii;
#define x first
#define y second

const int N = 105;
pii need[N],p[N];
bool st[N];

int main() 
{
	int n,m;scanf("%d%d",&n,&m);
	forn(i,1,m)	scanf("%d%d",&need[i].x,&need[i].y);
	int k;scanf("%d",&k);
	forn(i,1,k)	scanf("%d%d",&p[i].x,&p[i].y);
	
	int res = 0;
	forn(S,0,(1 << k) - 1)
	{
		forn(i,1,n)	st[i] = 0;
		forn(j,0,k - 1)
		{
			if(S >> j & 1)	st[p[j + 1].x] = 1;
			else 			st[p[j + 1].y] = 1;
		}		
		int tmp = 0;
		forn(i,1,m)	if(st[need[i].x] && st[need[i].y])	++tmp;
		res = max(res,tmp);
	}
	printf("%d",res);
	return 0;
}

D - Staircase Sequences

题目大意:给定一个\(n\)求公差为\(1\)的等差数列且和为\(n\)的方案数.

数据范围:\(1 \leq N \leq 10^{12}\)

思路

设这个等差数列首项为\(x\)末项为\(y\)则有\((x+y)*(y - x + 1) = 2 * N\).那么根据复杂度反推,可能是个根号算法,可以分解\(2*N\)的约数\(d\),那么枚举\(d\)的同时\(N/d\)也是一个合法的约数,此时有两种情况:要么\(d=(x+y)\)此时可以反推出一个条件,要么\(d=(y-x+1)\)此时也可以反推出一个条件,根据条件判断当前情况是否合法,如果合法就累加.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	


int main() 
{
	ll N;cin >> N;
	ll n = 2 * N;
	ll res = 0;
	for(ll i = 1;i <= n / i;++i)
	{
		if(n % i == 0)
		{
			// cout << i << " " << n / i << endl;
			ll d = i;
			if((d * d + d - n) % (2 * d) == 0)	++res;
			if((n - d * d + d) % (2 * d) == 0)	++res;
		}
	}
	cout << res << endl;
	return 0;
}

E - Magical Ornament

题目大意:有\(n\)个石头,编号从\(1\)开始,有\(m\)对关系表示一对石头可以摆放在相邻的位置,如果两个石头没有这样的关系则不可以放在相邻的位置.现在要做一个牛逼的法阵,这个法阵是一排连续的石头,要求相邻的石头满足关系,并且整个序列需要包含指定的\(k\)个牛逼石头,其编号由\(c_1,c_2...c_k\)指定.求一个合法的牛逼法阵最少需要多少个石头.

数据范围:

\(1 \leq n,m \leq 10^5\)

\(1 \leq k \leq 17\)

思路

思路肯定从特别小的\(k\)入手,因为要包含指定的\(k\)个牛逼石头,所以很自然的可以想到拿状压记录每个牛逼石头的选取状态.可以初步地把状态转移方程搞出来:

  • 状态:\(f[S][i]\)表示当前选取的石头集合是\(S\),最后一个选取的牛逼石头是\(i\).
  • 入口:\(f[1 << i][i] = 1\)只选取一个石头自己的时候
  • 转移:枚举当前\(S\)中存在的一个石头\(j\)并且枚举将要加入这个集合的下一个石头\(i\),那么首先需要满足这两个变量与集合的关系,其次转移就是\(f[S | (1 <<i)][i] = min(f[S | (1 << i)][i],f[S][j] + dist(j,C[i]))\).因为这里有个问题:这个石头\(j\)他不一定是和下一个\(i\)可以直接相邻摆放的,但是并不意味着就不能放入\(i\)了,可以在\(j\)之后放入一些石头并且最终联通向\(i\),所以这里的\(dist(j,i)\)就表示从一个牛逼石头\(j\)连向任意一个石头\(i\)的距离(当然转移方程里面是指定的牛逼石头\(C_i\)才对).
  • 出口:\(\min\limits_{i=1}^n{f[S][i]}\).
  • 注意实际写代码的时候有所有编号调整到从\(0\)开始的操作.

现在的问题就是剩下一个\(dist\)的处理了,建图比较简单,如果两个石头之间存在可相邻关系那么就连一条权值为\(1\)的无向边.同时也不需要求任意两点的距离,牛逼石头最多\(17\)个,所以预处理出来每个牛逼石头的单源最短路就可以了.因为权值都是\(1\)所以跑个简单的BFS就可以了.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	

const int N = 1e5+7,M = 2 * N,CK = 20,INF = 0x3f3f3f3f;
int dist[CK][N],c[CK];
int edge[M],succ[M],ver[N],idx;
int f[1 << 17][CK];
void add(int u,int v)
{
	edge[idx] = v;
	succ[idx] = ver[u];
	ver[u] = idx++;
}

void bfs(int f)
{
	memset(dist[f],0x3f,sizeof dist[f]);
	queue<int> q;q.push(c[f]);dist[f][c[f]] = 0;
	while(!q.empty())
	{
		int u = q.front();q.pop();
		for(int i = ver[u];~i;i = succ[i])
		{
			int v = edge[i];
			if(dist[f][v] > dist[f][u] + 1)
			{
				dist[f][v] = dist[f][u] + 1;
				q.push(v);
			}
		}
	}
}

int main() 
{
	memset(ver,-1,sizeof ver);
	int n,m;scanf("%d%d",&n,&m);
	forn(i,1,m)
	{
		int u,v;scanf("%d%d",&u,&v);
		--u;--v;
		add(u,v),add(v,u);
	}
	
	int k;scanf("%d",&k);
	forn(i,0,k - 1)	scanf("%d",&c[i]),--c[i];
	
	forn(i,0,k - 1)	bfs(i);
	
	memset(f,0x3f,sizeof f);
	forn(i,0,k - 1)	f[1 << i][i] = 1;
	
	forn(S,1,(1 << k) - 1)
	{
		forn(j,0,k - 1)	forn(i,0,k - 1)
		{
			if(f[S][j] == INF)	continue;
			if(!(S >> j & 1) || dist[j][c[i]] == INF || (S >> i & 1))	continue;
			f[S | (1 << i)][i] = min(f[S | (1 << i)][i],f[S][j] + dist[j][c[i]]);
		}
	}
	int res = INF;
	forn(i,0,k - 1)	res = min(res,f[(1 << k) - 1][i]);
	printf("%d",res == INF ? -1 : res);
	return 0;
}

F - Shift and Inversions

思路

因为每次修改都是把第一个元素放到最后一个元素,所以直接树状数组维护一下值域查询就可以了.

逆序对是\(n^2\)级别的,需要开ll.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	

const int N = 3e5+7;
int c[N],a[N];
int n;

inline int lowbit(int x)
{
	return x & -x;
}

void modify(int x,int v)
{
	for(int i = x;i < N;i += lowbit(i))
		c[i] += v;
}

int query(int x)
{
	int res = 0;
	for(int i = x;i;i -= lowbit(i))
		res += c[i];
	return res;
}

int main() 
{
	scanf("%d",&n);
	ll res = 0;
	forn(i,1,n)
	{
		scanf("%d",&a[i]);
		a[i]++;
		res += query(N - 1) - query(a[i]);
		modify(a[i],1);
	}
	printf("%lld\n",res);
	forn(k,1,n - 1)
	{
		res -= query(a[k] - 1);
		res += query(N - 1) - query(a[k]);
		printf("%lld\n",res);
	}
	return 0;
}

标签:AtCoder,forn,int,题解,ll,190,long,石头,scanf
来源: https://www.cnblogs.com/HotPants/p/14351595.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有