NWERC2024 AEJLDFKHM
传送门:2024-2025 ICPC Northwestern European Regional Programming Contest (NWERC 2024)
欧洲区域赛,希望以后有机会能打现场。
签到题,根据题意模拟
#include<bits/stdc++.h> using namespace std; struct node{ string act; string com; }f[100000]; bool cmp(node a,node b){ return a.com<b.com; } void solve(){ int n;cin>>n; getchar(); for(int i=1;i<=n;i++){ string s; getline(cin,s); int len=s.length(),pos=-1; for(int j=0;j<len;j++){ if(s[j]<='Z'&&s[j]>='A'){ pos=j;break; } } string tmp=""; for(int j=pos;j<len;j++){ tmp=tmp+s[j]; } f[i].act=s; f[i].com=tmp; // cout<<"i= "<<i<<'\n'; // cout<<s<<" "<<tmp<<'\n'; } sort(f+1,f+n+1,cmp); for(int i=1;i<=n;i++) cout<<f[i].act<<'\n'; } int main(){ int t=1; while(t--){ solve(); } }
小诈骗题,手玩了几次后发现每次下标的变化规律为:i =(i*2)%n
所以快速幂求一下第k次后的下标为 i*(2^k)%n 即可
#include<bits/stdc++.h> using namespace std; #define int long long string s; int qpow(int a,int b,int mod){ int ret=1; while(b){ if(b&1) ret=ret*a%mod; a=a*a%mod; b>>=1; } return ret; } void solve(){ int n,k; cin>>n>>k; cin>>s; int base=qpow(2,k,n); for(int i=0;i<n;i++){ int at=i*base%n; cout<<s[at]; } } signed main(){ int t=1; while(t--){ solve(); } }
贪心,显然 hi 越高的塔架越不受限制,排序后优先处理 hi 大的
#include<bits/stdc++.h> using namespace std; const int N = 1e5+5; int a[N],b[N],r[N]; int main(){ int n; cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]>>b[i]>>r[i]; } for(int i=1;i<=n;i++){ int ret=1000100; for(int j=1;j<=n;j++){ if(i==j||r[j]<=r[i]) continue; int d=(a[i]-a[j])*(a[i]-a[j])+(b[i]-b[j])*(b[i]-b[j]); d=sqrt(d); ret=min(ret,d); } ret=min(ret,r[i]); cout<<ret<<'\n'; } }
二分答案+贪心check。发现能放艺术品的架子数具有单调性,于是考虑二分答案。
check部分:显然艺术品优先放高度小的架子是更优的,反证:如果放高度大的架子,可能导致有本书原来放得下,现在反而放不下了
#include<bits/stdc++.h> using namespace std; const int N = 1e5+5; int a[N],b[N],lef[N]; int n,m,x,y; int check(int mid){ for(int i=1;i<=n;i++){ if(i<=mid) lef[i]=y; else lef[i]=x; } int p=1; for(int i=1;i<=m;i++){ if(lef[p]>0&&a[p]>=b[i]){ lef[p]--; } else { while(lef[p]==0||a[p]<b[i]){ p++; if(p>n) break; } if(p>n) return 0; lef[p]--; } } return 1; } int main(){ ios_base::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>n>>m>>x>>y; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=m;i++) cin>>b[i]; sort(a+1,a+n+1); sort(b+1,b+m+1); int l=0,r=n,ans=-1; while(l<=r){ int mid=(l+r)>>1; if(check(mid)){ ans=mid; l=mid+1; } else r=mid-1; } if(ans!=-1) cout<<ans<<'\n'; else cout<<"impossible"; }
有意思的小dp。
Q:给定欧洲若干个政党目前的席位数,要求选出若干个政党组成一个联盟,满足:
1、Σ 联盟席位 > 总席位的一半
2、在联盟内去掉任意一个政党,都使得1、不满足。
求满足条件的联盟数量。
A:考虑强化两个条件,转化成:“在联盟内去掉最小值,使得 Σ 联盟席位 <= 总席位的一半” 。
枚举最小值,设最小值为mi,问题变成:选出若干子集,使得 Σ子集 + mi > 总席位的一半,Σ子集 < 总席位的一半。
n很小,且值域<=10000,可用01背包解决。
#include<bits/stdc++.h> #define int long long using namespace std; const int N = 65; int half,n,ans=0; int a[N],sum[N],dp[60*10005]; void solve(int len){ int mi=a[len]; len--; for(int j=0;j<=sum[n];j++) dp[j]=0; dp[0]=1; for(int i=1;i<=len;i++){ for(int j=sum[i];j>=0;j--){ if(j-a[i]>=0){ dp[j]+=dp[j-a[i]]; } } } //cout<<"len= "<<len<<'\n'; for(int i=0;i<=half+mi;i++){ if(i+mi>half&&i<=half) { // cout<<i<<" "<<half<<" "<<dp[i]<<'\n'; ans+=dp[i]; } } //cout<<ans<<'\n'; } bool cmp(int a,int b){ return a>b; } signed main(){ cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]; half=sum[n]/2; for(int i=1;i<=n;i++){ solve(i); } cout<<ans; }
模拟,用单调栈预处理出"右边第一个比自己大的数",用并查集加速查找过程。
#include<bits/stdc++.h> using namespace std; const int N=1e6+5; int a[N],L[N],pa[N],val[N]; int n,q,tot=0; int find(int x){ if(x!=pa[x]) return pa[x]=find(pa[x]); else return x; } void merge(int a,int b){ int fa=find(a),fb=find(b); if(fa>fb) swap(fa,fb); pa[fa]=fb; } void solve(){ cin>>n>>q; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) pa[i]=i; stack<int>s; for(int i=n;i>=1;i--){ while(!s.empty( )&&a[s.top( )]<=a[i]){ s.pop( ); } if(s.empty( )) L[i]=n+1; else L[i]=s.top( ); s.push(i); } while(q--){ char op;cin>>op; if(op=='+'){ int l,x;cin>>l>>x; // cout<<"666= "<<'\n'; int to=find(l);// the right most // cout<<"666"<<'\n'; while(x&&to<=n){ to=find(to); int d=min(a[to]-val[to],x); val[to]+=d; x-=d; tot+=d; if(a[to]==val[to]) { merge(to,l); to=L[to]; } // cout<<"to= "<<to<<'\n'; } } else { int l;cin>>l; cout<<val[l]<<'\n'; } } } int main(){ ios_base::sync_with_stdio(false); cin.tie(0);cout.tie(0); int t=1; while(t--){ solve(); } }
虚假的概率论,真实的小清新图论
显然一条路径上经过多个有存货的商店是没用的,只需要一个。
考虑枚举路径上第一个有存货的商店为 i,则1到 i 的最短路可求,n 到 i 的最短路可求,相加则是从1出发经过点 i 到达n的最短路径。
再考虑什么时候才考虑要去 i ,肯定是其他更近的商店都没存货了,这部分用前缀积预处理一下
#include<bits/stdc++.h> using namespace std; #define int long long const int maxn=1e6; double p[maxn]; int d[maxn][2],cnt=0,n,m,k,point[maxn],dis[maxn],vis[maxn],head[maxn]; struct node{ double p; int dis; }f[maxn]; bool cmp(node a,node b){ return a.dis<b.dis; } double sum[maxn]; struct Edge{ int to,w,nex; }edge[maxn]; struct lys{ int id,d; bool operator > (const lys tmp)const{ return d>tmp.d; } }; void add(int a,int b,int val){ cnt++;edge[cnt].to=b;edge[cnt].w=val;edge[cnt].nex=head[a];head[a]=cnt; } priority_queue<lys,vector<lys>,greater<lys> > q; void dij(int s){ memset(dis,0x7f,sizeof(dis)); memset(vis,0,sizeof vis); dis[s]=0; q.push((lys){s,0}); while(!q.empty()){ lys tmp=q.top(); q.pop(); if(vis[tmp.id]){continue;} vis[tmp.id]=1; for(int i=head[tmp.id];i;i=edge[i].nex){ if(dis[edge[i].to]>dis[tmp.id]+edge[i].w){ dis[edge[i].to]=dis[tmp.id]+edge[i].w; q.push((lys){edge[i].to,dis[edge[i].to]}); } } } } signed main(){ cin>>n>>m>>k; for(int i=1;i<=m;i++){ int u,v,c;cin>>u>>v>>c; add(u,v,c); add(v,u,c); } int flag=0; for(int i=1;i<=k;i++){ cin>>point[i]>>p[i]; if(p[i]==1) flag=1; } if(!flag){ cout<<"impossible"; return 0; } dij(1); for(int i=1;i<=n;i++) d[i][1]=dis[i]; dij(n); for(int i=1;i<=n;i++) d[i][2]=dis[i]; sum[0]=1; for(int i=1;i<=k;i++){ f[i].p=p[i]; f[i].dis=d[point[i]][1]+d[point[i]][2]; } sort(f+1,f+k+1,cmp); double ans=0; for(int i=1;i<=k;i++){ ans+=sum[i-1]*f[i].p*f[i].dis; sum[i]=sum[i-1]*(1-f[i].p); } cout<<fixed<<setprecision(10)<<ans; }
很妙的题,想了好久没想到,一看题解:?!
Q:给定一个映射函数,可提问1000次(C , R),交互器会返回 f( f( f( f( ...f(R)... )))) (共迭代 C 次的值)。
求确定一个二元组(C , R)使得 f( f( f( f( ...f(R)... )))) (共迭代 C 次) = C。
A:
这道题告诉我们要勇于尝试,大胆赋值。
这也是虚假的概率,真实的计算几何
问题转化为在凸包上任取3点,构成的三角形面积和。
想了很久不会做,看题解才知道面积可以用向量叉积求,且叉积有分配率,可以化简式子
最后求得的面积/多边形面积即可
但题解里有个笔误,是 (n-j) 不是 (n-1-j)
#include<bits/stdc++.h> using namespace std; const int N = 1e6+5; #define int long long struct point{ double x,y; }p[N]; int n; double prex[N],prey[N],sufx[N],sufy[N]; double q(point a,point b){ return 1.0*a.x*b.y-1.0*a.y*b.x; } double getarea(){ double sum = 0; for(int i = 1; i <= n ; i++){ int nxt=(i+1); if(i==n) nxt=1; sum += q(p[i],p[nxt]); } return fabs(sum)/2; } signed main(){ ios_base::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>n; for(int i=1;i<=n;i++) cin>>p[i].x>>p[i].y; for(int i=1;i<=n;i++){ prex[i]=prex[i-1]+p[i].x; prey[i]=prey[i-1]+p[i].y; } for(int i=n;i>=1;i--){ sufx[i]=sufx[i+1]+p[i].x; sufy[i]=sufy[i+1]+p[i].y; } __int128 ans=0; for(int i=1;i<=n;i++){ point a,b; a.x=(i-1)*p[i].x-prex[i-1]; a.y=(i-1)*p[i].y-prey[i-1]; b.x=sufx[i+1]-(n-i)*p[i].x; b.y=sufy[i+1]-(n-i)*p[i].y; ans+=q(a,b); // cout<<i<<" "<<q(a,b)<<'\n'; } double fz=ans*1.0/2; double fm=getarea(); double ret=fz/fm; // cout<<"the area= "<<fm<<'\n'; cout<<fixed<<setprecision(10)<<ret; }
需要注意力。
注意到对于最左边的点来说,最优路径肯定是从它开始往右延申的,同理上、下、右
最后会连成:
最后会连成一个矩形,特判内点。
没有ac code,因为某种神秘的原因至今仍wa on test 4..