【题解】电子科技大学第十八届 ACM 程序设计竞赛

毫无悬念地被大一爷锤爆了,一道难题也不会,摸了个第十

A - Rapport Test

线段树,主席树

B - Carving

二分

二分内切球半径(r),使得球心((r,r,r))到平面(frac{x}{a}+frac{y}{b}+frac{z}{c}=1)的距离为(r)

#include <cstdio>
#include <cmath>
using namespace std;
int a,b,c;
double l,r,mid,v,k1,k2,k3,eps=1e-9;
int main(){
    scanf("%d%d%d",&a,&b,&c);
    l=0,r=(double)a*b*c/(a*b+b*c+a*c);
    k1=a*b+b*c+a*c;
    k2=a*b*c;
    k3=sqrt(pow(a*b,2)+pow(b*c,2)+pow(a*c,2));
    while (r-l>eps){
        mid=(r+l)/2;
        if ((k2-mid*k1)/k3>mid)
            l=mid;else r=mid;
        
    }
    v=4.0/3.0*M_PI*pow(l,3);
    printf("%.2lf",v);
}

C - Rather Easy Problem

中国剩余定理

#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}
ll ex_gcd(ll a,ll b,ll &x,ll &y){
    if(b==0){
        x=1;y=0;
        return a;
    }
    ll ans=ex_gcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-a/b*y;
    return ans;
}
ll modeqset(ll *a,ll *b,ll n){
    ll i,d,c,x,y,t;
    for(i=1;i<n;i++){
        c=b[i]-b[i-1];
        d=ex_gcd(a[i-1],a[i],x,y);
        if(c%d) return -1;
        t=a[i]/d;
        x=(x*(c/d)%t+t)%t;
        b[i]=x*a[i-1]+b[i-1];
        a[i]=a[i-1]*(a[i]/d);
    }
    return b[n-1];
}
int main(){
    ll a[30],b[30],m,tmp,flag,i,n,lcm=1,num;
        lcm=1;
        scanf("%lld%lld",&n,&m);
        for(i=0;i<m;i++){
            scanf("%lld",&a[i]);
            lcm=lcm/(gcd(lcm,a[i]))*a[i];
        }
        for(i=0;i<m;i++) scanf("%lld",&b[i]);
        ll res=modeqset(a,b,m);
        if(res==-1|| res>n){
            printf("0
");
        }else{
            num=(n-res)/lcm+1;
            if(res==0) num--;
            printf("%lld
",num);
        }
    return 0;
}

D - Nearest Shelter

并查集,离散化

考虑到实际上只有(m)个点是有意义的,所以可以先对坐标进行离散化

再用并查集进行路径压缩,从而加快查询速度

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+2;
int n,m,op,pos,fa[N],tmp[N],dis[N];
struct L{
    int op,pos;
}l[N];
int find(int x){
    if (x==fa[x]) return x;
    return fa[x]=find(fa[x]);
}
int getid(int x){
    return lower_bound(tmp+1,tmp+m+2,x)-tmp;
}
int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m+1;i++) fa[i]=i;
    for (int i=1;i<=m;i++){
        scanf("%d%d",&l[i].op,&l[i].pos);
        tmp[i]=l[i].pos;
    }
    tmp[m+1]=n+1;
    sort(tmp+1,tmp+m+2);
    for (int i=1;i<=m;i++){
        int id=getid(l[i].pos),nextid=getid(l[i].pos+1);
        if (l[i].op==1){
            if (tmp[nextid]==l[i].pos+1)
                fa[id]=nextid;
            dis[id]=1;
        }else{
            int x=find(id);
            if (x==m+1){
                printf("-1
");
            }else{
                if (dis[x])
                    printf("%d
",tmp[x]+1);
                else
                    printf("%d
",tmp[x]);
            }
            
        }
    }
}

E - Didn't I Say to Make My Abilities Average in the Next Life?!

线段树

开两个线段树,一个维护区间最大值,另一个维护区间最小值

取查询结果的均值即可

#include <cstdio>
#include <iostream>
using namespace std;
const int N=1e5;
int n,m,a,op,cnt=0,q[N],qcnt=0;
struct MaxSTree{
    struct P{
        int l,r,v,tag;
    }t[4*N];
    int v[N];
     int build(int x,int l,int r){
         t[x].l=l,t[x].r=r,t[x].tag=0;
         if (l==r) return t[x].v=v[l];
         int mid=(l+r)/2;
         return t[x].v=max(build(x*2,l,mid),build(x*2+1,mid+1,r));
     }
     void pushdown(int x){
         if (!t[x].tag) return;
         t[x*2].v+=t[x].tag;
         t[x*2+1].v+=t[x].tag;
         t[x*2].tag+=t[x].tag;
         t[x*2+1].tag+=t[x].tag;
         t[x].tag=0;
     }
     int add(int x,int l,int r,int v){
         if (l==t[x].l&&r==t[x].r){t[x].tag+=v;return t[x].v+=v;}
         pushdown(x);
         int mid=(t[x].l+t[x].r)/2;
         if (r<=mid) return t[x].v=max(t[x*2+1].v,add(x*2,l,r,v));
         else if (l>=mid+1) return t[x].v=max(t[x*2].v,add(x*2+1,l,r,v));
         else return t[x].v=max(add(x*2,l,mid,v),add(x*2+1,mid+1,r,v));
     }
     int query(int x,int l,int r){
         if (l==t[x].l&&r==t[x].r) return t[x].v;
         pushdown(x);
         int mid=(t[x].l+t[x].r)/2;
         if (r<=mid) return query(x*2,l,r);
         else if (l>=mid+1) return query(x*2+1,l,r);
         else return max(query(x*2,l,mid),query(x*2+1,mid+1,r));
     }
 }st1,st2;
int main(){
    scanf("%d%d",&n,&m);
    for (int i=0;i<n;i++){
        scanf("%d",&op);
        if (op==1){
            scanf("%d",&a);
            st1.v[cnt]=a;
            st2.v[cnt]=-a;
            cnt++;
        }else{
           q[qcnt]=cnt;
           qcnt++;
        }
    }
    st1.build(1,0,cnt-1);
    st2.build(1,0,cnt-1);
    for (int i=0;i<qcnt;i++){
        double ans=(double)(st1.query(1,max(q[i]-m,0),q[i]-1)-st2.query(1,max(q[i]-m,0),q[i]-1))/2;
        printf("%.1lf
",ans);
    }
}

F - Fatdog 和他的拓扑排列

找规律,分类讨论

G - Fatdog Wants to Buy Games

并查集

容易看出答案是连通块个数减一

另外需要考虑没有任何人玩过游戏的特殊情况,这时答案是总人数

#include <cstdio>
const int N=2e5+1;
int n,m,k,u,v,fa[N],vis[N],ans=0;
int find(int x){
    if (x==fa[x]) return x;
    return fa[x]=find(fa[x]);
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n+m;i++) fa[i]=i;
    for (int i=1;i<=k;i++){
        scanf("%d%d",&u,&v);
        int fx=find(u),fy=find(n+v);
        if (fx!=fy) fa[fx]=fy;
    }
    for (int i=1;i<=n;i++){
        int fx=find(i);
        if (!vis[fx]){
            vis[fx]=1;
            ans++;
        }
    }
    if (k==0)
        printf("%d",n);
    else
        printf("%d",ans-1);
}

H - Moca 酱果然是天才

概率DP

I - 致十年以后的我,现在的你猜的是什么数?

阶乘

容易看出最优的询问方式是依次询问二进制的每一位,所以答案是(n)的排列数

(n)超过(10^6+3)时,因为计算过程中有模数因子,所以答案为(0)

#include <cstdio>
#define ll long long
const ll M=1e6+3;
ll n,ans=1,k=1;
int main(){
    scanf("%lld",&n);
    if (n>=M)
        ans=0;
    else
        for (ll i=1;i<=n;i++) ans=ans*i%M;
    printf("%lld",ans);
}

J - Kanade Loves Symmetry

递归,树状数组

依次处理从左到右的每个字符,只需要将同一种字符中最靠右侧的一个移动到与之相对称位置即可

如果当前处理的字符没有剩余可以与之配对的相同字符,则交换顺序从右向左处理

需要使用树状数组来维护每个元素在移动后的正确位置

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e5+1;
deque<int> q[26];
string s;
ll len,cnt[26],pd=0,ans,vis[N],t[N],l,r;
int lowbit(int x){
	return x&(-x);
}
void add(int x,int v){
	while(x<=len+1){
		t[x]+=v;
		x+=lowbit(x);
	}
}
int query(int x){
	int ans=0;
	while(x){
		ans+=t[x];
		x-=lowbit(x);
	}
	return ans;
}
int main(){
    cin>>s;
    len=s.length();
    l=0;r=len-1;
    for (ll i=0;i<len;i++){
        ll x=s[i]-'a';
        q[x].push_back(i);
        cnt[x]++;
    }
    for (ll i=0;i<26;i++){
        if (cnt[i]%2==1) pd++;
    }
    if (pd>1){
        printf("Impossible");
        return 0;
    }
    for (ll i=0;i<len/2;i++){
        while (vis[l]) l++;
        while (vis[r]) r--;
        ll lnow=i;
        ll rnow=len-i-1;
        ll x=s[l]-'a';
        if (q[x].size()>=2){
            ll mov=q[x].back();
            ll movnow=mov-query(mov+1);
            ans+=rnow-movnow;
            q[x].pop_front();q[x].pop_back();
            add(mov+1,1);
            vis[l]=1;vis[mov]=1;
        }else{
            x=s[r]-'a';
            ll mov=q[x].front();
            ll movnow=mov-query(mov+1);
            ans+=movnow-lnow;
            q[x].pop_front();q[x].pop_back();
            add(l+1,-1);add(mov+1,1);
            vis[r]=1;vis[mov]=1;
        }
    }
    printf("%lld",ans);
}

K - Senpai and Pascal

找规律

从第一个二进制不同的位开始依次填写(1)即可

#include <cstdio>
#define ll long long
ll a,b,c;
ll l[63],r[63],x[63];
int main(){
    scanf("%lld%lld",&a,&b);
    for (ll i=0;i<63;i++) if (a&(1LL<<i)) l[i]++;
    for (ll i=0;i<63;i++) if (b&(1LL<<i)) r[i]++;
    for (ll i=62;i>=0;i--){
        if (l[i]==r[i]){
            c+=(l[i]<<i);
        }else{
            c+=(1LL<<(i+1))-1;
            break;
        }
    }
    printf("%lld
",c);
}

L - UESTCPC Finals 2077

最大值

排除掉第一名做过的题目后在剩余的题目中找通过人数最多的即可

#include <bits/stdc++.h>
using namespace std;
int n,a[13],b[13],ans,maxx=0;
string f;
int main(){
    cin>>n;
    cin>>f;
    for (int i=0;i<n;i++) b[f[i]-'A']=1;
    for (int i=0;i<13;i++) cin>>a[i];
    for (int i=0;i<13;i++){
        if (a[i]>maxx&&!b[i]){
            ans=i;
            maxx=a[i];
        }
    }
    printf("%c
",'A'+ans);
}

M - 查理不冲浪!

自动机

手写自动机进行模式匹配

#include <bits/stdc++.h>
using namespace std;
const int R=10,C=1e5;
int r,c;
char a[R][C],ans[C];
void dfs(int y,int x){
    a[y][x]='.';
    if (y-1>=0&&a[y-1][x]=='o') dfs(y-1,x);
    if (y+1<r&&a[y+1][x]=='o') dfs(y+1,x);
    if (x-1>=0&&a[y][x-1]=='o') dfs(y,x-1);
    if (x+1<c&&a[y][x+1]=='o') dfs(y,x+1);
}
int main(){
    scanf("%d%d",&r,&c);
    for (int i=0;i<r;i++){
        string f;
        cin>>f;
        for (int j=0;j<c;j++)
            a[i][j]=f[j];
    }
    for (int i=0;i<c;i++){
        for (int j=0;j<r;j++){
            if (a[j][i]=='o'){
                if (a[j+1][i]=='o'&&a[j+2][i]=='o'&&a[j+3][i]=='o'&&a[j+4][i]=='o'){
                    if (a[j][i+1]=='.'){
                        ans[i]='1';
                    }
                    else if (a[j+2][i+1]=='.'){
                        ans[i+1]='0';
                    }
                    else if (a[j+1][i+2]=='o'){
                        ans[i+1]='8';
                    }else{
                        ans[i+1]='6';
                    }
                }
                else if (a[j][i+1]=='.'){
                    ans[i+1]='4';
                }
                else if (a[j+2][i+1]=='.'){
                    ans[i+1]='7';
                }
                else if (a[j+1][i]=='.'&&a[j+2][i]=='o'&&a[j+3][i]=='o'&&a[j+4][i]=='o'){
                    ans[i+1]='2';
                }
                else if (a[j+1][i]=='.'&&a[j+2][i]=='o'&&a[j+3][i]=='.'&&a[j+4][i]=='o'){
                    ans[i+1]='3';
                }
                else if (a[j+1][i]=='o'&&a[j+2][i]=='o'&&a[j+3][i]=='.'&&a[j+4][i]=='o'){
                    if (a[j+1][i+2]=='o'){
                        ans[i+1]='9';
                    }else{
                        ans[i+1]='5';
                    }
                }
                dfs(j,i);
            }
        }
        if (ans[i]) printf("%c",ans[i]);
    }
}

N - The Journey of Elaina

DFS序,树状数组

参考DFS序七个经典问题,本题目对应的问题是单点修改、子树和查询

#include <cstdio>
const int N=1e5+1;
int n,f[N],p[N],q,op,c,w,t[N],cnt=0,idx1[N],idx2[N];
int sz,head[N],vis[N];
struct E{
    int next,to;
}e[N*2];
int lowbit(int x){
	return x&(-x);
}
void add(int x,int v){
	while(x<=n){
		t[x]+=v;
		x+=lowbit(x);
	}
}
int sum(int x){
	int ans=0;
	while(x){
		ans+=t[x];
		x-=lowbit(x);
	}
	return ans;
}
void insert(int a,int b){
    sz++;
    e[sz].next=head[a];
    head[a]=sz;
    e[sz].to=b;
}
void dfs(int x){
    vis[x]=1;
    idx1[x]=++cnt;
    for (int i=head[x];i;i=e[i].next){
        int v=e[i].to;
        if (!vis[v]) dfs(v);
    }
    idx2[x]=cnt;
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&f[i]);
    for (int i=2;i<=n;i++){
        scanf("%d",&p[i]);
        insert(i,p[i]);
        insert(p[i],i);
    }
    dfs(1);
    for (int i=1;i<=n;i++){
        add(idx1[i],f[i]);
    }
    scanf("%d",&q);
    for (int i=1;i<=q;i++){
        scanf("%d",&op);
        if (op==1){
            scanf("%d%d",&c,&w);
            add(idx1[c],w-f[c]);
            f[c]=w;
        }else{
            scanf("%d",&c);
            printf("%d
",sum(idx2[c])-sum(idx1[c]-1));
        }
    }
}

O - Fatdog's True Love

二分,容斥

P - UNIX 时间戳

差分约束

原文地址:https://www.cnblogs.com/algonote/p/14060246.html