46th ICPC沈阳站部分解题报告以及总结

发布于 2021-11-24  1299 次阅读


  • 留底击伤你的石头,从错误里吸收

先大致写几篇现场做出来的题吧还有几个后来两天补出来的题

我们现场写出来四道题,不过由于罚时高,我们队与牌子失之交臂了,呜呜呜真就差一点

B. Bitwise Exclusive-OR Sequence

现场的时候对于二进制下的三十位每一位都建图,也就是建30个图,然后染色,不过由于常数过大一直超时,不过好在最后使劲优化常数过了,下面是赛场时的代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll read(){
	ll res=0;
	char ch=getchar();
	while(ch>='0'&&ch<='9'){
		res+=(ch-'0');res*=10;ch=getchar();
	}
	return res/10;
}
int c[35][150000],flag,cnt1,cnt2;
int head[35][150000],nt[35][400005],val[35][400005],cea[35],to[35][400005];
ll pow2[40];
ll ans=0;
void add(int th,int u,int v,int w){
	int& ce=cea[th];
	to[th][++ce]=v,nt[th][ce]=head[th][u],head[th][u]=ce,val[th][ce]=w;
	to[th][++ce]=u,nt[th][ce]=head[th][v],head[th][v]=ce,val[th][ce]=w;	
}
void dfs(int th,int now,int color){
	if(flag) return;
	c[th][now]=color;
	if(color==1)cnt1++;
	else cnt2++;
	for(int i=head[th][now];i;i=nt[th][i]){
		int nxt=color,v=to[th][i];
		if(val[th][i]==1) nxt=3-color;
		if(c[th][v]==0)
			dfs(th,v,nxt);
		else
		if(c[th][v]!=nxt){
			flag=1;
			return;
		}
	}
}
int main(){
	pow2[0]=1;
	for(int i=1;i<=30;i++) pow2[i]=pow2[i-1]*2;
	int n=read(),m=read();
	for(int i=1;i<=m;i++){
		int u=read(),v=read();
		ll w=read();
		for(int j=0;j<=30;j++){
			int tmp=pow2[j]&w;
			if(tmp) tmp=1;
			add(j,u,v,tmp);
			//if(j==2) cout<<(pow2[j]&w)<<endl;
		}
	}
	//cout<<endl;
	for(int i=0;i<=30;i++){
		for(int j=1;j<=n;j++){
			if(c[i][j]) continue;
			cnt1=0,cnt2=0;
			dfs(i,j,1);
			if(flag){
				cout<<-1;
				return 0;
			}
			/*if(i==2) cout<<cnt1<<
			" "<<cnt2<<endl;*/
			ans+=min(cnt1,cnt2)*pow2[i];	
		}
	}
	/*for(int i=head[2][3];i;i=nt[2][i])
		cout<<to[2][i]<<" ";*/
	printf("%lld",ans);
	return 0;
}

后来赛后题解有提到过我的做法,提到了一种优化方式,就是30位完全可以一起染色,这是后来写的

#include <bits/stdc++.h>
#define ll long long
using namespace std;
vector<pair<int,int>>G[150000];
int n,m,col[150000],flag;
ll cnt0[40],cnt1[40];
ll ans;
void dfs(int now,int color){
	if(flag) return;
	col[now]=color;
	for(int i=0;i<=30;i++){
		if((color>>i)&1) cnt1[i]++;
		else cnt0[i]++;
	}
	for(auto node:G[now]){
		int v=node.first,w=node.second;
		if(col[v]==-1) dfs(v,color^w);
		else
		if(col[v]!=(color^w)){
			flag=1;
			return;
		}
	}
}

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) col[i]=-1;
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		G[u].push_back(make_pair(v,w));
		G[v].push_back(make_pair(u,w));
	}
	for(int i=1;i<=n;i++){
		if(col[i]!=-1) continue;
		memset(cnt1,0,sizeof(cnt1));
		memset(cnt0,0,sizeof(cnt0));
		dfs(i,0);
		if(flag){
			cout<<-1;
			return 0;
		}
		for(int i=0;i<=30;i++){
			ans+=min(cnt0[i],cnt1[i])*(1ll*(1<<i));
		}
	}
	cout<<ans;
	return 0;
}

