2024.6.1 联系记录

1. 消耗战(P2495)

题目描述

在一场战争中,战场由 n 个岛屿和 n1 个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为 1 的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他 k 个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。

侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到 1 号岛屿上)。不过侦查部门还发现了这台机器只能够使用 m 次,所以我们只需要把每次任务完成即可。

输入格式

第一行一个整数 n,表示岛屿数量。

接下来 n1 行,每行三个整数 u,v,w ,表示 u 号岛屿和 v 号岛屿由一条代价为 w 的桥梁直接相连。

n+1 行,一个整数 m ,代表敌方机器能使用的次数。

接下来 m 行,第 i 行一个整数 ki ,代表第 i 次后,有 ki 个岛屿资源丰富。接下来 ki 个整数 h1,h2,...,hki ,表示资源丰富岛屿的编号。

输出格式

输出共 m 行,表示每次任务的最小代价。

数据规模与约定

  • 对于 10% 的数据,n10,m5
  • 对于 20% 的数据,n100,m100,1ki10
  • 对于 40% 的数据,n1000,1ki15
  • 对于 100% 的数据,2n2.5×105,1m5×105,ki5×105,1ki<n,hi1,1u,vn,1w105

简化一下就是每次询问给一堆关键点,每次可以删一些边,删边有代价,求所有关键点与根不连通的最小代价。

暴力 DP 显然,但 O(qn) 过不了一点。

考虑典中典虚树,虚树建完之后咋搞呢,发现如果把一个关键点和根隔开的最小代价就是直接删到根路径中最小的边,dfs 预处理一下就和暴力 dp 没区别了。

要记住虚树咋求的,单调栈维护一条链,如果赛时写挂了就根号分治骗分。

#include<bits/stdc++.h>
namespace Limie{
	#define x first
	#define y second
	#define int long long
	using namespace std;
	typedef long long LL;
	typedef unsigned long long ULL;
	typedef pair<int,int> PII;
}using namespace Limie;
int n,m;
int h[500010],e[1000010],w[1000010],ne[1000010],idx;
int a[500010],dfn[500010],cnt;
int f[500010][20],d[500010],mn[500010];
int stk[500010],top,k;
bool st[500010];
void add(int a,int b,int c){e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++;}
void add(int a,int b){e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
void dfs(int u,int fa)
{
    dfn[u]=++cnt;
	f[u][0]=fa;
    d[u]=d[fa]+1;
    for(int i=1;i<20;i++)f[u][i]=f[f[u][i-1]][i-1];
    for(int i=h[u];~i;i=ne[i]){
        int v=e[i];
        if(v!=fa){
            if(u!=1)mn[v]=min(mn[u],w[i]);
            else mn[v]=w[i];
            dfs(v,u);
        }
    }
}
int lca(int x,int y)
{
    if(d[x]<d[y])x^=y^=x^=y;
    for(int i=19;i>=0;i--)
        if((1<<i)<=d[x]-d[y])x=f[x][i];
    
    if(x==y)return x;
    for(int i=19;i>=0;i--)
        if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];

    return f[x][0];
}
bool cmp(int x,int y){return dfn[x]<dfn[y];}
void build()
{
    sort(a+1,a+k+1,cmp);
    stk[top=1]=1,h[1]=-1;
    for(int i=1;i<=k;i++){
        if(a[i]!=1){
            int l=lca(a[i],stk[top]);
            if(l!=stk[top]){
                while(dfn[l]<dfn[stk[top-1]])add(stk[top-1],stk[top]),top--;
                if(dfn[l]!=dfn[stk[top-1]]) {
                    h[l]=-1;
                    add(l,stk[top]),stk[top]=l;
                }else add(l,stk[top--]);
            }
            h[a[i]]=-1;
            stk[++top]=a[i];
        }
    }
    for(int i=1;i<top;i++)add(stk[i],stk[i+1]);
}
int dp(int u)
{
    int ans=0;
    for(int i=h[u];~i;i=ne[i]){
        int v=e[i];
        ans+=dp(v);
    }if(u==1)return ans;
    if(st[u])return mn[u];
    else return min(ans,mn[u]);
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int i;
	cin>>n;
    memset(h,-1,sizeof h);
    for(i=1;i<n;i++){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }
    dfs(1,1);
    memset(h,-1,sizeof h);
    cin>>m;
    while(m--){
        cin>>k;
        idx=0;
        for(i=1;i<=k;i++){
            cin>>a[i];
            st[a[i]]=1;
        }
        build();
        cout<<dp(1)<<'\n';
        for(i=1;i<=k;i++)st[a[i]]=0;
    }
}

