【第3次区域赛训练赛】最终做了6题,完全是因为这场比赛水的缘故。。。赛后又补了个题,现在来写一下这7题的题解,总结一下。

A - Parentheses】https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5476

【题意】括号匹配问题,求最少的翻转(左右括号转换)操作使得原串合法.

【解题方法】用栈来模拟即可。栈模拟判断当前串是否合法:①. 如果当前是'(',直接入栈.②. 如果当前是')',如果栈非空,则弹出一个'('; 如果栈空就把当前的')'变成'('入栈.最后的结果栈中肯定全是'(',那么把其中的一半变成')'即可.

【AC 代码】

B - Linear】 Ecosystem https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5477 

【题意】对一个k元向量, 每次左乘一个k*k的矩阵得到新的向量.问经过一定次数的左乘后,能否使得该向量不再变化. (同时要求此时向量非零)

【解题方法】设初始向量为A,矩阵为P.由于每次矩阵P都是左乘A, 那么可以把若干个P合并. 则题目的条件是:化简为: 由于要求 所以 P-1 必须不可逆.可以直接用高斯消元求P-1的秩,判断是否可逆(满秩即可逆).

【AC 代码】

#include <math.h>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
const double eps=1e-10;
const int maxn = 500;
double a[maxn][maxn];
int guass(int n)
{
    int res=0,r=0;
    for(int i=0; i<n; i++)
    {
        for(int j=r; j<n; j++) if(fabs(a[j][i])>eps)
        {
            for(int k=i; k<n; k++) swap(a[j][k],a[r][k]);
            break;
        }
        if(fabs(a[r][i])<eps){
            res++;continue;
        }
        for(int j=0; j<n; j++) if(j!=r&&fabs(a[j][i])>eps)
        {
            double tmp=a[j][i]/a[r][i];
            for(int k=i; k<n; k++) a[j][k]-=tmp*a[r][k];
        }
        r++;
    }
    return res;
}

int main()
{
    int n;
    int T,cas=0;
    scanf("%d",&T);
    while(T--)
    {
        ++cas;
        scanf("%d",&n);
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++){
                scanf("%lf",&a[i][j]);
            }
            a[i][i]-=1.0;
        }
        int ret=guass(n);
        if(ret) printf("1");
        else printf("0");
        if(cas==5)
        {
            if(T) printf("\n");
            cas=0;
        }
        else if(T){
            printf(" ");
        }
    }
    printf("\n");
    return 0;
}

C - Least Crucial Node】 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5478