E.Edward Gaming, the Champion

签到题不多说,edg牛逼!!!7777777

#include <bits/stdc++.h>
using namespace std;
char s[250000];
int ans;
int main(){
	cin>>(s+1);
	int len=strlen(s+1);
	for(int i=1;i+4<=len;i++){
		if(s[i]=='e'&&s[i+1]=='d'&&s[i+2]=='g'&&s[i+3]=='n'&&s[i+4]=='b') ans++;
	}
	cout<<ans;
	return 0;
}

F.Encoded Strings I

就一模拟

#include <bits/stdc++.h>
using namespace std;
int n,cnt[2000],G[100];
char s[2000];
string ans="";
void getG(int len){
	cnt[len+1]=0;
	map<char,bool>m;
	for(int i=len;i>=1;i--){
		cnt[i]=cnt[i+1];
		if(!m[s[i]]){
			m[s[i]]=1;
			cnt[i]++;
		}
	}
	for(int i=1;i<=26;i++) G[i]=0;
	for(int i=1;i<=26;i++){
		for(int j=len;j>=1;j--){
			if(s[j]=='a'+i-1){
				G[i]=cnt[j+1];
				break;
			}
		}
	}
}
int main(){
	cin>>n;
	cin>>(s+1);
	for(int len=1;len<=n;len++){
		getG(len);
		string tmp="";
		for(int i=1;i<=len;i++){
			int index=G[s[i]-'a'+1]+1;
			char c='a'+index-1;
			tmp+=c;
		}
		ans=max(ans,tmp);
	}
	cout<<ans;
	return 0;
}

J.Luggage Lock

这也许是我赛场上最满意的一道题,出去上趟厕所想出来的

很多人都是用bfs写的而我用的是差分

因为这个很明显是一个区间加或者区间减的操作,所以枚举4位数达到目标数字的途径,不溢出,向上溢出(超过9),向下溢出(减到小于0)。然后计算把两个差分数组变成一样的需要几次

#include <bits/stdc++.h>
using namespace std;
char s1[10],s2[10];
int a[10],b[10],f[100][20],ki[20];
int aa[10],bb[10],ans,cnt;
void dfs(int now){
	if(now>4){
		cnt++;
		for(int i=1;i<=4;i++) f[cnt][i]=ki[i];
		return;
	}
	ki[now]=a[now];
	dfs(now+1);
	ki[now]=a[now]-10;
	dfs(now+1);
	ki[now]=a[now]+10;
	dfs(now+1);
}
void solve(){
	cnt=0;
	cin>>(s1+1)>>(s2+1);
	for(int i=1;i<=4;i++)
		a[i]=s1[i]-'0',b[i]=s2[i]-'0';
	dfs(1);
	int ans=2147483647;
	for(int l=1;l<=cnt;l++){
		for(int i=1;i<=4;i++) aa[i]=f[l][i]-f[l][i-1];
		for(int i=1;i<=4;i++) bb[i]=b[i]-b[i-1];
		int cnt1=0,cnt2=0;
		for(int i=1;i<=4;i++){
			if(aa[i]>bb[i]) cnt1+=(aa[i]-bb[i]);
			else cnt2+=(bb[i]-aa[i]);
		}
		ans=min(ans,min(cnt1,cnt2)+abs(cnt1-cnt2));
	}
	cout<<ans<<endl;
}
int main(){
	int T;
	cin>>T;
	while(T--)
		solve();
	return 0;
} 

然后接下来是我这两天补的三道题,后边可能还会断断续续的更新,不过那些题都很有难度,所以得多搞一阵子了

H.Line Graph Matching

