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
min∑wici∑ai,j×ci≥vj
对偶
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
max∑vjxj∑ai,j×xj≤wi
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 n≤3 特判, n > 3 n>3 n>3 简单dp一下。这题就切了。
然后题解给出的证明很显然(问题是咋想出来的啊我giao
至于这为什么是对的,考虑对 ∣ S ∣ |S| ∣S∣ 归纳,不妨设 S S S 前 ∣ S ∣ − 1 |S|-1 ∣S∣−1 位中有相邻两个字符不同,那么如果 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;
}