title : 第十八届同济大学程序设计竞赛暨高校网络友谊赛
date : 2022-5-30
tags : ACM,题解,练习记录
author : Linno
第十八届同济大学程序设计竞赛暨高校网络友谊赛
题目链接:https://ac.nowcoder.com/acm/contest/16832
A-自适应树游走协议
签到题,虽然说签了很久。题意是满二叉树中每个结点有状态01。如果有结点的两个叶子儿子都是1则会发生冲突,并且这个信息会向上传递。已知检查一个结点有无冲突和解决冲突叶子都需要一个时间单位,问需要花费多少时间解决冲突。
子树中有两个以上1的叶子就需要继续遍历,记录子树中1的个数,把树建出来向上求和即可。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e6+7;
const int mod=1e9+7;
#define ls (p<<1)
#define rs (p<<1|1)
#define mid ((l+r)>>1)
int n,k,idx=0,tr[N<<2],lf[N<<2];
inline void update(int p,int l,int r,int pos){
if(l==r){
tr[p]=1;
return;
}
if(pos<=mid) update(ls,l,mid,pos);
else update(rs,mid+1,r,pos);
tr[p]=tr[ls]+tr[rs];
}
inline void query(int p){
++idx;
if(tr[p]<=1) return;
query(ls);
query(rs);
}
void Solve(){
cin>>n>>k;
for(int i=1,p;i<=k;++i){
cin>>p;
update(1,1,n,p);
}
query(1);
cout<<idx<<"\n";
}
signed main(){
int T=1;
while(T--){
Solve();
}
return 0;
}
B-简单的数学题
数学不太会,题解区有。以后自己能写出来再补上。
https://blog.nowcoder.net/n/90a3fa421572421e98a2b1309ff972ad
C-困难的数学题
给定正整数n,将其分解为若干个不小于k的正整数之和,有多少种方案?(顺序不同的划分也视为不同的方案)
打个表就可以找到规律了,硬推比较难得出转移方程。
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e6+7;
const int mod=1e9+7;
int S[N],n,k,ans;
void Solve(){
cin>>n>>k;
for(int i=0;i<k;++i) S[i]=0;
S[k]=1;
for(int i=k+1;i<=n;++i){
S[i]=(S[i-1]+S[i-k])%mod;
}
cout<<S[n]<<"\n";
}
signed main(){
// ios::sync_with_stdio(0);
// cin.tie(0);cout.tie(0);
// freopen("in.cpp","r",stdin);
// freopen("out.cpp","w",stdout);
int T=1;
// cin>>T;
// clock_t start,finish;
// start=clock();
while(T--){
Solve();
}
// finish=clock();
// cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl; return 0;
}
D-平衡的字符串
给定01和?组成的串,?可以当作0或者1,问能否使得所有长度为k的子串中0和1的个数相等。
首先k是奇数肯定不行。发现只要第i为是0/1,那么第i+k位也一定是一样的数,因此我们可以全部映射到前面k位,并且判断能否把0和1都填成k/2个即可。
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e6+7;
const int mod=1e9+7;
//int read(){ int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int n,k,f[N];
string str;
void Solve(){
cin>>n>>k;
cin>>str;
int flag=0,idx=0,xx=0;
if(k&1||(n<k)){
cout<<"No\n";
return;
}else{
for(int i=0;i<n;++i){
if(str[i]=='1'){
if(f[i%k]=='0') flag=1;
else f[i%k]='1';
}else if(str[i]=='0'){
if(f[i%k]=='1') flag=1;
else f[i%k]='0';
}
}
for(int i=0;i<k;++i){
if(f[i]=='0') ++idx;
else if(f[i]=='1') --idx;
else ++xx;
}
if(abs(idx)>xx) flag=1;
}
if(flag) cout<<"No\n";
else cout<<"Yes\n";
}
signed main(){
// ios::sync_with_stdio(0);
// cin.tie(0);cout.tie(0);
// freopen("in.cpp","r",stdin);
// freopen("out.cpp","w",stdout);
int T=1;
// cin>>T;
// clock_t start,finish;
// start=clock();
while(T--){
Solve();
}
// finish=clock();
// cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl; return 0;
}
E-不平衡的字符串
也不太会,以后再补。
https://blog.nowcoder.net/n/8a09e1a2772c48959d7290e1dca67df7
F-值钱的项链
在换中的每个位置填上红色或者蓝色的珠子,规定不能连续两个红色,求最大价值。
起始位红色做一遍,蓝色做一遍dp,然后取最大值即可。
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e6+7;
const int mod=1e9+7;
//int read(){ int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int n,m,ans,vis[N],mx[N][2],dp[N][2];
//int val[N][N],col[N][N];
vector<vector<int>>val,col;
void Solve(){
cin>>n>>m;
val.resize(n+1);
col.resize(n+1);
for(int i=1;i<=n;++i){
val[i].emplace_back(0);
for(int j=1,x;j<=m;++j){
cin>>x;
val[i].emplace_back(x);
}
}
for(int i=1;i<=n;++i) mx[i][0]=mx[i][1]=-1;
for(int i=1;i<=n;++i){
col[i].emplace_back(0);
for(int j=1,x;j<=m;++j){
cin>>x;
col[i].emplace_back(x);
if(col[i][j]&1){
mx[i][1]=max(mx[i][1],val[i][j]);//该位置价值最大的红颜色
}else{
mx[i][0]=max(mx[i][0],val[i][j]);//该位置价值最大的蓝颜色
}
}
}
dp[1][0]=mx[1][0],dp[1][1]=-1; //第一个是蓝的情况
for(int i=2;i<=n;++i){
if(dp[i-1][0]==-1) dp[i][1]=-1;
else dp[i][1]=(mx[i][1]==-1)?-1:(dp[i-1][0]+mx[i][1]);
if(dp[i-1][1]!=-1&&dp[i-1][0]!=-1){
dp[i][0]=(mx[i][0]==-1)?-1:(max(dp[i-1][1],dp[i-1][0])+mx[i][0]);
}else if(dp[i-1][1]!=-1){
dp[i][0]=(mx[i][0]==-1)?-1:(dp[i-1][1]+mx[i][0]);
}else if(dp[i-1][0]!=-1){
dp[i][0]=(mx[i][0]==-1)?-1:(dp[i-1][0]+mx[i][0]);
}else dp[i][0]=-1;
}
ans=max(dp[n][0],dp[n][1]);
dp[1][1]=mx[1][1],dp[1][0]=-1; //第一个是红,最后一个只能是蓝
for(int i=2;i<=n;++i){
if(dp[i-1][0]==-1) dp[i][1]=-1;
else dp[i][1]=(mx[i][1]==-1)?-1:(dp[i-1][0]+mx[i][1]);
if(dp[i-1][1]!=-1&&dp[i-1][0]!=-1){
dp[i][0]=(mx[i][0]==-1)?-1:(max(dp[i-1][1],dp[i-1][0])+mx[i][0]);
}else if(dp[i-1][1]!=-1){
dp[i][0]=(mx[i][0]==-1)?-1:(dp[i-1][1]+mx[i][0]);
}else if(dp[i-1][0]!=-1){
dp[i][0]=(mx[i][0]==-1)?-1:(dp[i-1][0]+mx[i][0]);
}else dp[i][0]=-1;
}
if(n==1) cout<<max(mx[1][0],mx[1][1])<<"\n";
else cout<<max(ans,dp[n][0])<<"\n";
for(int i=1;i<=n;++i) col[i].clear(),val[i].clear();
}
signed main(){
// ios::sync_with_stdio(0);
// cin.tie(0);cout.tie(0);
// freopen("in.cpp","r",stdin);
// freopen("out.cpp","w",stdout);
int T=1;
cin>>T;
// clock_t start,finish;
// start=clock();
while(T--){
Solve();
}
// finish=clock();
// cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl; return 0;
}
G-自行车调度
给一张图,每个图上都有点权,每条边有单位花费,可以支付代价转移点的若干点权。求所有点点权相同的最小代价。
最小费用流的裸题。知道怎么建模就行了:比平均值大的点权和源点相连,否则和汇点相连,流量就是这个差值,然后点之间不限流量,规定单位费用就可以跑了。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=505,M=1e5+7;
typedef long long ll;
int read(){ int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
void write(ll x){if(x>9) write(x/10);putchar(x%10+'0');}
struct E{int v,w,f,nxt;}e[M];
int head[N],cnt=1;
inline void addedge(int u,int v,int w,int f){e[++cnt]=(E){v,w,f,head[u]};head[u]=cnt;}
int n,m,a[N];
int inq[N],pre[N];
ll mcost,mflow,sum,need,flow[N],dis[N];
inline bool spfa(int s,int t){
for(int i=0;i<=n+1;++i) dis[i]=inf,inq[i]=0,flow[i]=0;
queue<int>q;
q.push(s);
inq[s]=1;dis[s]=0;flow[s]=inf;
while(q.size()){
int fro=q.front();
q.pop();
inq[fro]=0;
for(int i=head[fro];i;i=e[i].nxt){
int to=e[i].v,w=e[i].w,f=e[i].f;
if(w&&dis[to]>dis[fro]+f){
dis[to]=dis[fro]+f;
flow[to]=min(flow[fro],(ll)w);
pre[to]=i;
if(!inq[to]){
q.push(to);
inq[to]=1;
}
}
}
}
return dis[t]!=inf;
}
inline void update(int s,int t){
int x=t;
while(x!=s){
int i=pre[x];
e[i].w-=flow[t];
e[i^1].w+=flow[t];
x=e[i^1].v;
}
mflow+=flow[t];
mcost+=(ll)flow[t]*(ll)dis[t];
}
inline void EK(int s,int t){
while(spfa(s,t)){
update(s,t);
}
}
void solve(){
sum=0;cnt=1;need=0;mflow=0;mcost=0;
memset(head,0,sizeof(head));
memset(flow,0,sizeof(flow));
memset(pre,0,sizeof(pre));
n=read();m=read();
for(int i=1;i<=n;++i) a[i]=read(),sum+=a[i];
for(int i=1,u,v,w;i<=m;++i){
u=read();v=read();w=read();
addedge(u,v,inf,w); //单位代价
addedge(v,u,0,-w);
addedge(v,u,inf,w);
addedge(u,v,0,-w);
}
if(sum%n!=0){
puts("-1");
}else{
sum/=n;
for(int i=1;i<=n;++i){
if(a[i]>sum) addedge(0,i,a[i]-sum,0),addedge(i,0,0,0),need+=a[i]-sum;
else addedge(i,n+1,sum-a[i],0),addedge(n+1,i,0,0);
}
EK(0,n+1);
if(mflow==need) write(mcost),putchar('\n');
else puts("-1");
}
}
signed main(){
int t=read();
while(t--){
solve();
}
return 0;
}
H- 三阳开泰
给定A,B,C,X,问有多少a,b,满足
a
≤
A
,
b
≤
B
,
c
≤
C
a
xor
b
≤
X
,
b
xor
c
≤
X
,
c
xor
a
≤
X
a≤A,b≤B,c≤C\\ a\,\operatorname{xor}\,b \le X,b\,\operatorname{xor}\,c\le X,c\,\operatorname{xor}\,a\le X
a≤A,b≤B,c≤Caxorb≤X,bxorc≤X,cxora≤X
数据范围得出是数位DP题,对我来说思维量有点大。跑记忆化搜索,选择当前数位a,b,c填的数,然后往下一位转移,需要记录当前为a,b,c能填的最大数以及三个异或值当前位能填的最大数。
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=1e6+7;
const int mod=1e9+7;
//int read(){ int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int a,b,c,x,cnta,cntb,cntc,cntx;
int aa[N],bb[N],cc[N],xx[N];
int dp[100][2][2][2][2][2][2];
/*
对于二进制的每一位i(从高到低),考虑a,b,c能填0~num[i]之中的哪些数字
填上这些数字之后判断a^b,b^c,c^a是否大于x,合法则计算贡献。
*/
inline int dfs(int pos,int la,int lb,int lc,int lab,int lac,int lbc){
if(pos==-1) return 1;
int &ans=dp[pos][la][lb][lc][lab][lac][lbc];
if(ans!=-1) return ans;
ans=0;
int upa=la?aa[pos]:1;
int upb=lb?bb[pos]:1;
int upc=lc?cc[pos]:1;
int upx=xx[pos];
for(int i=0;i<=upa;++i){
for(int j=0;j<=upb;++j){
for(int k=0;k<=upc;++k){
if(lab&&((i^j)>xx[pos])) continue;
if(lac&&((i^k)>xx[pos])) continue;
if(lbc&&((j^k)>xx[pos])) continue;
ans=(ans+dfs(pos-1,la&&(i==upa),lb&&(j==upb),lc&&(k==upc),lab&&(i^j==xx[pos]),lac&&(i^k==xx[pos]),lbc&&(j^k==xx[pos]))%mod)%mod;
}
}
}
return ans;
}
void Solve(){
cin>>a>>b>>c>>x;
memset(aa,0,sizeof aa);
memset(bb,0,sizeof bb);
memset(cc,0,sizeof cc);
memset(xx,0,sizeof xx);
memset(dp,-1,sizeof dp);
cnta=cntb=cntc=cntx=0;
while(a) aa[cnta++]=a&1,a>>=1;
while(b) bb[cntb++]=b&1,b>>=1;
while(c) cc[cntc++]=c&1,c>>=1;
while(x) xx[cntx++]=x&1,x>>=1;
cout<<dfs(63,1,1,1,1,1,1)<<"\n";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("in.cpp","r",stdin);
// freopen("out.cpp","w",stdout);
int T=1;
cin>>T;
// clock_t start,finish;
// start=clock();
while(T--){
Solve();
}
// finish=clock();
// cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl;
return 0;
}
I-图中修边
一次操作可以使a和b之间新增一条边,a与c,b与c之间的两条边消失。
问,给定的简单无向图是否可以在有限次操作之后,令图中所有点的度数小于等于1。
判一下每个连通块是否是完全图或者欧拉图即可,可以并查集做。
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+7;
int fa[N],u[N],v[N],sz[N],sum[N],val[N];
int n,m,deg[N],dfn[N],flag,res;
inline int find(int x){return (fa[x]==x)?x:(fa[x]=find(fa[x]));}
void Solve(){
cin>>n>>m;
for(int i=1;i<=n;++i) deg[i]=dfn[i]=sz[i]=val[i]=sum[i]=0;
flag=0;
for(int i=1;i<=n;++i){fa[i]=i;sz[i]=1;}
for(int i=1;i<=m;++i){
cin>>u[i]>>v[i];
// addedge(u,v); addedge(v,u);
++deg[u[i]];++deg[v[i]];
}
for(int i=1;i<=n;++i){
if(deg[i]>1&°[i]%2==0){
val[i]=1;
}
sum[i]=deg[i];
}
for(int i=1,fx,fy;i<=m;++i){
fx=find(u[i]),fy=find(v[i]);
if(fx!=fy){
fa[fx]=fy;
sz[fy]+=sz[fx];
sum[fy]+=sum[fx];
val[fy]+=val[fx];
}
}
int flag=1;
for(int i=1;i<=n;++i){
if(fa[i]==i){
if(val[i]==sz[i]) flag=0;
if(sum[i]==sz[i]*(sz[i]-1)&&sz[i]>2) flag=0;
}
}
if(flag) cout<<"YES\n";
else cout<<"NO\n";
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int T=1;
cin>>T;
// clock_t start,finish;
// start=clock();
while(T--){
Solve();
}
// finish=clock();
// cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl; return 0;
}
J-下围棋
博弈,给定一棵树,两人轮流裁剪任意非根结点及其所在的子树,不能操作的人输。
树上删边的裸题,每个点的SG值由其儿子(异或)决定,然后考虑叶子结点SG值是1。
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;
//int read(){ int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
//void write(int x){if(x>9) write(x/10);putchar(x%10+'0');}
int n,sg[N];
vector<int>G[N];
inline void dfs(int x,int f){
sg[x]=0;
for(auto to:G[x]){
if(to==f) continue;
dfs(to,x);
sg[x]^=(sg[to]+1);
}
}
void Solve(){
cin>>n;
for(int i=1,u,v;i<n;++i){
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,0);
if(sg[1]) cout<<"NO\n";
else cout<<"YES\n";
for(int i=1;i<=n;++i) G[i].clear();
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("in.cpp","r",stdin);
// freopen("out.cpp","w",stdout);
int T=1;
cin>>T;
// clock_t start,finish;
// start=clock();
while(T--){
Solve();
}
// finish=clock();
// cerr<<((double)finish-start)/CLOCKS_PER_SEC<<endl; return 0;
}