Gym - 101480 CERC 15:部分题目题解(队内第N次训练)

-------------------题目难度较难,但挺有营养的。慢慢补。

 

A .ASCII Addition

pro:用一定的形式表示1到9,让你计算加法。

sol:模拟。 

solved by fzl;

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
typedef long long ll;
string s[11] =
{
    "xxxxxx...xx...xx...xx...xx...xxxxxx",
    "....x....x....x....x....x....x....x",
    "xxxxx....x....xxxxxxx....x....xxxxx",
    "xxxxx....x....xxxxxx....x....xxxxxx",
    "x...xx...xx...xxxxxx....x....x....x",
    "xxxxxx....x....xxxxx....x....xxxxxx",
    "xxxxxx....x....xxxxxx...xx...xxxxxx",
    "xxxxx....x....x....x....x....x....x",
    "xxxxxx...xx...xxxxxxx...xx...xxxxxx",
    "xxxxxx...xx...xxxxxx....x....xxxxxx",
    ".......x....x..xxxxx..x....x......."
};
char Map[10][1000];
int judge(int x)
{
    string tmp;
    for(int i = 0; i < 7; i++)
        for(int j = x; j < x + 5; j++)tmp += Map[i][j];
    for(int i = 0; i <= 10; i++)if(s[i] == tmp)return i;
}
int main()
{
    for(int i = 0; i < 7; i++)cin >> Map[i];
    int n = strlen(Map[0]);
    ll cnt = 0, a;
    for(int i = 0; i < n; i += 6)
    {
        int tmp = judge(i);
        if(tmp == 10){a = cnt;cnt = 0;continue;}
        cnt = cnt * 10 + tmp;
    }
    a = a + cnt;
    stringstream ss;string tmp;
    ss << a;
    ss >> tmp;
    for(int i = 0; i < 7; i++)for(int j = 0; j < tmp.size(); j++)
    {
        for(int k = 5 * i; k < 5 * i + 5; k++)cout<<s[tmp[j] - '0'][k];
        if(j == tmp.size() - 1)cout<<endl;
        else cout<<".";
    }
    return 0;
}
View Code

 

B .Book Borders

pro:给定文本,假设每行最多放L个字母时,统计第一行的信息。

sol:模拟+二分即可。

solve by pb。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
string s[500010];
int a,b,cnt,ans,n,sum[500010]; 
template<class T>
inline void read(T&a){
    char c=getchar();
    for(a=0;(c<'0'||c>'9')&&c!='-';c=getchar());
    bool f=0;if(c=='-')f=1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())a=a*10+c-'0';
    if(f)a=-a;
}
int main(){
    ios::sync_with_stdio(false);
    while(cin.peek()!='\n')cin>>s[++n],sum[n]=sum[n-1]+s[n].size();
    cin>>a>>b;
    for(int i=a;i<=b;i++){
        int las=0,mid,l,r,now,hang=0;
        ans=0;
        while(las<n){
            l=las+1,r=n;
            while(l<=r){
                mid=(l+r)>>1;
                if(sum[mid]-sum[las]+mid-las-1<=i)now=mid,l=mid+1;
                else r=mid-1;
            }
            ans+=s[las+1].size();
            las=now;hang++;
        }
        printf("%d\n",ans+hang-1);
    }
    return 0;
}
View Code

 

D .Digit Division

pro:给定一个数字串,让你切割,使得切割后的每一部分都是M的倍数。

sol:保存前面切割的方案数,dp即可。 没mod 1e9+7,wa了一发。

solve by fcy。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
const int Mod=1e9+7;;
ll ans,mp; char c[maxn];
int main()
{
    mp=1;
    int N,M,x=0;
    scanf("%d%d%s",&N,&M,c+1);
    rep(i,1,N){
        x=x*10+c[i]-'0'; x%=M;
        if(x==0) ans=mp,(mp+=mp)%=Mod;
        else ans=0;
    }
    printf("%lld\n",ans%Mod);
    return 0;
}
View Code

 

F.Frightful Formula

