重庆八中周赛Round#1

b998a3b3-532a-451f-99af-7a0998831c4b

 

 贴贴题解

#include<bits/stdc++.h>
using namespace std;
int n,m;
int s[505][505];
int c=0;
bool f1,f2;
int main(){
    //freopen("cover.in","r",stdin);
    //freopen("cover.out","w",stdout);
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
    	string a;
    	cin>>a;
        for(int j=1;j<=m;j++){
        	s[i][j]=a[j-1]-'0';
        	if(s[i][j]==0) f2=1;
            if(s[i][j]==1) c++;
        }
    }
    for(int i=0;i<=n+1;i++){
    	s[i][0]=1;
    	s[i][m+1]=1;
	}
	for(int i=0;i<=m+1;i++){
		s[0][i]=1;
		s[n+1][i]=1;
	}//边界处理,避免出界 
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(s[i][j]==0){
				if(s[i+1][j]==0||s[i-1][j]==0||s[i][j+1]==0||s[i][j-1]==0||s[i-1][j-1]==0||s[i-1][j+1]==0||s[i+1][j+1]==0||s[i+1][j-1]==0){
					f1=1;
					i=n+1;
					break;
				}
			}
		}
	}// 
	if(f1){
		printf("%d",c);
	}
	else if(f2){
		printf("%d",c-1);
	}
	else{
		printf("%d",c-2);
	}
}

考试时,没想到对角时也成立的情况,拿了80pts


 

小小线段树,岂能难倒我

 代码:

#include<bits/stdc++.h>
#define N 500005
using namespace std;
typedef long long ll;
const ll p=998244353;
int n,m;
int typ[N];
ll a[N];
struct tree{
	ll sum,mul;
	int l,r;
}t[N<<2];
void pushup(int k){ 
	t[k].mul=(t[k<<1].mul*t[k<<1|1].mul)%p;
	t[k].sum=(t[k<<1].sum*t[k<<1|1].mul+t[k<<1|1].sum)%p;
}
void build(int k,int l,int r){//建树 
	t[k].l=l,t[k].r=r;
	if(l==r){
		if(typ[l]==0){
			t[k].mul=a[l];
			t[k].sum=0;
		}
		else {
			t[k].mul=1;
			t[k].sum=a[l];
		}
		return ;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build((k<<1)|1,mid+1,r);
	pushup(k);
}
void change(int k,int pos,int flag,ll val){//单点修改加法 
	if(t[k].l==t[k].r){
		if(flag==0){
			t[k].mul=val;
			t[k].sum=0;
		}
		else {
			t[k].mul=1;
			t[k].sum=val;
		}
		return ;
	}
	int mid=(t[k].l+t[k].r)>>1;
	if(pos<=mid)change(k<<1,pos,flag,val);
	else change((k<<1)|1,pos,flag,val);
	pushup(k);
} 
struct st{
	ll x1,x2;
};
st getsum(int k,int l,int r){
	st an;
	an.x1=1,an.x2=0;
	if(l<=t[k].l&&t[k].r<=r){
		an=((st){t[k].mul,t[k].sum});
		return  an;
	}
	int mid=(t[k].l+t[k].r)>>1;
	if(mid>=l){
		st cc=getsum(k<<1,l,r);
		an.x1=(an.x1*cc.x1)%p;
		an.x2=(an.x2*cc.x1+cc.x2)%p;
	}
	if(mid<r){
		st cc=getsum(k<<1|1,l,r);
		an.x1=(an.x1*cc.x1)%p;
		an.x2=(an.x2*cc.x1+cc.x2)%p;
	}
	return an;
}
int main(){
	freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d%lld",&typ[i],&a[i]);
	}
	build(1,1,n);
	while(m--){
		int op,x,y;
		ll z;
		scanf("%d%d%d%lld",&op,&x,&y,&z);
		if(op==0){
			change(1,x,y,z);
		}
		else {
			st aa=getsum(1,x,y);
			printf("%lld\n",(aa.x1*z+aa.x2)%p);
		}
	}
} 
/*
5 5
0 8
1 5
1 7
1 1
0 8
1 5 5 3
0 2 0 6
0 1 1 2
0 5 1 6
1 1 5 6
*/

 思路:

大体上可以知道这道题要用到点双联通分量,

观察样例1:

 红色的数字为点的编号,蓝色的为耗用资源;

易知:0,1,2,3 为一个点双;2,4 为一个;2,5 为一个;

          2为割点

通过缩点可得下图:

 黄色边即为缩点的边,黑色边都忽略;

观察上图可知,若要保证联通至少要占2个叶子(即总的叶子数-1)

所以关键是找叶子,叶子有一个特性:叶子所在的点双只有一个割点;

特判:

整张图若没有割点,那么ans=min(cost[i])

#include <bits/stdc++.h>
#define pb(i) push_back(i)
using namespace std;
typedef long long ll;
const ll N=200005;
ll n,m,cnt=1,tot,c;
ll MAX,MIN=2e16;
ll ans;
ll cost[N];
ll dfn[N],low[N],head[N<<1],mco[N];
bool vis[N];
bool f[N];
stack<ll>s;
vector<ll>sthold[N];
ll pos[N];
struct edge{
	ll next,to;
}e[N<<2];
void add(ll from,ll to){
	e[++cnt].next=head[from];
	e[cnt].to=to;
	head[from]=cnt;
}
void tarjan(ll k,ll fa){
	dfn[k]=++tot,low[k]=tot;
	ll son=0;
	s.push(k);
	for(ll i=head[k];i;i=e[i].next){
		ll v=e[i].to;
		if(fa==i) continue;
		if(!dfn[v]){
			son++;
			tarjan(v,i);
			low[k]=min(low[k],low[v]);
			if(dfn[k]<=low[v]){
				c++;
				while(!s.empty()&&s.top()!=v){
					sthold[c].pb(s.top());
					s.pop();
				}
				sthold[c].pb(v),sthold[c].pb(k);
				s.pop();
				if(k!=0) vis[k]=1;
			}
		}
		else{
			low[k]=min(low[k],dfn[v]);
		}
	}
	if(k==0&&son>1) vis[k]=1;
}
int main() {
	freopen("stronghold.in","r",stdin);
	freopen("stronghold.out","w",stdout);
	scanf("%lld%lld",&n,&m);
	for(ll i=0;i<n;i++) {
		scanf("%lld",&cost[i]);
		MIN=min(cost[i],MIN);
	}
	for(ll i=1,x,y;i<=m;i++){
		scanf("%lld%lld",&x,&y);
		add(x,y),add(y,x);
	}
	tarjan(0,0);
	ll o=n-1;
	ll sta=2e9;
	for(ll i=1;i<=c;i++){
		ll cc=0;
		ll p;
		ll MI=2e15;
		for(auto x:sthold[i]){
			if(!vis[x]) {
				MI=min(MI,cost[x]);
				f[x]=1;
			}
			else p=x,sta=p,cc++;
		}
		if(MI==2e15) continue;
		if(cc==1) {
			ans+=MI;
			MAX=max(MAX,MI);
		} 
	}
	if(sta==2e9) {
		printf("%lld\n",MIN);
		return 0;
	}
	printf("%lld\n",ans-MAX);
}
/*
5 8
19 67 35 31 44
0 1
0 2
0 3
1 2
1 3
2 3
2 4
3 4
*/
/*
19
*/

 就这样


                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值