CCPC 2019 哈尔滨 E(建图+拓扑dp计数+计算贡献 I(思维

35 篇文章 1 订阅
32 篇文章 0 订阅

E. Exchanging Gifts
题意:
给定 n n n 个序列,每行一个,有两种给出方式
1 1 1 k k k a 1 , a 2 . . . a k a_1,a_2...a_k a1,a2...ak ( a i < = n ) (a_i<=n) (ai<=n) 表示第 i i i 个序列有 k k k 个数
2 2 2 x x x y y y ( x , y < = i − 1 ) (x,y<=i-1) (x,y<=i1) 表示第 i i i 个序列由第 x x x 个与 y y y 个按照 x , y x,y x,y 的顺序拼接而成
求第 n n n 个序列的最大满意度。
一个序列的满意度被定义为:一个序列重新排序前某个位置为 a i a_i ai,排序后为 a j a_j aj,如果 a i ! = a j a_i!=a_j ai!=aj,那么这个位置是满意位置,满意度为满意位置的个数
思路:
显然最大满意度讨论出现个数最多的一个数即可
s u m sum sum 为序列长度, M a x Max Max 为出现最多一个数的次数
如果 M a x ∗ 2 < = s u m Max*2<=sum Max2<=sum,则答案为 s u m sum sum
否则,会有 M a x ∗ 2 − s u m Max*2-sum Max2sum 个位置不满意,因此答案为 s u m − ( M a x ∗ 2 − s u m ) sum - (Max*2-sum) sum(Max2sum)
主要是求第 n n n 个序列的长度以及出现最多的数的次数
开始考虑建图 D F S DFS DFS
然后这样写,就喜提 T L E TLE TLE
显然许多节点可能被重复遍历多次
这时记忆化搜索是没法写的,因为需要更新数据累计贡献
T L E TLE TLEcode:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 2e5 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
vector <int> v[maxn];
int cnt[maxn];
ll sum = 0;
vector <int> e[maxn];
void dfs(int x){
	if(v[x].size()){
		sum += v[x].size();
		cnt[x] += 1;
		return;
	}
	for(auto y : e[x]){
		dfs(y);
	}
}
void work()
{
	cin >> n;
	for(int i = 1; i <= n; ++i) v[i].clear(), cnt[i] = 0, e[i].clear();
	sum = 0;
	unordered_map <int, ll> ma;
	for(int i = 1, op, k; i <= n; ++i){
		cin >> op >> k;
		if(op == 1){
			for(int j = 1, x; j <= k; ++j){
				cin >> x;
				v[i].push_back(x);
			}	
		}
		else{
			cin >> m;
			e[i].push_back(k);
			e[i].push_back(m);
		}
	}
	dfs(n);
	for(int i = 1; i <= n; ++i) if(v[i].size())
	{
		for(auto x : v[i]) ma[x] += cnt[i];
	}
	ll Max = 0;
	for(auto x : ma) Max = max(Max, x.second);
//	cout << sum << " " << Max << endl;
	if(Max * 2 <= sum) cout << sum << endl;
	else
	{
		cout << sum - (Max * 2 - sum) << endl;
	}
}

int main()
{
	ios::sync_with_stdio(0);
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

题解
题解给出的思路就很妙
初始设置 n u m [ n ] = 1 num[n]=1 num[n]=1,然后进行拓扑排序就可以类似 d p dp dp 的计数转移到叶子节点,统计出 n n n 到某个叶子节点有多少条路,就是这个叶子节点最后的贡献次数
但是这里要注意,拓扑排序用的 d e g deg deg 并不是整个图中的
比如 1 , 2 1,2 1,2 这两个序列组成了序列 3 3 3,会从 3 3 3 1 , 2 1,2 1,2 连边。 1 , 2 1,2 1,2 又组成了序列 4 4 4,会从 4 4 4 1 , 2 1,2 1,2 连边, 4 4 4 是最终序列。那么按照倒着拓扑的思想,会从 4 4 4 出发,然后将 1 , 2 1,2 1,2 的入度 − − -- ,但是我们会发现,此时 1 , 2 1,2 1,2 的入度并不为 0 0 0,所以不会被加入到队列中。因此我们需要先 D F S DFS DFS 一遍计算 d e g deg deg
这里给出一组数据

1
4
1 1 1
1 1 2
2 1 2
2 1 2

这道题用快读才能过,卡 I / O I/O I/O 太阴间了
A C AC ACcode:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
using namespace std;
const int maxn = 1e6 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
// 快读板子
#define tpyeinput int
inline char nc() {static char buf[1000000],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;}
inline void read(tpyeinput &sum) {char ch=nc();sum=0;while(!(ch>='0'&&ch<='9')) ch=nc();while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+(ch-48),ch=nc();}

int n, m;

vector <int> v[maxn], e[maxn];
queue <int> q;
int deg[maxn];
ll num[maxn];
ll sum = 0;
bool vis[maxn];
unordered_map <int, ll> ma;

void init(){
	while(!q.empty()) q.pop();
	for(int i = 1; i <= n; ++i) 
		v[i].clear(), e[i].clear(), vis[i] = deg[i] = num[i] = 0;
	ma.clear();
}
void dfs(int x){
	for(auto to : e[x])// 注意这里该如何统计 deg
	{// 一个点可能被多个点指向,因此 deg 计数要在递归外边进行,放在里边只会计一次,显然不对
		deg[to]++;
		if(!vis[to]){
			vis[to] = 1;
			dfs(to);	
		}
	}
}
void toposort(){
	q.push(n);
	while(!q.empty())
	{
		int x = q.front(); q.pop();
		for(auto to : e[x]){
			deg[to]--;
			num[to] += num[x];
			if(deg[to] == 0) q.push(to);
		}
	}
}
void work()
{
	read(n);
	init();
	for(int i = 1, op, k; i <= n; ++i){
		read(op); read(k);
		if(op == 1){
			for(int j = 1, x; j <= k; ++j){
				read(x);
				v[i].push_back(x);
			}	
		}
		else{
			read(m);
			e[i].push_back(k);
			e[i].push_back(m);
		}
	}
	dfs(n);
	num[n] = 1;
	toposort();
	for(int i = 1; i <= n; ++i) if(v[i].size())
	{
		for(auto x : v[i]) ma[x] += num[i];
	}
	ll Max = 0, sum = 0;
	for(auto x : ma) Max = max(Max, x.second), sum += x.second;
	if(Max * 2 < sum) printf("%lld\n", sum);
	else printf("%lld\n", sum - (Max * 2 - sum));
}

int main()
{
//	ios::sync_with_stdio(0);
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

I. Interesting Permutation
题意:
传送门读题吧
思路:
显然可以判掉一些答案为 0 0 0 的情况
剩下的看题解
code:

#include<bits/stdc++.h>
#define endl '\n'
#define ll long long
#define ull unsigned long long
#define ld long double
#define all(x) x.begin(), x.end()
#define eps 1e-6
using namespace std;
const int maxn = 2e5 + 9;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
ll n, m;
int h[maxn];
int f[maxn];

void work()
{
	cin >> n;
	for(int i = 1; i <= n; ++i) cin >> h[i];
	for(int i = 2; i <= n; ++i) if(h[i-1] > h[i] || h[i] >= n || h[i-1] >= n){
		cout << 0 << endl;return;
	}
	if(h[1]){
		cout << 0 << endl;return;
	}
	ll ans = 1, cnt = 0;
	for(int i = 2; i <= n; ++i){
		if(h[i] == h[i-1]){
			ans = ans * cnt % mod;
			--cnt;
		}
		else if(h[i] > h[i-1]){
			cnt = (cnt + h[i] - h[i-1] - 1 + mod) % mod;
			ans = ans * 2 % mod;
		}
	}
	cout << ans << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	int TT;cin>>TT;while(TT--)
	work();
	return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值