2020 CCPC Wannafly Winter Camp Day2 解题报告

A


计算每个元音对所包含它的区间的贡献,把这些贡献相加除以所以的的区间就是答案。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const int N=1e6+7;

double sum[N],f[N];
char s[N];

int main(){
	scanf("%s",s+1);
	int len=strlen(s+1);
	for(int i=1;i<=len;i++){
		if(s[i]=='a'||s[i]=='e'||s[i]=='i'||s[i]=='o'||s[i]=='u'||s[i]=='y'){
			sum[i]=sum[i-1]+1;
		}else sum[i]=sum[i-1];
	}

	double Ans=0;
	int i;
	for(i=1;i<=len/2;i++){
		if(i==1) f[i]=sum[len];
		else f[i]=f[i-1]+sum[len-i+1]-sum[i-1];
		Ans+=f[i]/(double)i;
	}
	for(;i<=len;i++){
		f[i]=f[len-i+1];
		Ans+=f[i]/(double)i;
	}

	Ans/=1.0*((double)(1.0*len*(1.0*len+1.0))/2.0);

	printf("%.9lf\n",Ans);

}

B


由nim和的性质可知要把当前局面变成先手必败态,需要选定某一堆拿去一些石子。设当前亦或和为\(x\),相当于求有多少个\(y\)满足\(y>x\ xor\ y\),这样就能将y变为\(x^y\)来使得异或和为0。满足这样条件的\(y\)一定在是\(x\)最高为1的二进制位也是1的\(y\)


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const int N=1e5+7;

int n;
ll a[N],x=0;
int cnt[67];

int main(){
	n=input();
	for(int i=1;i<=n;i++)
		a[i]=input();

	for(int i=1;i<=n;i++){
		for(int j=60;j>=0;j--){
			if(a[i]&(1ll<<j)) cnt[j]++;
		}
		x^=a[i];
		int res=0;
		for(int j=60;j>=0;j--){
			if(x&(1ll<<j)){
				res=cnt[j];
				break;
			}
		}
		printf("%d\n",res);
	}
}

E


树上启发式合并set。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

#define pb push_back

const int N=1e5+7;

vector <int> G[N];
set <int> s[N];
ll Ans[N];

void dfs(int u,int fa){
	s[u].clear();s[u].insert(u);Ans[u]=0;
	for(auto v:G[u]){
		if(v==fa) continue;
		dfs(v,u);
		if(s[v].size()>s[u].size()) swap(s[v],s[u]),Ans[u]=Ans[v];
		for(auto &iter:s[v]){
			auto pos=s[u].lower_bound(iter);
			if(pos==s[u].end()){
				auto tmp=pos;--tmp;
				Ans[u]+=1ll*(iter-*tmp)*(iter-*tmp);
			}else if(pos==s[u].begin()){
				Ans[u]+=1ll*(iter-*pos)*(iter-*pos);
			}else{
				auto tmp=pos;--tmp;
				Ans[u]-=1ll*(*pos-*tmp)*(*pos-*tmp);
				Ans[u]+=1ll*(iter-*pos)*(iter-*pos);
				Ans[u]+=1ll*(iter-*tmp)*(iter-*tmp);
			}
			s[u].insert(iter);
		}
		s[v].clear();
	}
}

int main(){
	int n=input();
	for(int i=2;i<=n;i++){
		int v=input();
		G[i].pb(v);
		G[v].pb(i);
	}

	dfs(1,0);

	for(int i=1;i<=n;i++){
		printf("%lld\n",Ans[i]);
	}

}

K


DP,直接转移时间复杂度显然过不了,考虑再AC自动机上转移就减少为根号级别,就可以过了。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const int N=5e5+7;
const ll INF=0x3f3f3f3f3f3f3f3f;
int n;

ll dp[N],val[N];

namespace AC{
	int tr[N][26],tot,dep[N];
	int fail[N];
	void Ins(char *s,ll w){
		int u=0;
		for(int i=1;s[i];i++){
			if(!tr[u][s[i]-'a'])tr[u][s[i]-'a']=++tot;
			u=tr[u][s[i]-'a'];
		}
		val[u]=min(val[u],w);
		dep[u]=strlen(s+1);
	}

	void build(){
		queue<int> q;
		for(int i=0;i<26;i++)
			if(tr[0][i]) q.push(tr[0][i]);
		while(!q.empty()){
			int u=q.front();q.pop();
			for(int i=0;i<26;i++){
				if(tr[u][i])
					fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]);
				else tr[u][i]=tr[fail[u]][i];
			}
		}
	}

	void query(char *t){
		int u=0;
		for(int i=1;t[i];i++){
			u=tr[u][t[i]-'a'];
			for(int j=u;j;j=fail[j]){
				if(dep[j])
					dp[i]=min(dp[i],dp[i-dep[j]]+val[j]);
			}
		}
		if(dp[strlen(t+1)]>=INF) printf("-1\n");
		else printf("%lld\n",dp[strlen(t+1)]);
	}
}
void init(){
	for(int i=0;i<N;i++)
		dp[i]=val[i]=INF;
	dp[0]=val[0]=0;
}

char s[N];
int main(){
	n=input();init();
	for(int i=1;i<=n;i++){
		ll w;
		scanf("%s%lld",s+1,&w);
		AC::Ins(s,w);
	}
	scanf("%s",s+1);
	AC::build();
	AC::query(s);
	return 0;
}

H


直接抄原题解:

考虑\(k\)个点的完全图,若\(k\)为奇数,则存在一条欧拉回路。若\(k\)为偶数,同理至少把\(k-2\)个点补足\(\frac{k-2}{2}\)条边,使得除\(2\)点外任意一点度数都为偶数才会存在欧拉路径。

因此,通过二分/小心枚举得到最大的\(m\),之后建图跑欧拉回路。


#include <bits/stdc++.h>

using namespace std;

#define ll long long
ll input(){
	ll x=0,f=0;char ch=getchar();
	while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return f? -x:x;
}

const int N=4e3+7;
int G[N][N],cur[N];
int Ans[N*N],cnt;
ll n,m;

void solve(){
	int x=1;cnt=0;
	while(x<=m){
		Ans[cnt++]=x;
		while(cur[x]<=m&&G[x][cur[x]]==0) cur[x]++;
		G[x][cur[x]]--;G[cur[x]][x]--;
		x=cur[x];
	}
}

int main(){
	n=input();
	ll l=1,r=3ll*sqrt(n)+1;
	while(l<r){
		ll mid=(l+r)/2,tmp=0;
		if(mid&1) tmp=mid*(mid-1)/2+1;
		else tmp=mid*mid/2;
		if(tmp>n) r=mid;
		else l=mid+1;
	}
	m=l-1;
	printf("%lld\n",m);
	if(n>2000000) return 0;
	for(int i=1;i<=m;i++){
		for(int j=1;j<=m;j++) G[i][j]=i==j?0:1;
	}
	if(m%2&&m>1) G[1][m]--,G[m][1]--;
	else{
		for(int i=3;i<=m;i+=2)
			G[i][i-1]++,G[i-1][i]++;
	}
	for(int i=1;i<=m;i++) cur[i]=1;
	solve();
	while(cnt<n) Ans[cnt++]=1;
	for(int i=0;i<cnt;i++){
		printf("%d%c",Ans[i],i==cnt-1? '\n':' ');
	}
}
posted @ 2020-02-19 17:02  _aether  阅读(246)  评论(0编辑  收藏  举报