当边数是偶数时,所有边都可以被匹配完。当边数是奇数时,恰好有一条边不会被匹配,如果是非割边,这条边可以不被匹配,如果是割边,且割去这条边之后两侧边数均为偶数,则这条边也可以不被匹配。当时不会用tarjan 2333333

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,dfn[150000],low[150000],tim,siz[150000];
ll sum;
vector<pair<int,int>>G[150000];
void tarjan(int now,int fa){
	dfn[now]=low[now]=++tim;
	for(auto node:G[now]){
		int v=node.first,w=node.second;
		if(v==fa) continue;
		if(!dfn[v]){
			siz[now]++;
			tarjan(v,now);
			siz[now]+=siz[v];
			low[now]=min(low[now],low[v]);
		}
		else{
			low[now]=min(low[now],dfn[v]);
			if(dfn[v]>dfn[now]) siz[now]++;
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		G[u].push_back(make_pair(v,w)),G[v].push_back(make_pair(u,w));
		sum+=w;
	}
	if(m%2==0){
		cout<<sum;return 0;
	}
	tarjan(1,0);
	ll ans=0;
	for(int i=1;i<=n;i++){
		for(auto node:G[i]){
			int v=node.first,w=node.second;
			if(dfn[v]<dfn[i]) continue;
			if(low[v]<=dfn[i]||siz[v]%2==0) ans=max(ans,sum-w);
		}
	}
	cout<<ans;
	return 0;
}

L.Perfect Matchings

思维难度不大,一个树上背包,再来一个容斥

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n;
vector<int>G[4005];
int siz[4005];
ll f[4005][2005][2],g[2005][2];
ll p[2005];
const int mod=998244353;
void dfs(int now,int fa){
	f[now][0][0]=1;
	siz[now]=1;
	for(auto v:G[now]){
		if(v==fa) continue;
		dfs(v,now);
		for(int i=0;i<=n+5;i++) g[i][0]=g[i][1]=0; 
		for(int i=0;i<=siz[now]/2;i++)
		for(int j=0;j<=siz[v]/2;j++){
			if(j){
				g[i+j][0]+=(f[v][j][0]+f[v][j][1])%mod*f[now][i][0]%mod;
				g[i+j][1]+=(f[v][j][0]+f[v][j][1])%mod*f[now][i][1]%mod;
			}
			
			g[i+j+1][1]+=f[now][i][0]*f[v][j][0]%mod;
		}
		siz[now]+=siz[v];
		for(int i=0;i<=siz[now]/2+1;i++) f[now][i][0]+=g[i][0],f[now][i][1]+=g[i][1],
		f[now][i][0]%=mod,f[now][i][1]%=mod;
	}
}
int main(){
	cin>>n;
	for(int i=1;i<2*n;i++){
		int u,v;
		cin>>u>>v;
		G[u].push_back(v),G[v].push_back(u);
	}
	dfs(1,0);
	p[0]=1;
	for(int i=1;i<=n;i++) p[i]=(p[i-1]*(2*i-1))%mod;
	ll ans=p[n];
	//cout<<ans<<endl;
	int tmp=-1;
	for(int i=1;i<=n;i++){
		ans+=tmp*(f[1][i][0]+f[1][i][1])*p[n-i];
		//cout<<f[1][i][0]<<" "<<f[1][i][1]<<endl;
		ans+=mod,ans%=mod;
		tmp*=-1;
	}
	cout<<ans;
	return 0;
}

M.String Problem

还是思维难度不大,不过当时现场没看到这道题

kmp,或者AC自动机就行

#include <bits/stdc++.h>
using namespace std;
char s[1000005];
int nxt[1000005];
int main(){
	scanf("%s",s+1);
	int len=strlen(s+1);
	cout<<"1 1"<<endl;
	int head=1,tot=1;
	for(int i=2;i<=len;i++){
		while(tot!=nxt[tot]&&s[head+nxt[tot]]<s[i]) head=head+(tot-nxt[tot]),tot=nxt[tot];
		if(s[i]==s[head+nxt[tot]]&&tot){
			tot++;
			nxt[tot]=nxt[tot-1]+1;
		}
		else{
			tot++;
			nxt[tot]=0;
		}
		printf("%d %d\n",head,i);
	}
	return 0;
}

下面来做做总结,或许这一场打完后还是没有拿到牌子,不过很接近了,这让我感觉到了很大的希望,接下来猛猛淦!!!先说说这次所存在的问题,第一点就是一个团队配合的事情,大家互相讨论吧也没有一个很有效的信息交换,各抒己见之后,基本没有很大信息交换,然后团队也没有一个很明确的分工。其次就是透过本场比赛,发现本来自己并不在意的知识点出了好多题啊!!!

下一场努力吧!终须会时辰到,别怕


回忆这理想不够理想,沿途逛世间一趟只有向上