20210621模拟赛

鬼知道我已经多久没更博客了。

原因是近一个月的训练都是做杂题,而且杂题都是不准放出来的(没错,就是模拟赛的题混成一堆丢给你刷。

然后傻逼csdn不能加私密,于是乎好久都没写博客了。

接下来是退役前的最后一个月。计划是一周四场模拟赛 + 一些杂题。好歹OI一场,多留下一点东西吧,退役前博客会持续更新,顺便也每日监督自己不划水了。

链接都是校内的所以麻油题面。

A. ix35的点集

构造。

由于样例没有 NO ,所以直接猜想如果没有奇环就一定有答案。

然后一个很自然的想法就是取没有出边的强连通分量出来,给强连通分量的点染色,相邻的两个点颜色不同,由于没有奇环,所以一定染出来合法。取一个颜色的点出来放到 K K K 里面去,然后把连向 K K K 的点删掉。这个强连通分量也删掉。

我们不断去取没有出边的强连通分量,归纳一下,一定有解。

#include <bits/stdc++.h>
#define N 5003
using namespace std;
int n,m,T;
vector<int> son[N],_son[N],a,res;
void add(int x,int y){ son[x].push_back(y),_son[y].push_back(x); }
bool tag[N],vis[N],tag_1;
int dfn[N],low[N],ti,cnt,co[N];
stack<int> st;
void tarjan(int x){
	dfn[x]=low[x]=++ti;
	st.push(x); vis[x]=1;
	for(int y : son[x]){
		if(tag[y]) continue;
		if(!dfn[y]) co[y]=co[x]^1,tarjan(y),low[x]=min(low[x],low[y]);
		else if(vis[y]) tag_1=tag_1|(co[y]!=co[x]^1),low[x]=min(low[x],dfn[y]);
	}
	if(low[x]==dfn[x]){
		++cnt;
		while(st.top()!=x){
			if(cnt==1&&co[st.top()]==co[x]) a.push_back(st.top());
			vis[st.top()]=0,st.pop();
		}
		if(cnt==1) a.push_back(x);
		vis[st.top()]=0,st.pop();
	}
}
int solve(){
	a.clear(); cnt=0;
	for(int i=1;i<=n;i++) low[i]=dfn[i]=co[i]=0;
	for(int i=1;i<=n;i++)
		if(!tag[i]){ tarjan(i); break; }
	for(int x : a){
		res.push_back(x); tag[x]=1;
		for(int y : _son[x]) tag[y]=1;
	}
	return cnt;
}
int main(){
//	freopen("test.in","r",stdin);
	cin>>n>>m>>T;
	int u,v;
	for(int i=1;i<=m;i++) cin>>u>>v,add(u,v);
	for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
	if(tag_1){ cout<<"ODD\n"; return 0; }
	int now=n;
	while(solve()) ;
	cout<<"YES\n";
	cout<<res.size()<<'\n';
	for(int i=0;i<res.size();i++){
		cout<<res[i]<<' ';
	}
	cout<<endl;
	return 0;
}

B. Jerry and Tom

场上口胡了但没写(艹我居然忘了可行费用流咋写 救命

线性规划, a i , j a_{i,j} ai,j 表示 i i i 是否在 j j j 路径上,列出式子
min ⁡ ∑ w i c i ∑ a i , j × c i ≥ v j \min \sum w_i c_i \\ \sum a_{i,j}\times c_i \ge v_j minwiciai,j×civj
对偶
max ⁡ ∑ v j x j ∑ a i , j × x j ≤ w i \max \sum v_j x_j\\ \sum a_{i,j}\times x_j \le w_i maxvjxjai,j×xjwi
w i w_i wi 就是树边的流量限制,每条路径 ( u , v ) (u,v) (u,v) v v v u u u 连一条流量无穷的边,单位流量费用为 v j v_j vj 。裸的最大费用可行流。

#include <bits/stdc++.h>
#define N 5003
#define M 1000005
using namespace std;
int n,m,fa[N],s,t;
int f[N],nxt[M],to[M],fl[M],co[M],_=-1; 
void link(int x ,int y,int z,int val){
	nxt[++_]=f[x],f[x]=_,to[_]=y,fl[_]=z,co[_]=val;
	nxt[++_]=f[y],f[y]=_,to[_]=x,fl[_]=0,co[_]=-val;
} 
queue<int> q;
int dis[N],vis[N],cur[N];
int lim;
bool spfa(){
	lim++;
	for(int i=0;i<=t;i++) dis[i]=1e9,vis[i]=0,cur[i]=f[i];
	dis[t]=0; q.push(t);
	int x,y;
	while(!q.empty()){
		x=q.front(); q.pop();
		vis[x]=0;
		for(int i=f[x];i!=-1;i=nxt[i]){
			y=to[i]; if(!fl[i^1]||dis[y]<=dis[x]+co[i^1]) continue;
			dis[y]=dis[x]+co[i^1]; 
			if(!vis[y]) vis[y]=1,q.push(y);
		}
	}
	return dis[s]!=dis[0];
}
int dfs(int x,int flow){
	vis[x]=1;
	if(x==t) return flow; 
	int y,u,now=0;
	for(int i=cur[x];i!=-1;i=nxt[i]){
		y=to[cur[x]=i]; if(!fl[i]||vis[y]||dis[x]!=dis[y]+co[i]) continue;
		u=dfs(y,min(flow-now,fl[i]));
		fl[i]-=u,fl[i^1]+=u;
		now+=u;
		if(now==flow) break;
	}
	if(!now) dis[x]=1e9;
	vis[x]=0;
	return now;
}
int zkw_dinic(){
	int res=0,u;
	while(spfa()) while((u=dfs(s,1e9)))res+=u*dis[s];
	return res;
}
int sum1[N],sum2[N];
int main(){
	memset(f,-1,sizeof(f));
	cin>>n>>m; s=n+1,t=s+1;
	int u,v,w,res=0; 
	for(int i=2;i<=n;i++) cin>>u>>v,link(u,i,v,0);
	for(int i=1;i<=m;i++){
		cin>>u>>v>>w; res+=w*50;
		link(u,v,50,w);
		sum1[u]+=50,sum2[v]+=50;
	}
	for(int i=1;i<=n;i++){
		if(sum1[i]) link(s,i,sum1[i],0);
		if(sum2[i]) link(i,t,sum2[i],0);
	}	
	res-=zkw_dinic();
	cout<<res<<'\n';	
}

C. 五五开

妙题

妙啊妙啊

妙不可言,手玩出正解。

我们猜想当 n n n 大于 3 3 3 时, T T T 的充要条件是

  • a , b , c a,b,c a,b,c 看成 0 , 1 , 2 0,1,2 0,1,2 ,总和 m o d   3 mod\ 3 mod 3 是保持不变。
  • T = S T=S T=S 或者 T T T 中有相邻的相同字符。

然后你发现你猜对了(woc?请问这怎么猜

n ≤ 3 n\le3 n3 特判, n > 3 n>3 n>3 简单dp一下。这题就切了。

然后题解给出的证明很显然(问题是咋想出来的啊我giao

至于这为什么是对的,考虑对 ∣ S ∣ |S| S 归纳,不妨设 S S S ∣ S ∣ − 1 |S|-1 S1 位中有相邻两个字符不同,那么如果 S S S T T T 最后一位相等直接做完了。否则,假如 S S S T T T 最后一位分别是 1 和 2,先把 倒数第二位变成 0,其他随便,然后操作最后两位使得 S S S T T T 最后一位相等,就好了。

#include <bits/stdc++.h>
#define N 200005
using namespace std;
typedef long long ll;
const int mod=998244353;
int n,sum;
char s[N];
ll f[N][3][3][2];
int main(){
	ll res=0;
	scanf("%s",s+1);
	n=strlen(s+1);
	for(int i=1;i<=n;i++) sum+=s[i]-'a';
	sum%=3;
	f[1][0][0][0]=f[1][1][1][0]=f[1][2][2][0]=1;
	for(int i=2;i<=n;i++){
		for(int j=0;j<3;j++){ 
			for(int l=0;l<3;l++){
				for(int k=0;k<3;k++){
					if(j==l) 
						(f[i][j][(k+j)%3][1]+=f[i-1][l][k][0]+f[i-1][l][k][1])%=mod;
					else
						(f[i][j][(k+j)%3][0]+=f[i-1][l][k][0])%=mod,
						(f[i][j][(k+j)%3][1]+=f[i-1][l][k][1])%=mod;
				}
			}
		}
	}
	for(int i=1;i<n;i++) if(s[i]==s[i+1]) res++;
	if(res==n-1) return cout<<"1\n",0;
	if(n==2) return cout<<"2\n",0;
	if(n==3) return puts(s[1]==s[3]?"7":s[1]==s[2]||s[2]==s[3]?"6":"3"),0;
	res=res?0:1;
	for(int j=0;j<3;j++) (res+=f[n][j][sum][1])%=mod;
	cout<<res<<endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值