Codeforces Round #366 题解

4 篇文章 0 订阅

A题Hulk

题目大意思:

模拟题。

1的话输出 i hate it

2次的话,就输出 i hat that i love it

3次就输出 i hate that i love that i hate it


然后模拟一下。仔细读题仔细仔细的读题…… 我先漏了that,又读题漏了it……


#include <iostream>
#include <ctime>
#include <cstdio>
#include <queue>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
#include <string>
using namespace std;


typedef long long LL;
const int maxn = 10e5 *4 + 10;
const int mod = 1e9+7;


LL powMod( LL a , LL b , LL p = mod  )//a^b % p
{
    LL r = 1 ;
    a %= p ;
    while( b )
    {
        if( b&1 ) r = r*a%p ;
        b >>= 1 ;
        a = a*a%p ;
    }
    return r ;
}

string a="I hate";
string b="I love";
string that="that";
string it = "it";


int main()
{
	int n;
	cin >> n;
	for (int i = 1; i < n ;++ i)
		if (i%2==1)	cout<<a<<" "<<that<<" ";
		else cout<<b<<" "<<that<<" ";
	if (n%2==1)	cout<<a<<" ";
	else cout <<b<<" ";
	cout<<it<<endl;

	return 0;
}


B题Spider Man:蜘蛛侠……

题目大意:

手机有N个应用,每个应用会收到消息。

有3种操作。

第一个: A应用收到一条信息

第二个:   打开A应用,查看所有A应用消息

第三个:查看消息队列里,最早的T的应用消息(会重复查看到,曾经看过的消息)


问:每次操作后,有多少个未读消息。


方法:大模拟,用map映射一下就行了nlogn

方法2:O(n)的算法,还是大模拟……只不过记录一些信息即可,不难想。


方法1:

#include <iostream>
#include <ctime>
#include <cstdio>
#include <queue>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
#include <string>
using namespace std;


typedef long long LL;
const int maxn = 10e5 *4 + 10;
const int mod = 1e9+7;


LL powMod( LL a , LL b , LL p = mod  )//a^b % p
{
    LL r = 1 ;
    a %= p ;
    while( b )
    {
        if( b&1 ) r = r*a%p ;
        b >>= 1 ;
        a = a*a%p ;
    }
    return r ;
}

string a="I hate";
string b="I love";
string that="that";
string it = "it";


int main()
{
	int n;
	cin >> n;
	for (int i = 1; i < n ;++ i)
		if (i%2==1)	cout<<a<<" "<<that<<" ";
		else cout<<b<<" "<<that<<" ";
	if (n%2==1)	cout<<a<<" ";
	else cout <<b<<" ";
	cout<<it<<endl;

	return 0;
}

方法2:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <ctime>
using std::sort;
using std::max;
using std::min;
using std::cout;
using std::cin;
using std::endl;
using std::swap;
using std::pair;
using std::vector;
using std::set;
using std::multiset;
using std::queue;
using std::map;
#define x first
#define y second
typedef unsigned long long uLL;
typedef long long LL;
typedef pair<int, int> pii;
const LL LINF = 1ll << 60;
const int INF = 0x3f3f3f3f;
const double eps = 1e-8;

const int maxn = 300000+ 10;

int A[maxn], cur;
pii num[maxn];
int main() 
{
	double t11 = clock();
	int n, q;
	scanf("%d%d", &n, &q);
	for (int i = 0; i <= n; i++) num[i].y = -1;
	cur = 0;
	int op, t;
	int idx = 0;
	int ans = 0;
	while (q--)
	{
		scanf("%d%d", &op, &t);
		if (op == 1) {
			ans++;
			A[idx++] = t;
			num[t].x++;
		}
		else if (op == 2) {
			ans -= num[t].x;
			num[t].x = 0;
			num[t].y = idx - 1;
		}
		else {
			for (int i = cur; i < t; i++) {
				if (A[i] && num[A[i]].y < i) {
					ans--;
					num[A[i]].x--;
					A[i] = 0;
				}
			}
			cur = max(cur,t);
		}
		printf("%d\n", ans);
	}
	return 0;
}

