2019.11.7解题报告
T1:同花顺
题目描述
所谓同花顺,就是指一些扑克牌,它们花色相同,并且数字连续。
现在我手里有 \(n\)张扑克牌,但它们可能并不能凑成同花顺。我现在想知道,最
少更换其中的多少张牌,我能让这 \(n\) 张牌凑成一个同花顺?
输入格式
第一行一个整数\(n\),表示扑克牌的张数。
接下来 \(n\) 行,每行两个整数 \(a_i\) 和 \(b_i\) 。其中 \(a_i\) 表示第 \(i\) 张牌的花色,\(b_i\) 表示第
\(i\) 张牌的数字。
(注意: 这里的牌上的数字不像真实的扑克牌一样是 \(1\) 到 \(13\), 具体见数据范围)
输出格式
一行一个整数,表示最少更换多少张牌可以达到目标。
样例输入 1
5
1 1
1 2
1 3
1 4
1 5
样例输出 1
0
样例输入 2
5
1 9
1 10
2 11
2 12
2 13
样例输出 2
2
数据范围
对于 \(30\%\) 的数据,\(n ≤ 10\)。
对于 \(60\%\) 的数据,\(n ≤ 10^5\) ,\(1 ≤ a_i ≤ 10^5\) ,\(1 ≤ b_i ≤ n\)。
对于 \(100\%\)的数据,\(n ≤ 10^5 ,1 ≤ a_i ,b_i ≤ 10\)
Solution:
去重+离散化
贪心的选择花色最多的顺子,然后枚举每个数字作为左端点在这个花色里能组成长度为\(n\)的顺子需要修改或者添加几张牌,记录答案即可。
注意:
\(1\).\(vector\)的去重方法:
先排序再使用\(unique\)函数
\(unique\)函数将重复的元素放到\(vector\)的尾部,然后返回指向第一个重复元素的迭代器,再用\(erase\)函数将从这个元素到最后的所有元素清除
Code:
#include <vector>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5+7;
int n, ans, a[N], b[N], r;
vector <int> der, num[N];
int read() {
int s = 0, w = 1; char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') w = -1; ch = getchar();}
while(isdigit(ch)) {s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
int main() {
n = read();
for(int i = 1; i <= n; i++) a[i] = read(), b[i] = read(), der.push_back(a[i]);
sort(der.begin(), der.end());
der.erase(unique(der.begin(), der.end()), der.end());
for(int i = 1; i <= n; i++) a[i] = lower_bound(der.begin(), der.end(), a[i]) - der.begin() + 1;
for(int i = 1; i <= n; i++) num[a[i]].push_back(b[i]);
for(int i = 1; i <= der.size(); i++) {
sort(num[i].begin(), num[i].end());
num[i].erase(unique(num[i].begin(), num[i].end()), num[i].end());
for(int j = 0; j < num[i].size(); j++)
r = lower_bound(num[i].begin(), num[i].end(), num[i][j] + n - 1) - num[i].begin() - 1, ans = max(ans, r - j + 1);
}
cout << n - ans << endl;
return 0;
}
话说\(STL\)真的挺好用的感觉
T2:做实验
题目描述
有一天,你实验室的老板给你布置的这样一个实验。
首先他拿出了两个长度为\(n\) 的数列 \(a\)和 \(b\),其中每个 \(a_i\) 以二进制表示一个集
合。例如数字 \(5 = (101)_2\) 表示集合 \({1,3}\)。第 \(i\) 次实验会准备一个小盒子,里面装
着集合 \(a_i\) 所有非空子集的纸条。老板要求你从中摸出一张纸条,如果满足你摸出的
纸条是 \(a_i\) 的子集而不是 $a_{i−b_i} \(,\)a_{i−b_i +1}$ ,...,\(a_{i−1}\) 任意一个的子集,那么你就要 ***;
反之,你就逃过一劫。
令你和老板都没有想到的是,你竟然每次都逃过一劫。在庆幸之余,为了知道
这件事发生的概率,你想要算出每次实验有多少纸条能使你 ***
输入格式
第一行一个数字 \(n\)。
接下来 \(n\) 行,每行两个整数,分别表示 \(a_i\) 和 \(b_i\) 。
输出格式
\(n\) 行,每行一个数字,表示第 \(i\) 次实验能使你 *** 的纸条数。
样例输入 1
3
7 0
15 1
3 1
样例输出 1
7
8
0
4
数据范围
对于 \(30\%\) 的数据,\(n,a_i ,b_i ≤ 100\)
对于 \(70\%\) 的数据,\(n,a_i ,b_i ≤ 60000\)
对于 \(100\%\) 的数据,\(n,a_i ,b_i ≤ 10^5\)
保证所有的 \(a_i\) 不重复,\(b_i < i\)
Solution:
枚举每一个\(a[i]\)的子集
设\(f[s]\)为\(s\)这个子集最后一次出现在哪个\(a[i]\)当中
从\(a[1]\)到\(a[n]\)依次处理。对\(a[i]\),枚举其所有的子集\(s\),若\(f[s]>=i-b[i]\),则答案\(+1\)。同时更新\(f[s]\)
Code:
真的懒了,第一题就看了很久太浪费时间了
\(so!std:\)
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int n,m,f[N];
int scan(){int i=0;scanf("%d",&i);return i;}
void print(int x,int t=7){
if(!t)return;
print(x/2,t-1);
printf("%d",x&1);
}
int main(){
int i,j,k,a,b;
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
n = scan();
int t,ans;
for(i=1;i<=n;i++){
a = scan();b = scan();
t = a;
ans =0;
do{
if(f[t] < i-b)ans++;
f[t] = i;
//print(t);printf("\n");
t = a&(t-1);
}while(t);
printf("%d\n",ans);
}
return 0;
}
T3:拯救世界:
题目描述
\(C\) 城所有的道路都是单向的。不同道路之间有路口,每个路口都有一个大楼。
有一天, 城市里的所有大楼因为不明原因, 突然着火了。 作为超人的你要去拯救
这些大楼。初始的时候你在 \(S\) 号楼,最后你必须到达某个有补给站的大楼,你可以
沿着单向道路行驶。你可以经过某条道路或者某个大楼若干次,经过一个大楼你就
可以消灭一个大楼的大火。每个大楼都有一个重要程度,最后这个任务的评价分数
就是你经过的所有大楼的重要度之和(若重复经过某个大楼多次,则不重复算分) 。
你是一个聪明的超人,你想知道,通过合理的规划路线,你这次任务能得到的
最高得分是多少。
注意,该城市的道路可能有重边或自环。
输入格式
第一行包括两个整数 \(n\),\(m\),\(n\) 表示路口的个数(即大楼的个数) ,\(m\) 表示道路
的条数。
接下来 \(m\) 行,每行两个整数 \(x,y\),表示 \(x\) 到 \(y\) 之间有一条单向道路。
接下来 \(n\) 行,每行一个整数,按顺序表示每个大楼的重要度。
接下来一行包含两个整数 \(S\) 和 \(P\),\(S\) 是出发的路口(大楼)的编号,\(P\) 是有补
给站的大楼的数量。
接下来一行 \(P\) 个整数,表示有补给站的大楼的编号。
输出格式
输出一行一个整数,表示你得分的最大值。
样例输入 1
6 7
1 2
2 3
3 5
6
2 4
4 1
2 6
6 5
10
12
8
16
1
5
1 4
4 3 5 6
样例输出 1
47
数据范围
对于 \(1、2、3\) 测试点,\(N,M ≤ 300\)
对于 \(4、5、6、7、8、9、10\) 测试点,\(N,M ≤ 3000\)
对于 \(11、12、13、14、15\) 测试点,\(N,M ≤ 500000\)。每个大楼的重要度均为非
负数且不超过 \(4000\)。
输入数据保证你可以从起点沿着单向道路到达其中的至少一个有补给站的大
楼。
注意,输入数据中存在树和链的特殊情况
Solution:
\(Tarjan\)缩点+拓扑排序找最长路
考场\(Code\):
//tarjan缩点+dfs
#include <cstdio>
#include <iostream>
using namespace std;
const int N = 5e5+7;
int head[N], cnt, dfn[N], low[N], tot, num, belon[N], siz[N], w[N], stac[N], top, st, p;
int Head[N], n, m;
bool vis[N], ok[N], okk[N], flag;
long long ans, sum;
struct node {int nxt, to;}e[N];
struct Node {int nxt, to;}E[N];
int read() {
int s = 0, w = 1; char ch = getchar();
while(!isdigit(ch)) {if(ch == '-') w = -1; ch = getchar();}
while(isdigit(ch)) {s = s * 10 + ch - '0'; ch = getchar();}
return s * w;
}
void add(int x, int y) {
E[++cnt].nxt = Head[x];
E[cnt].to = y;
Head[x] = cnt;
}
void tarjan(int u) {
int v;
dfn[u] = low[u] = ++tot;
stac[++top] = u, vis[u] = 1;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
else if(vis[v]) low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]) {
num++;
do{
v = stac[top--], vis[v] = 0;
belon[v] = num, siz[num] += w[v];
if(ok[v]) okk[num] = 1;
}while(v != u);
}
}
void dfs(int x) {
if(okk[x]) ans = max(ans, sum);
for(int i = Head[x]; i; i = E[i].nxt)
sum += siz[E[i].to], dfs(E[i].to), sum -= siz[E[i].to];
}
int main() {
freopen("save.in", "r", stdin); freopen("save.out", "w", stdout);
n = read(), m = read();
for(int i = 1, x, y; i <= m; i++) x = read(), y = read(), e[i].nxt = head[x], e[i].to = y, head[x] = i;
for(int i = 1; i <= n; i++) w[i] = read();
st = read(), p = read();
for(int i = 1, x; i <= p; i++) x = read(), ok[x] = 1;
for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i);
for(int i = 1; i <= n; i++)
for(int j = head[i]; j; j = e[j].nxt)
if(belon[i] != belon[e[j].to]) add(belon[i], belon[e[j].to]);
sum += siz[belon[st]], dfs(belon[st]);
cout << ans << endl;
fclose(stdin); fclose(stdout);
return 0;
}
\(std\)
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int MAXN = 500100;
int father[MAXN] = {0};
int dfsTime[MAXN] = {0}, linkTime[MAXN] = {0}, cct[MAXN] = {0}, st[MAXN] = {0};
bool statue[MAXN] = {0};
int nowTime = 0, cnt = 0, top = 0;
int point[MAXN] = {0}, nxt[MAXN] = {0}, v[MAXN] = {0}, cur[MAXN] = {0}, tot = 0;
int money[MAXN] = {0};
bool cctbar[MAXN] = {0}, bar[MAXN] = {0};
int cctmoney[MAXN] = {0}, f[MAXN] = {0};
int x[MAXN], y[MAXN], n, m, start, p;
int rudu[MAXN] = {0}, able[MAXN] = {0};
inline void memorycheck()
{
int memory = sizeof(dfsTime) + sizeof(linkTime) + sizeof(cct) + sizeof(st) + sizeof(point)
+ sizeof(nxt) + sizeof(v) + sizeof(money) + sizeof(cctbar) + sizeof(bar)
+ sizeof(cctmoney) + sizeof(f) + sizeof(x) + sizeof(y);
printf("%d\n", memory / 1024 / 1024);
}
inline void clear()
{
tot = 0;
memset(point, 0, sizeof(point));
memset(nxt, 0, sizeof(nxt));
}
inline void addedge(int x, int y)
{
tot++;
nxt[tot] = point[x];
point[x] = tot;
v[tot] = y;
}
inline void dfs(int now)
{
while (now)
{
if (!dfsTime[now])
{
dfsTime[now] = linkTime[now] = ++nowTime;
st[++top] = now;
cur[now] = point[now];
}
else
{
if (statue[now])
{
linkTime[now] = min(linkTime[now], linkTime[v[cur[now]]]);
statue[now] =false;
}
cur[now] = nxt[cur[now]];
}
if (!cur[now])
{
if (dfsTime[now] == linkTime[now])
{
cnt++;
while (1)
{
cct[st[top--]] = cnt;
if (st[top + 1] == now) break;
}
}
now = father[now];
continue;
}
if (!dfsTime[v[cur[now]]])
{
statue[now] = true;
father[v[cur[now]]] = now;
now = v[cur[now]];
continue;
}
else
if (!cct[v[cur[now]]])
linkTime[now] = min(linkTime[now], dfsTime[v[cur[now]]]);
}
}
int main()
{
freopen("save.in", "r", stdin);
freopen("save.out", "w", stdout);
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
scanf("%d%d", &x[i], &y[i]);
addedge(x[i], y[i]);
}
for (int i = 1; i <= n; i++) scanf("%d", &money[i]);
scanf("%d%d", &start, &p);
for (int i = 1; i <= p; i++)
{
int x;
scanf("%d", &x);
bar[x] = true;
}
for (int i = 1; i <= n; i++)
if (!dfsTime[i])
dfs(i);
clear();
for (int i = 1; i <= n; i++)
{
cctbar[cct[i]] |= bar[i];
cctmoney[cct[i]] += money[i];
}
for (int i = 1; i <= m; i++)
if (cct[x[i]] != cct[y[i]])
{
addedge(cct[x[i]], cct[y[i]]);
rudu[cct[y[i]]]++;
}
queue<int> q;
while (!q.empty()) q.pop();
for (int i = 1; i <= cnt; i++)
if (!rudu[i])
q.push(i);
able[cct[start]] = 1;
int ans = 0;
while (!q.empty())
{
int now = q.front(); q.pop();
f[now] += cctmoney[now] * able[now];
if (cctbar[now])
ans = max(ans, f[now]);
for (int temp = point[now]; temp; temp = nxt[temp])
{
rudu[v[temp]]--;
if (!rudu[v[temp]]) q.push(v[temp]);
f[v[temp]] = max(f[v[temp]], f[now]);
able[v[temp]] |= able[now];
}
}
printf("%d\n", ans);
}
以后不想写解题报告了太浪费时间了\(wxf\)会说我的!
谢谢收看, 祝身体健康!