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,不上代码了。
第六题:
第七题:
以后再补?说着玩的,不补了。