第三题 Thor:哎呀,和第二题写反了……算了

这道题就是取石子游戏,如果用SG函数来做,又大材小用了~

谁赢,只要看一堆石子是奇数还是偶数就行了。因为显然,一堆数字可以分多少次,很容易意淫出来。

比如2个石子,分为1+1

3个石子,分为1+2,再分为1+1+1

然后只要考虑一开始给的数字的奇偶就知道答案了。


#include <iostream>
#include <ctime>
#include <cstdio>
#include <queue>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
#include <string>
using namespace std;


typedef long long LL;
const int maxn = 300000 + 100;
const int mod = 1e9+7;

map<int, int>g[maxn];
map<int, int>h;

int n, m;
int tot;//总信息数
int yijing;//已经读的

void init()
{
	scanf("%d%d", &n, &m);
}


void one(int x, int idx)//idx时间,x收到一个信息
{
	++tot;
	g[x][idx] = idx;
	h[idx] = x;
}

void two(int x)//读光X的信
{
	yijing += g[x].size();
	for (auto i = g[x].begin(); i != g[x].end(); ++ i)
	{
		h.erase(i -> second);
	}
	g[x].clear();
}

int tmp[maxn];

void three(int x)//读最新的X个信,当前有now个。 也就是读到now-x+1个
{
	int tail = 0;
	for (auto i = h.begin(); i != h.end(); ++ i)
	{
		if (i -> first > x)	break;
		g[i -> second].erase(i->first);
		tmp[++tail] = i -> first;
	}
	yijing += tail;
	for (int i = 1; i <= tail; ++ i)	h.erase(tmp[i]);
}


void doit()
{
	for (int i = 1; i <= m; ++ i)
	{
		int flag, x;
		scanf("%d%d", &flag, &x);
		if (flag==1)
		{
			one(x, tot+1);
			//printf("%d\n", tot - yijing);
			printf("%d\n", h.size());
		}
		if (flag == 2)
		{
			two(x);
			//printf("%d\n", tot - yijing);
			printf("%d\n", h.size());
		}
		if (flag == 3)
		{
			three(x);
			//printf("%d\n", tot - yijing);
			printf("%d\n", h.size());
		}
	}
}

int main()
{
	init();
	doit();
	return 0;
}


第四题Ant Man

这道题,比赛现场没10个人做出来……贪心是错的(某菊苣告诉我有反例)

好吧~但是我还是感觉贪心说不定是对的~~~


抽象模型后题目大意思:

有n个节点,每个节点往左边出去,要花费left_out的代价,往右边离开这个节点,需要right_out的代价。 从左边进入这个节点,需要left_in的代价,从右边进入这个节点,需要right_in的代价。

求,从S出发,到E,遍历所有点的最小代价。

DP。


按照官方题解的意思:

可以把最左边的k个点,看为一个整体。 如果把这k个点拆分为很多个点集(就是用某个方法连成一条链,但是不是圈!),每个集合一定符合如下特征

1、未来的点,可以进这个集合,也可以从这个集合出到未来的点上

2、未来的点,只能进这个集合,这个集合不出去。(包含了起点)

3、这个集合,只能出去,不能进来。(包含了终点)

然后就可以DP了

f[i][j][k][l]表示,前i个点,有j个1类链,有k个2类链,有l个3类链。 转移也还算简单,如果已经求得f[i]的值,可以把i+1的点的所有出入都穷举一遍讨论一下,就可以了。

然后对于f[n]的时候,要考虑合并k,l的两条链。最终答案是f[n][0][0][0]。


还有一个思路:

f[i][j]表示,前i个点,拆分成若干个链,入度+出度一共为j的最短方案(有起点的链,入度为0,有终点的链,出度为0)我会贴出Jirachi菊苣的代码……侵删。


首先是我的:

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

typedef long long LL;
const long long inf = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 5101;

LL n, s, e, x[MAXN], a[MAXN], b[MAXN], c[MAXN], d[MAXN];
LL f[2][MAXN][2][2];