pro:给定N*N矩阵,给出第一行第一列的初始值 f[i][1]和f[1][i],其他位置的满足f[i][j]=a*f[i][j-1]+b*f[i-1][j]+c;  求f[N][N];

sol:如果没有c,此题直接组合数可以做,但是有c,可能要fft或者其他多项式的算法。 比较麻烦。

我们可以想办法把c去掉,不过数学渣fcy好像不会,只能参考别人的。 待定系数法很好的解决了这个问题。

参考:https://blog.csdn.net/liufengwei1/article/details/78271574

 

H .Hovering Hornet

pro:三维平面里,给定一个5*5*5的立方体,内部含有一个1*1*1的骰子,现在有一只蜜蜂,问它看到的点数和的期望。

sol:由于二者都是处于水平位置,所以看到上面的点的概率是一定的。 而周围4个点的概率取决于底面的面积,直接用半平面交即可。

updated by fcy。

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=10010;
const double eps=1e-8;
struct point{
    double x,y;
    point(){}
    point(double xx,double yy):x(xx),y(yy){}
};
struct line{
    point a;//起点
    point p;//起点到终点的向量
    line(){}
    line(point aa,point pp):a(aa),p(pp){}
    double angle;
};
double dot(point a,point b){ return a.x*b.x+a.y*b.y;}
double det(point a,point b){ return a.x*b.y-a.y*b.x;}
point operator *(point A,double p){ return point(A.x*p,A.y*p);}
point operator +(point A,point B){return point(A.x+B.x,A.y+B.y);}
point operator -(point A,point B){return point(A.x-B.x,A.y-B.y);}
double getangle(point a){ return atan2(a.y,a.x);}
double getangle(line a){ return getangle(a.p);}
point llintersect(line A,line B)
{
    point C=A.a-B.a;
    double t=det(C,B.p)/det(B.p,A.p);
    return A.a+A.p*t;
}
point s[maxn]; line t[maxn],q[maxn]; int head,tail;
bool cmp(line a,line b){
    double A=getangle(a),B=getangle(b);
    point t=(b.a+b.p)-a.a;
    if(fabs(A-B)<eps) return det(a.p,t)>0.0;
    return A<B;
}
bool onright(line P,line a,line b)
{
    point o=llintersect(a,b);
    point Q=o-P.a;
    return det(Q,P.p)>0; //如果同一直线上不能相互看到,则>=0
}
point w[maxn]; int pcnt=0;
double halfplaneintersect(int N,line p)
{
    s[N+1]=s[1];
    rep(i,1,N) t[i].a=s[i],t[i].p=s[i+1]-s[i];
    t[++N]=p;
    sort(t+1,t+N+1,cmp);
    int tot=0;
    rep(i,1,N-1) {
        if(fabs(getangle(t[i])-getangle(t[i+1]))>eps)
          t[++tot]=t[i];
    }
    t[++tot]=t[N]; head=tail=0;
    rep(i,1,tot){
        while(tail>head+1&&onright(t[i],q[tail],q[tail-1])) tail--;
        while(tail>head+1&&onright(t[i],q[head+1],q[head+2])) head++;
        q[++tail]=t[i];
    }
    while(tail>head+1&&onright(t[head+1],q[tail],q[tail-1])) tail--;
    //while(tail>head+1&&onright(t[tail],q[head+1],q[head+2])) head++;加上了会wa6,不知道为什么
    pcnt=0; q[tail+1]=q[head+1];
    rep(i,head+1,tail)  w[++pcnt]=llintersect(q[i],q[i+1]);
    double res=0;
    rep(i,2,pcnt-1) res+=det(w[i]-w[1],w[i+1]-w[1]);
    return res/2;
}
void solve()
{
    double ans=0,area=5.0*5*5-1*1*1;
    rep(i,1,4) scanf("%lf%lf",&s[i].x,&s[i].y);
    ans+=6.0*halfplaneintersect(4,line(point(-0.5,0.5),point(1,0)))*5/area;
    ans+=5.0*5*5*4/area;
    ans+=4.0*halfplaneintersect(4,line(point(-0.5,-0.5),point(0,1)))*5/area;
    ans+=3.0*halfplaneintersect(4,line(point(0.5,0.5),point(0,-1)))*5/area;
    ans+=1.0*halfplaneintersect(4,line(point(0.5,-0.5),point(-1,0)))*5/area;
    printf("%.10lf\n",ans);
}
int main()
{
    solve();
    return 0;
}
View Code

 

