集训记录 1.7

集训记录 1.7

感觉最近效率好低啊。

打了几场省选模拟赛,几乎都垫底了。

主要是图论,贪心和博弈,其他的没怎么写,感觉DP以后有必要补,字符串和网络流暂时放一放。

【模板】最大流

EK和Dinic都学了,后者写的比较熟。

Dinic
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5100;
const ll inf=1e16;
int n,m,s,t,a,b,c,depth[N];
ll ans;
bool vis[N];
struct graph{
	int cnt=1,head[N],nxt[N<<1],to[N<<1],now[N];
	ll val[N<<1];
	void add(int u,int v,int w){
		cnt++;
		to[cnt]=v;
		nxt[cnt]=head[u];
		val[cnt]=w;
		head[u]=cnt;
	}
}g;
queue<int>q;
bool bfs(){
	memset(depth,0,sizeof(int)*(n+1));
	memcpy(g.now,g.head,sizeof(int)*(n+1));
	while(!q.empty())q.pop();
	q.push(s);
	depth[s]=1;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=g.head[u];i;i=g.nxt[i]){
			if(!g.val[i])continue;
			int v=g.to[i];
			if(!depth[v]){
				depth[v]=depth[u]+1;
				q.push(v);
				if(v==t)return 1;
			}
		}
	}
	return 0;
}
ll dfs(int u,ll sum){
	if(u==t||sum==0)return sum;
	ll res=0,k=0;
	for(int i=g.now[u];i&&sum;i=g.nxt[i]){
		g.now[u]=i;
		int v=g.to[i];
		if(depth[v]==depth[u]+1){
			k=dfs(v,min(sum,g.val[i]));
			res+=k;
			sum-=k;
			g.val[i]-=k;
			g.val[i^1]+=k;
		}
	}
	return res;
}
signed main()
{
	// freopen("q.in","r",stdin);
	// freopen("q.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&s,&t);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&a,&b,&c);
		g.add(a,b,c);
		g.add(b,a,0);
	}
	while(bfs()){
		ans+=dfs(s,inf);
	}
	cout<<ans;
}

墨墨的等式

学了学转圈法求同余最短路。发现每个物品的转移都形成环,在环上跑两圈转移即可。思路过于神奇,可能说不明白,具体实现看代码。

CODE
for(int i=1;i<=n;i++){
	int d=__gcd(a[i],a[1]);
	for(int j=0;j<d;j++){
		for(int k=j,c=1;c<=2;c+=(k==j)){
			int p=(k+a[i])%a[1];
			dp[p]=min(dp[p],dp[k]+a[i]);
			k=p;
		}
	}
}

Buy One, Get One Free

CF 3000*的反悔贪心。

n 个必须买的物品,支持如下买一赠一:对于一个全价购买的物品,可以赠送一个价格 严格小于 它的物品,求最小代价。

按照这样贪心:首先降序排序,将价格相同的物品合并,并记录个数。用堆维护赠送的物品价格。如果当前物品(设价格为 p )可以被赠送,则先入大根堆,对于这种物品不能赠送的部分,如果堆顶元素(设代价为 k )小于该物品,则反悔;否则,若 2×p>k 且该物品剩余数量不少于2,则将两个该物品和堆顶捆绑,看做一个新物品,代价为 2×pk ,放入堆即可。发现这样可以反悔所有可能的情况,最后堆中的元素和就是优惠的价值。

CODE
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+100;
int n,a[N],lsh[N],sum[N],cnt,num[N],p[N],tot;
bool vis[N];
priority_queue<int,vector<int>,greater<int> >q;
ll ans;
stack<int>st;
signed main()
{
	// freopen("q.in","r",stdin);
	// freopen("q.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		ans+=a[i];
	}
	sort(a+1,a+n+1,greater<int>());
	memcpy(lsh,a,sizeof(a));
	cnt=unique(lsh+1,lsh+n+1)-lsh-1;
	for(int i=1,j=0;i<=n;i++){
		if(a[i]!=a[i-1])j++;
		num[j]++;
	}
	for(int i=1;i<=cnt;i++)sum[i]=sum[i-1]+num[i];
	for(int i=1;i<=cnt;i++){
		int k=min(num[i],sum[i-1]-tot*2);
		for(int j=1;j<=k;j++){
			st.push(lsh[i]);tot++;
		}
		for(int j=1;j<=num[i]-k;j+=2){
			if(q.empty())break;
			if(q.top()<lsh[i]){
				q.pop();
				st.push(lsh[i]);
				if(j!=num[i]-k){
					st.push(lsh[i]),tot++;
				}
			}
			else {
				if(j!=num[i]-k&&lsh[i]*2>q.top()){
					st.push(lsh[i]*2-q.top());tot++;
					st.push(q.top());q.pop();
				}
			}
		}
		while(!st.empty()){
			q.push(st.top());
			st.pop();
		}
	}
	while(!q.empty()){
		ans-=q.top();
		q.pop();
	}
	cout<<ans;
}

种树

又是反悔贪心。

按照降序贪心,对于一个已选的 ai 只有同时选择 ai1ai+1 才会反悔,链表维护,反悔时,更改 aiai1+ai+1ai

剩下的以后再写。

fw

posted @   Abnormal123  阅读(37)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示