2.最大 m 子段和(加强版)/CF280D (加强加强版)

先考虑 CF280D。

题面

长度为 n 的数列,支持两种操作:
1.修改某个位置的值
2.询问区间 [l,r] 里选出至多 k 个不相交的子段和的最大值。
一共有 m 个操作。


如果 k=1 随便做,线段树秒了喵。

发现不会 k>1 喵。

思考贪心算法,发现可以反悔,就是每次贪心取最大子段和喵,取完后把区间取反,如果取到了两个区间有交,那么重合部分相加为 0 喵,所以这个贪心是真的喵。

随便写写就过了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int n,m;
#define mid (l+r>>1)
struct Extm{
	int l,r,ans;
	Extm operator+(const Extm &w)const{return {l,w.r,w.ans+ans};}
	bool operator<(const Extm &y)const{return ans<y.ans;}
	bool operator>(const Extm &y)const{return ans>y.ans;}
};
struct Node{
	int l,r;
	Extm sum;
	Extm premax,sufmax,ansmax;
	Extm premin,sufmin,ansmin;
	bool tag;
}tr[400010];
Node operator+(const Node &x,const Node &y)
{
	Node u;
	u.sum=x.sum+y.sum;
	u.premax=max(x.premax,x.sum+y.premax);
	u.sufmax=max(y.sufmax,x.sufmax+y.sum);
	u.ansmax=max({x.ansmax,y.ansmax,x.sufmax+y.premax});
	u.premin=min(x.premin,x.sum+y.premin);
	u.sufmin=min(y.sufmin,x.sufmin+y.sum);
	u.ansmin=min({x.ansmin,y.ansmin,x.sufmin+y.premin});
	u.tag=0;
	return u;
}
void pushtag(int u)
{
	swap(tr[u].premin,tr[u].premax);
	swap(tr[u].sufmin,tr[u].sufmax);
	swap(tr[u].ansmin,tr[u].ansmax);
	tr[u].premin.ans*=-1,tr[u].premax.ans*=-1;
	tr[u].sufmin.ans*=-1,tr[u].sufmax.ans*=-1;
	tr[u].ansmin.ans*=-1,tr[u].ansmax.ans*=-1;
	tr[u].sum.ans*=-1;
	tr[u].tag^=1;
}
void pushdown(int u)
{
	if(!tr[u].tag)return;
	pushtag(u<<1),pushtag(u<<1|1);
	tr[u].tag=0;
}
void newnode(int u,int x,int c)
{
	tr[u].premin=tr[u].premax={x,x,c};
	tr[u].sufmin=tr[u].sufmax={x,x,c};
	tr[u].ansmin=tr[u].ansmax={x,x,c};
	tr[u].sum={x,x,c},tr[u].tag=0;
}
void modify(int u,int l,int r,int x,int c)
{
	if(l==r)return newnode(u,x,c);
	pushdown(u);
	if(x<=mid)modify(u<<1,l,mid,x,c);else modify(u<<1|1,mid+1,r,x,c);
	tr[u]=tr[u<<1]+tr[u<<1|1];
}
void reverse(int u,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)return pushtag(u);
	pushdown(u);
	if(L<=mid)reverse(u<<1,l,mid,L,R);
	if(mid<R)reverse(u<<1|1,mid+1,r,L,R);
	tr[u]=tr[u<<1]+tr[u<<1|1];
}
Node query(int u,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)return tr[u];
	pushdown(u);
	if(R<=mid)return query(u<<1,l,mid,L,R);
	if(mid<L)return query(u<<1|1,mid+1,r,L,R);
	return query(u<<1,l,mid,L,R)+query(u<<1|1,mid+1,r,L,R);
}
#undef mid
queue<Node> q; 
int main()
{
	int i;
	cin>>n;
	for(i=1;i<=n;i++){
		int x;
		cin>>x;
		modify(1,1,n,i,x);
	}
	cin>>m;
	while(m--){
		int ty,l,r,k;
		cin>>ty>>l>>r;
		if(ty){
			cin>>k;
			int s=0;
			while(k--){
				Node ans=query(1,1,n,l,r);
				if(ans.ansmax.ans<=0)break;
				q.push(ans),s+=ans.ansmax.ans;
				reverse(1,1,n,ans.ansmax.l,ans.ansmax.r);
			}
			cout<<s<<'\n';
			while(q.size()){
				Node ans=q.front();
				reverse(1,1,n,ans.ansmax.l,ans.ansmax.r);
				q.pop();
			}
		}else modify(1,1,n,l,r);
	}
}