【题意】求标号最小的最大割点.(删除该点后,指定点#sink能到达的点数减少最多).

【解题方法】大概是求割点,维护size,判断一下就好,队友写的,我不会图论(甩锅),不过貌似很简单的样子QAQ

【AC 代码】

#include<bits/stdc++.h>
using namespace std;
const int N= 110;
vector<int>v[N];
int vis[N],dfn[N],low[N],cut[N],bridge[N][N],sz[N];
void dfs(int u,int fa,int dep)
{
    vis[u]=1;
    dfn[u]=low[u]=dep;
    int children=0;
    for(int i=0;i<v[u].size();i++){
        int t=v[u][i];
        if(t!=fa&&vis[t]==1){
            if(dfn[t]<low[u]){
                low[u]=dfn[t];
            }
        }
        if(vis[t]==0){
            dfs(t,u,dep+1);
            children++;
            if(low[t]<low[u]) low[u]=low[t];
            if((fa==-1&&children>1)||(fa!=-1&&low[t]>=dfn[u])){
                cut[u]++;
            }
            if(low[t]>dfn[u]){
                bridge[u][t]=bridge[t][u]=1;
            }
        }
    }
    vis[u]=2;
}
void dfs1(int u)
{
    vis[u]=1;
    sz[u]=1;
    for(int i=0;i<v[u].size();i++){
        int ed=v[u][i];
        if(vis[ed]) continue;
        dfs1(ed);
        sz[u]+=sz[ed];
    }
}
int main()
{
    int n,m,x,y,st;
    while(~scanf("%d",&n)&&n){
    memset(bridge,0,sizeof(bridge));
    memset(vis,0,sizeof(vis));
    memset(cut,0,sizeof(cut));
    scanf("%d%d",&st,&m);
    for(int i=1;i<=n;i++) v[i].clear();
    while(m--){
        scanf("%d%d",&x,&y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    dfs(st,-1,0);
    memset(vis,0,sizeof(vis));
    memset(sz,0,sizeof(sz));
    dfs1(st);
    int ans,maxx=0;
    for(int i=1;i<=n;i++){
       // if(cut[i]) printf("%d\n",i);
        if(sz[i]>maxx&&cut[i]){
            maxx=sz[i];
            ans=i;
        }
    }
    printf("%d\n",ans);
    }
    return 0;
}

D - Discrete Logarithm Problem】 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5479

【题意】<big>求一个x使得 a^x%p = b p为素数</big>

【解题方法】快速幂模拟一下就行啦,也可以暴力模拟,雀巢原理嘛。

【AC 代码】

G - Maximum Cut Order】https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5482

【题意】大概读懂题,理解了,这题就完全是水题了。

【解题方法】不说了,代码很清楚了。

【AC代码】

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N =6e5;
int vis[N],ans[N],cnt,n,k;
struct node
{
    int x,y;
    friend bool operator < ( const node &a,const node &b)
    {
        if(a.y==b.y) return a.x>b.x;
        else return a.y<b.y;
    }
};
priority_queue<node>q;
void dfs(int u)
{
    ans[cnt++]=u;
    vis[u]=1;
    node t;
    if(u*2<=n&&vis[2*u]==0){
        t={2*u,u%k};
        q.push(t);
    }
    if((2*u+1)<=n&&vis[2*u+1]==0){
        t={2*u+1,(u+1)%k};
        q.push(t);
    }
    if(u/2>0&&vis[u/2]==0){
        t={u/2,(u-u/2)%k};
        q.push(t);
    }
    if(q.empty()) return ;
    t=q.top();
    q.pop();
    dfs(t.x);
}
int main()
{
    int T,st;
    scanf("%d",&T);
    while(T--){
        memset(vis,0,sizeof(vis));
        cnt=1;
        scanf("%d%d%d",&n,&st,&k);
        dfs(st);
        for(int i=1;i<=n;i++){
            if(i==1) printf("%d",ans[i]);
            else printf(" %d",ans[i]);
        }
        printf("\n");
    }
    return 0;
}

H - Separating Pebbles】 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5483

【题意】给出平面上的两类点,判断是否能画一条直线将两类点完全分割开来.

【解题方法】N*N*N暴力即可。

【AC 代码】

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 255;
struct point{
    int x,y,z;
    void read()
    {
        scanf("%d%d%d",&x,&y,&z);
    }
    bool operator<(const point &rhs) const{
        if(x==rhs.x) return y<rhs.y;
        return x<rhs.x;
    }
}a[maxn];
point subt(point a,point b)
{
    return point{a.x-b.x,a.y-b.y,a.z-b.z};
}
int xmult(point a,point b)
{
    return a.x*b.y-a.y*b.x;
}
int Cross(point a,point b,point c)//判断在直线的哪一侧
{
    return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
int cnt1,cnt2,n;
bool solve()
{
    bool jud=false;
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(i==j) continue;
            int s1=0,s2=0;
            for(int k=1; k<=n; k++)
            {
                if(Cross(a[i],a[j],a[k])>0&&a[k].z==0) s1++;
                else if(Cross(a[i],a[j],a[k])>0&&a[k].z==1) s2++;
                else if(Cross(a[i],a[j],a[k])==0&&a[k].z==0) s1++;
                else if(Cross(a[i],a[j],a[k])==0&&a[k].z==1) s2++;
            }
            if(s1==cnt1&&s2==0) jud=true;
            if(s1==0&&s2==cnt2) jud=true;
        }
    }
    return jud;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        cnt1=0,cnt2=0;
        for(int i=1; i<=n; i++)
        {
            a[i].read();
            if(a[i].z==0) cnt1++;
            else cnt2++;
        }
        sort(a+1,a+n+1);
        bool ***=false;
        point x=subt(a[1],a[2]);
        for(int i=2; i<=n; i++)
        {
            if(xmult(x,subt(a[1],a[i]))!=0) ***=true;
        }
        if(***==false)
        {
            int s1,s2;
            bool fff1,fff2;
            if(a[1].z==0) s1=0,fff1=1;
            else s2=0,fff2=1;
            for(int k=1; k<=n; k++)
            {
                if(fff1&&a[k].z==0) s1++;
                else break;
            }
            for(int k=1; k<=n; k++)
            {
                if(fff2&&a[k].z==1) s2++;
                else break;
            }
            if(s1==cnt1||s2==cnt2)
            {
                printf("1\n");
            }
            else
            {
                printf("0\n");
            }
        }
        else{
            bool jud=solve();
            if(jud) printf("1\n");
            else printf("0\n");
        }
    }
    return 0;
}

K - Robots】 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=5486

【题意】有一个基点和n个A机器m个B机器。A机器传输时间为x,B机器传输时间为y(x<y),现在要把所有机器上的信息汇总传输给基点。而且同一时刻,每个机器只能传给另一个机器或者接受来自另一个机器的传输。基点一次只能接收一个机器的信息。求最小传输时间。

【解题方法】首先B机器要满,所以首先要把B机器都传出去。先选择一个机器传给基点,剩下的机器都传输给A机器(同时进行)。如果此时A机器还有空闲的,那么就两两互传,把信息整合到一台机器上。就是消耗时间的时候尽可能的调动所有可用的机器传输。

【PS】这道题是我今天赛后补得。

【AC 代码】

【ACMER 万岁】