LL mymin(LL a, LL b)
{
	if (a<b)	return a;
	return b;
}

void init()
{
	scanf("%I64d%I64d%I64d", &n, &s, &e);
	for (int i = 1; i <= n; i++) scanf("%I64d", x + i);
	for (int i = 1; i <= n; i++) scanf("%I64d", a + i);
	for (int i = 1; i <= n; i++) scanf("%I64d", b + i);
	for (int i = 1; i <= n; i++) scanf("%I64d", c + i);
	for (int i = 1; i <= n; i++) scanf("%I64d", d + i);
}

void doit()
{
	memset(f, 0x3f, sizeof(f));
	if (s==1)	f[1][0][1][0] = -x[1] + d[1];
	else if (e==1)	f[1][0][0][1] = -x[1] + b[1];
	else f[1][1][0][0] = -x[1] - x[1] + b[1] + d[1];
	for (int i = 1; i < n - 1; ++ i)
	{
		int will = (i&1)^1;
		memset(f[will], 0x3f, sizeof(f[i^1]));
		for (int j = 0; j <= i; ++ j)
			for (int k = 0; k <= 1; ++ k)//s
				for (int l = 0; l <= 1; ++ l)//e
				{
					LL v = f[i & 1][j][k][l];
					if (v == inf)	continue;
					LL right_out = -x[i + 1] + d[i + 1];
					LL right_in = -x[i + 1] + b[i + 1];
					LL left_in = x[i + 1] + a[i + 1];
					LL left_out = x[i + 1] + c[i + 1];
					if (i + 1 == s)
					{
						f[will][j][1][l] = mymin(v + right_out , f[will][j][1][l]);
						if (j)	f[will][j - 1][1][l] = mymin(v + left_out , f[will][j - 1][1][l]);
						//if (l)	f[i + 1][j][0][0] = mymin(f[i + 1][j][0][0], v + left_out);
					}else if (i + 1 == e)
					{
						f[will][j][k][1] = mymin(f[will][j][k][1], v + right_in);//right in
						if (j)	f[will][j - 1][k][1] = mymin(f[will][j - 1][k][1], v + left_in);
					}else
					{

						if (j)	f[will][j][k][l] = mymin(f[will][j][k][l], v + left_in + right_out);
						if (j>1)  f[will][j-1][k][l] = mymin(f[will][j-1][k][l], v + left_in + left_out);
						if (j)	f[will][j][k][l] = mymin(f[will][j][k][l], v + right_in + left_out);
						f[will][j + 1][k][l] = mymin(f[will][j + 1][k][l], v + right_in + right_out);
						if (k) f[will][j][k][l] = mymin(f[will][j][k][l], v + left_in + right_out);
						if (k && j) f[will][j - 1][k][l] = mymin(f[will][j - 1][k][l], v + left_in + left_out);
						if (l) f[will][j][k][l] = mymin(f[will][j][k][l], v + left_out + right_in);
						if (l && j) f[will][j - 1][k][l] = mymin(f[will][j - 1][k][l], v + left_in + left_out);
					}
				}
	}
	int i = (n-1);
	int will = n & 1;
	memset(f[will], 0x3f, sizeof(f[will]));
	LL right_out = -x[i + 1] + d[i + 1];
	LL right_in = -x[i + 1] + b[i + 1];
	LL left_in = x[i + 1] + a[i + 1];
	LL left_out = x[i + 1] + c[i + 1];
	if (s==n)	f[will][0][0][0] = mymin(f[will][0][0][0], f[i & 1][0][0][1] + left_out);
	else if (e==n)	f[will][0][0][0] = mymin(f[will][0][0][0], f[i & 1][0][1][0] + left_in);
	else 	f[will][0][0][0] = mymin(f[will][0][0][0], f[i & 1][0][1][1] + left_in + left_out);
	cout<< f[will][0][0][0]<<endl;
}

int main() 
{
	init();
	doit();
	return 0;
}


然后是Jirachi菊苣的:

