NWERC2024 AEJLDFKHM

传送门:2024-2025 ICPC Northwestern European Regional Programming Contest (NWERC 2024)

欧洲区域赛,希望以后有机会能打现场。

A - Alphabetical Aristocrats

签到题,根据题意模拟

#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();
    }
}

 

E - Evolving Etymology

小诈骗题,手玩了几次后发现每次下标的变化规律为: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();
    }
}

 

J - Jib Job

贪心,显然 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';
    }
}

 

L - Limited Library

二分答案+贪心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";
}

 

D - Dutch Democracy

有意思的小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;
}

 

F - Flowing Fountain

模拟,用单调栈预处理出"右边第一个比自己大的数",用并查集加速查找过程。

#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();
    }
}

 

K - Kruidnoten

虚假的概率论,真实的小清新图论

显然一条路径上经过多个有存货的商店是没用的,只需要一个。

考虑枚举路径上第一个有存货的商店为 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;
}

 

H - Hash Collision

很妙的题,想了好久没想到,一看题解:?!

Q:给定一个映射函数,可提问1000次(C , R),交互器会返回 f( f( f( f( ...f(R)... )))) (共迭代 C 次的值)。

求确定一个二元组(C , R)使得 f( f( f( f( ...f(R)... )))) (共迭代 C 次) = C。

A:

这道题告诉我们要勇于尝试,大胆赋值。

 

M - Mouse Trap

这也是虚假的概率,真实的计算几何

问题转化为在凸包上任取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;
}

 

C - Connect Five

需要注意力。

注意到对于最左边的点来说,最优路径肯定是从它开始往右延申的,同理上、下、右

最后会连成:

最后会连成一个矩形,特判内点。

没有ac code,因为某种神秘的原因至今仍wa on test 4..

 

posted @ 2024-12-06 18:41  liyishui  阅读(2)  评论(0编辑  收藏  举报