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':' ');
}
}