#include <cstdio>
#include <iostream>
#include <algorithm>

const long long INF = 1ll << 50;
const int MAXN = 5101;

int n, s, e, x[MAXN], a[MAXN], b[MAXN], c[MAXN], d[MAXN];
long long dp[MAXN][MAXN];

void update(int i, int j, const long long &y) {
	if (j < 0) return;
	if (j == 0) {
		if (i != 0 && i != n) return;
	}
	dp[i][j] = std::min(dp[i][j], y);
}

int main() {
	scanf("%d%d%d", &n, &s, &e);
	for (int i = 1; i <= n; i++) scanf("%d", x + i);
	for (int i = 1; i <= n; i++) scanf("%d", a + i);
	for (int i = 1; i <= n; i++) scanf("%d", b + i);
	for (int i = 1; i <= n; i++) scanf("%d", c + i);
	for (int i = 1; i <= n; i++) scanf("%d", d + i);
	for (int i = 0; i <= n; i++)
		for (int j = 0; j <= 2 * n && j < MAXN; j++)
			dp[i][j] = INF;
	dp[0][0] = 0;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j <= 2 * i && j < MAXN; j++) {
			if (dp[i][j] == INF) continue;
			if (j == 0 && (i != 0 && i + 1 != n)) continue;
			if (i + 1 == s) {
				update(i + 1, j - 1, dp[i][j] + c[i + 1] + 1ll * j * (x[i + 1] - x[i]));
				update(i + 1, j + 1, dp[i][j] + d[i + 1] + 1ll * j * (x[i + 1] - x[i]));
			} else if (i + 1 == e) {
				update(i + 1, j - 1, dp[i][j] + a[i + 1] + 1ll * j * (x[i + 1] - x[i]));
				update(i + 1, j + 1, dp[i][j] + b[i + 1] + 1ll * j * (x[i + 1] - x[i]));
			} else {
				update(i + 1, j - 2, dp[i][j] + a[i + 1] + c[i + 1] + 1ll * j * (x[i + 1] - x[i]));
				if (!(j == 1 && e < s)) {
					update(i + 1, j, dp[i][j] + a[i + 1] + d[i + 1] + 1ll * j * (x[i + 1] - x[i]));
				}
				update(i + 1, j + 2, dp[i][j] + b[i + 1] + d[i + 1] + 1ll * j * (x[i + 1] - x[i]));
				if (!(j == 1 && s < e)) {
					update(i + 1, j, dp[i][j] + b[i + 1] + c[i + 1] + 1ll * j * (x[i + 1] - x[i]));
				}
			}
		}
	}
	std::cout << dp[n][0] << std::endl;
	return 0;
}

第五题Black Widow

题目大意,给一个式子形如:(括号里最多2个元素)


或者



每个变量最多只会出现2次。(有可能有变量出现一次)

也要考虑一个括号里,2个元素是同一个变量的情况……


求所有变量的取值方案总数。


写了一晚上WA了,不想调程序了。DP思路很好,程序细节非常多……超出了我的代码能力。但是思维值得借鉴

令:集合,为一个括号里的元素……


如果2个集合有交集,连一条边。

最后的图,一定是若干个链,和若干个环,还有若干个点。

没有联系的图,彼此之间取值不受影响。

我们需要知道,每个图异或结果为0和1的方案总数分别为多少,最后利用这些方案DP一下。

至于求每个图的0,1方案总数分别是多少,也是DP。

对于链:

直接DP下去,记录一些信息即可。(读到这个题的题解的菊苣,一定比我强……就不需要我赘述了)

对于单个点的情况,瞬间考虑完。

对于圈,记录前驱节点,然后对于出发的点的取值记录下来,在DP的时候加一维即可。在DP在最后一个点的时候,根据记录的信息返回值。这个细节略多,我写WA了不想改了……

然后把这几个部分的结果合起来再写个简单的01背包一样的DP,就可以求出方案总数了。


因为没AC,不上代码了。


第六题:

第七题:

以后再补?说着玩的,不补了。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值