弱化版很简单喵,题意就是没有修改且区间为整个序列。

有神秘简单做法,挖个坑,回头补喵。

贴一份来自 wmh 的神仙代码。

#include<bits/stdc++.h>
using namespace std;
int a[1000005],zt[1000005];
long long b[1000005];
int nxt[1000005],pre[1000005],vist[1000005];
priority_queue<pair<long long,int>>pq;
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]),zt[i]=(a[i]>=0);
    long long ans=0;
    int t=0;
    for(int i=1;i<=n;){
        int j=i;long long h=0;
        while(j<=n&&zt[j]==zt[i])h+=a[j++];
        if(zt[i])ans+=h;
        if(zt[i]||(i>1&&j<=n))b[++t]=(h<0?h:-h);
        i=j;
    }
    int k=max(0,(t+1)/2-m);
    for(int i=1;i<=t;i++)pq.push(make_pair(b[i],i));
    b[0]=b[t+1]=-1e18;
    for(int i=1;i<t;i++)nxt[i]=i+1;
    for(int i=2;i<=t;i++)pre[i]=i-1;
    while(k--){
        int aa=pq.top().second;
        if(vist[aa]){
            pq.pop();
            k++;
            continue;
        }
        ans+=pq.top().first;
        pq.pop();
        int a1=pre[aa],a2=nxt[aa];
        nxt[pre[a1]]=pre[nxt[a2]]=aa;
        pre[aa]=pre[a1],nxt[aa]=nxt[a2];
        vist[a1]=vist[a2]=1;
        pq.push(make_pair(b[aa]=b[a1]+b[a2]-b[aa],aa));
    }
    printf("%lld\n",ans);
    return 0;
}

3. 和谐矩阵(P3164)

题目描述

我们称一个由 01 组成的矩阵是和谐的,当且仅当每个元素都有偶数个相邻的 1。一个元素相邻的元素包括它本身,及他上下左右的 4 个元素(如果存在)。给定矩阵的行数和列数,请计算并输出一个和谐的矩阵。注意:所有元素为 0 的矩阵是不允许的。

输入格式

输入一行,包含两个空格分隔的整数 mn,分别表示矩阵的行数和列数。

输出格式

输出包含 m 行,每行 n 个空格分隔整数(01),为所求矩阵。测试数据保证有解。


一眼高消,跟昨天做的外星千足虫没有区别喵。

#include<bits/stdc++.h>
namespace Limie{
	#define x first
	#define y second
	using namespace std;
	typedef long long LL;
	typedef unsigned long long ULL;
	typedef pair<int,int> PII;
}using namespace Limie;
int n,m;
int dx[]={-1,0,1,0},dy[]={0,-1,0,1};
bitset<1610> a[1610];
void gauss()
{
	for(int i=0;i<n*m;i++){
		for(int j=i+1;j<n*m;j++)
			if(a[j][i]){swap(a[i],a[j]);break;}
			
		if(!a[i][i])a[i][i]=a[i][n*m]=1;
		for(int j=0;j<n*m;j++)
			if(j!=i&&a[j][i])a[j]^=a[i];
	}
	for(int i=0;i<n*m;i++){
		if(i&&i%n==0)cout<<'\n';
		cout<<a[i][n*m]<<' ';
	}
}
int main()
{
	int i,j;
	cin>>n>>m;
	for(i=0;i<n;i++)
		for(j=0;j<m;j++){
			a[i*m+j][i*m+j]=1;
			for(int k=0;k<4;k++){
				int x=i+dx[k],y=j+dy[k];
				if(x<0||x>=n||y<0||y>=m)continue;
				a[i*m+j][x*m+y]=1;
			}
		}
		
	gauss();
}

4. ABC356

今晚有点原神。

D 使用 1<<x 结果 x=-1,所以卡了一会喵。

F 是 fhq-treap 板子,调调细节就可以了喵。

G 不会喵。

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