I .Ice Igloos

pro:在500*500的二维平面上,给定N个圆。Q次询问,每次给出一个线段,问多少个圆和直线有交点。 N,Q<1e5

sol:由于平面并不大,所以我们可以暴力一点的做法。 即去找可能有交点是位置,然后看那个位置上的圆是否满足。

solved by fcy 我的做法是,枚举x坐标,然后求出这个x到直线最近的点,然后往两边找,复杂度~500*Q*常数。(比标程暴力一丢丢)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
double G[510][510];
struct point{
    double x,y;
    point(){}
    point(double xx,double yy):x(xx),y(yy){}
}A,B;
double det(point a,point b) {return a.x*b.y-a.y*b.x;}
double dot(point a,point b) {return a.x*b.x+a.y*b.y;}
double dist(point a,point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double ptoseg(point p)
{
    point Ap=point(p.x-A.x,p.y-A.y);
    point AB=point(B.x-A.x,B.y-A.y);
    point pB=point(B.x-p.x,B.y-p.y);
    if(dot(AB,Ap)<=0) return dist(A,p);
    if(dot(AB,pB)<=0) return dist(B,p);
    return fabs(det(Ap,AB)/dist(A,B));
}
int ans;
void get1(int x,int y)
{
    while(y>=1){
        double ty=1.0*y;
        double dis=ptoseg(point(1.0*x,ty));
        if(dis>1.0) break;
        if(G[x][y]>dis) ans++;
        y--;
    }
}
void get2(int x,int y)
{
    while(y<=500){
        double ty=1.0*y;
        double dis=ptoseg(point(1.0*x,ty));
        if(dis>1.0) break;
        if(G[x][y]>dis) ans++;
        y++;
    }
}
int main()
{
    int N,Q;
    scanf("%d",&N);
    rep(i,1,N){
         int x,y; double r;
         scanf("%d%d%lf",&x,&y,&r);
         G[x][y]=r;
    }
    scanf("%d",&Q);
    while(Q--){
        scanf("%lf%lf%lf%lf",&A.x,&A.y,&B.x,&B.y);
        if(A.x>B.x||(A.x==B.x&&A.y>B.y)) swap(A.x,B.x),swap(A.y,B.y); ans=0;
        if(A.x==B.x){
             for(int i=(int)A.y;i<=(int)B.y;i++)
                ans+=(G[(int)A.x][i]>0);
        }
        else if(A.y==B.y){
            for(int i=(int)A.x;i<=(int)B.x;i++)
                ans+=(G[i][(int)A.y]>0);
        }
        else {
            for(int i=max((int)A.x-1,1);i<=min((int)B.x+1,500);i+=1){
               double y=1.0*A.y;
               if(i!=A.x) y=A.y+1.0*(B.y-A.y)/(B.x-A.x)*(i-A.x);
               int t=(int)y;
               get1(i,t);
               get2(i,t+1);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 

J .Juice Junctions

pro:给定N点M边的无向图,保证没个点的度数不超过3。 现在求所有点对的最大流。N<3e3, M<4.5e3;

sol:开始以为是N*M的复杂度,枚举起点,然后dfs,发现由于方向不能确定,所有并不能实现。

updated by fcy。

由于度数<=3;所以我们枚举点对,然后去验证可能性(0,1,2,3)。

0:不连通;    1:连通;   2: 双连通; 3:删去任意一边任然是双连通。

0,1用并查集可以做; 2用tarjan做。 3的话,需要用点东西才行,std的做法是,枚举删去的边,然后保存所有点所在的scc,所以两个点的所有情况下scc都同,即满足,这里可以用hash做。

#include<bits/stdc++.h>
#define ll unsigned long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=100010;
const int seed1=131;
const int seed2=1331;
int Laxt[maxn],Next[maxn],To[maxn],cnt=1;
int q[maxn],top,dfn[maxn],low[maxn],scc[maxn],scc_cnt,times;
int fa[maxn],ans,del,hash1[maxn],hash2[maxn];
void add(int u,int v)
{
    Next[++cnt]=Laxt[u]; Laxt[u]=cnt; To[cnt]=v;
}
int find(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=find(fa[x]);
}
void dfs(int u,int f)
{
    q[++top]=u; dfn[u]=low[u]=++times;
    for(int i=Laxt[u];i;i=Next[i]){
        if(i==del*2||i==del*2+1||To[i]==f) continue;
        if(!dfn[To[i]]) {
            dfs(To[i],u);
            low[u]=min(low[u],low[To[i]]);
        }
        else low[u]=min(low[u],dfn[To[i]]);
    }
    if(dfn[u]==low[u]){
        scc_cnt++;
        while(1){
            int x=q[top--];
            scc[x]=scc_cnt;
            if(x==u) break;
        }
    }
}
int main()
{
    int N,M,u,v;
    scanf("%d%d",&N,&M);
    rep(i,1,N) fa[i]=i;
    rep(i,1,M){
        scanf("%d%d",&u,&v);
        fa[find(u)]=find(v);
        add(u,v); add(v,u);
    }
    rep(i,1,N)
     rep(j,i+1,N)
      if(find(i)==find(j)) ans++;

    rep(i,1,N)
      if(!dfn[i]) dfs(i,0);

    rep(i,1,N)
     rep(j,i+1,N)
      if(scc[i]==scc[j]) ans++;

    rep(i,1,M) {
        del=i; scc_cnt=0; times=0;
        rep(j,1,N) dfn[j]=scc[j]=0;
        rep(j,1,N) if(!dfn[j]) dfs(j,0);
        rep(j,1,N){
            hash1[j]=hash1[j]*seed1+scc[j];
            hash2[j]=hash2[j]*seed2+scc[j];
        }
    }
    rep(i,1,N)
     rep(j,i+1,N) if(hash1[i]==hash1[j]&&hash2[i]==hash2[j]) ans++;
    printf("%d\n",ans);
    return 0;
}
View Code

 

K .Kernel Knights

pro:给定二分图。每个点有个出度,现在让你找一种方案,选出其中一些点S,满足这些点之间没有边,而未选的点,至少有S中的点指向它,保证有解。

sol:首先,如果一个点没有入度,必选。 我们可以用topo排序得到这些点。 对于剩下的点,由于是二分图,我们选择其中一边即可。

solve by fzl&pb

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int maxn=2000010;
typedef long long ll;
int n,a[200010],du[200010];
queue<int>que;
bool s[200010],c[200010];
void dfs(int x,int y){
    if(y){
        c[x]=1;
        if(!s[a[x]]&&!c[a[x]])dfs(a[x],0);
    }
    else{
        s[x]=1;
        if(!s[a[x]]&&!c[a[x]])dfs(a[x],1);
    }
}
int main(){
    //freopen("1.in", "r", stdin);
    //freopen("1.out", "w", stdout);
    scanf("%d",&n);
    for(int i=1;i<=2*n;i++){
        scanf("%d",a+i);
        du[a[i]]++;
    }
    for(int i=1;i<=2*n;i++)
        if(!du[i])que.push(i);
    while(!que.empty()){
        int x=que.front();que.pop();
        //cout<<x<<" "<<c[x]<<endl;
        if(c[x])continue;du[a[x]]--;
        s[x]=1;
        if(!c[a[x]])c[a[x]]=1,du[a[a[x]]]--;
        if(!s[a[a[x]]] && !c[a[a[x]]] && !du[a[a[x]]])que.push(a[a[x]]);
    }
    for(int i=1;i<=2*n;i++)
        if(!s[i]&&!c[i])dfs(i,0);
    for(int i=1;i<=2*n;i++)
        if(s[i])printf("%d ",i);
    return 0;
}
View Code

 

posted @ 2019-04-08 16:22  nimphy  阅读(200)  评论(1编辑  收藏