2018 ICPC Asia Jakarta Regional Contest

题目传送门

题号 A B C D E F G H I J K L
状态 Ο . . Ο . . Ø Ø Ο Ο . Ο

Ο:当场

Ø:已补

.  :  待补

A. Edit Distance

Thinking:kk pai爷

Code:kk

  不能直接反转,比如"010101",直接反转后就变成"101010",右移一位,然后加个0就可以了。

  所以要先统计01的数量,如果0大于1,就全变成1,1大于0,就全变成0(从数量上的改变就大于s/2了),相等的话,就看首位是0还是1,取相反,后面和首位不一样就行(位置)。

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
#define fpn() freopen("simple.in","r",stdin)
#define rd read()
using namespace std;
const int maxn=2010;
typedef long long ll;
int a,b;
char s[maxn];
int main(){
    cin>>s+1;
    int n=strlen(s+1);
    for(int i=1;i<=n;i++)
    {
        if(s[i]=='0')a++;
        else b++;
    }
    if(a>b){
        for(int i=1;i<=n;i++)
        {
            printf("1");
        }
        puts("");
    }else if(a<b){
        for(int i=1;i<=n;i++)
        {
            printf("0");
        }
        puts("");
    }else{
        if(s[1]=='1'){
            printf("0");
            for(int i=2;i<=n;i++)
            {
                printf("1");
            }
            puts("");
        }else{
            printf("1");
            for(int i=2;i<=n;i++)
            {
                printf("0");
            }
            puts("");
        }
    }
} 
View Code

D. Icy Land

Thinking:kk

Code:pai爷

  首先我们考虑大一点的矩阵,对于$n*m$来说,中心的$(n-2)*(m-2)$的矩阵中,只要有冰地,这个冰地就必然到达不了,因为会直接划过去,所以我们要把中间这个矩阵直接变成"#",然后我们要确保外围的过道上有一个#能让我们进入中心区域。

  然后特殊考虑$2*n$的矩阵,还是中心的两行,只要上下有一个就可以了。

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
#define fpn() freopen("simple.in","r",stdin)
#define rd read()
using namespace std;
const int maxn=2010;
typedef long long ll;
int n,m,ans=0,flag;
char s[510][510];
void do1()
{
    for(int i=2;i<=m-1;i++)
      if(s[1][i]=='.') ans++;
}
void do2()
{
    for(int i=2;i<=n-1;i++)
      if(s[i][1]=='.') ans++;
}
void do3()
{
    for(int i=2;i<=n-1;i++)
      if(s[i][1]!='#'&&s[i][2]!='#') ans++;
}
void do4()
{
    for(int i=2;i<=m-1;i++)
      if(s[1][i]!='#'&&s[2][i]!='#') ans++;
}
void do5()
{
    for(int i=2;i<=n-1;i++)
      for(int j=2;j<=m-1;j++)
         if(s[i][j]=='.') ans++;
    flag=1;
    for(int i=2;i<=m-1;i++)
       if(s[1][i]=='#'||s[n][i]=='#') flag=0;
    for(int i=2;i<=n-1;i++)
       if(s[i][1]=='#'||s[i][m]=='#') flag=0;
    ans+=flag;
}
void work()
{
    if(n==1) do1(); 
    else if(m==1) do2(); 
    else if(m==2) do3(); 
    else if(n==2) do4();
    else do5();  
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
    work();
    printf("%d
",ans);
}
View Code

F. Popping Balloons

待补。 z

G. Go Make It Complete

补题:kk

  题意:给了一个无向图,要求你把这个无向图变成完全图,连边的条件是这条边的两个点的当前度数和大于等于k,问能变成完全图的最大的k是多少。

  思路:虽然AC了,但是感觉时间复杂度不太对。

  首先我们先把需要连的边处理出来,然后发现这个k是有可以二分的性质的,所以我们就二分k,每次把度数和大于等于k的边连上,更新度数,扫一遍这两个点连接的所有需要连的边,大于等于k的塞入队列,不断重复直到队列为空。类似拓扑排序。

  但是这样做的时间复杂度是$N3logn$,居然能过,,网上看到有人说可以用set维护边,但是细想感觉是不对的,所以比赛的时候,有这种看似过不了的算法,还是可以写一写的。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=510;
int mp[maxn][maxn],d[maxn],a[maxn][maxn],deg[maxn];
int n,m;
struct edge{
    int u,v;
}b[maxn*maxn];
int u,v,cnt;
bool judge(int k)
{
    memcpy(deg,d,sizeof(deg));
    memcpy(a,mp,sizeof(mp));
    queue<edge>q;
    for(int i=1;i<=cnt;i++)
    {
        int u=b[i].u,v=b[i].v;
        if(deg[u]+deg[v]>=k){
            a[u][v]=a[v][u]=1;
            q.push({u,v});
        }
    }
    while(!q.empty())
    {
        edge st=q.front();
        q.pop();
        int u=st.u,v=st.v;
        
        deg[u]++,deg[v]++;
        for(int i=1;i<=n;i++)
        {
            if(a[i][u]==0&&deg[i]+deg[u]>=k){
                q.push({i,u});
                a[i][u]=a[u][i]=1;
            }
            if(a[i][v]==0&&deg[i]+deg[v]>=k){
                q.push({i,v});
                a[i][v]=a[v][i]=1;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(deg[i]!=n-1)return false;
    }
    return true;
}
int main(){
    while(cin>>n>>m)
    {
        clr(mp,0);
        clr(d,0);
        cnt=0;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            mp[u][v]=mp[v][u]=1;
            d[u]++,d[v]++;
        }
        for(int i=1;i<=n;i++)
        {
            mp[i][i]=1;
            for(int j=i+1;j<=n;j++)
            {
                if(mp[i][j]==0){
                    b[++cnt].u=i,b[cnt].v=j;
                }
            }
        }
        int l=0,r=1000,mid,ans;
        while(l<r)
        {
            mid=(l+r)>>1;
            if(judge(mid)){
                ans=mid,l=mid+1;
            }else{
                r=mid;
            }
        }
        printf("%d
",ans);
    }
}
View Code

H. Lexical Sign Sequence

  训练的时候想的似乎是正解?不过电脑在刚J题,没时间写

补题:zz

       题意:给定一个只包含-1,0,1的数列,-1和1的位置的数不能改变,0的位置必须改变成-1或者1,并且使得改变后的数列满足k个条件,这k个条件都是一段区间里的数的和要大于等于某个数,如果这样的序列不存在就输出Impossible,否则输出字典序最小的序列。

       思路:可以根据初始的数列和每个给定的条件算出这k个区间每个区间最多有多少个-1(原本的-1不算),如果算出来的数量是小于0的,就肯定不存在;然后建立一个set,初始为空,根据最多能有都少个-1从小到大排序,枚举1到n位,枚举到第i位时,把不包含这个数的区间从set中删除,把起点为i的区间放到set里。如果原位子上的数为1或-1,就不动;如果set为空,这个位子上就放-1;如果set里有数,如果set里的第一个数大于0,就放-1,并且set里的数全减1,反之放1。

//#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string>
#include<math.h>
#include<cmath>
#include<time.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
#include<numeric>
#include<stack>
#include<bitset>
#include<unordered_map>
const int maxn = 0x3f3f3f3f;
const double EI = 2.71828182845904523536028747135266249775724709369995957496696762772407663035354594571382178525166427;
const double PI = 3.141592653589793238462643383279;
//#ifdef TRUETRUE
//#define gets gets_s
//#endif
using namespace std;
int c[100010], sum1[100010], sum2[100010], mp[100010], ans[100010];
struct s
{
    int a, b, c, d;
}z[100010];
struct ss
{
    int a, f, d,id;
}zz[200010];
struct sss
{
    int v, id;
    bool operator < (const sss &rhs) const
    {
        if (v == rhs.v)
        {
            return id < rhs.id;
        }
        return v < rhs.v;
    }
};
set<sss>st;
inline bool comp(ss a, ss b)
{
    if (a.a == b.a)
    {
        return a.f > b.f;
    }
    return a.a < b.a;
}
int main(void)
{
    //ios::sync_with_stdio(false);
    int n, k, i, tmp, n1, n2, x, wz, qq, pos;
    bool flag;
    while (~scanf("%d %d", &n, &k))
    {
        sum1[0] = 0;
        sum2[0] = 0;
        st.clear();
        memset(mp, 0, sizeof(mp));
        for (i = 1; i <= n; i++)
        {
            scanf("%d", c + i);
            if (c[i] == 0)
            {
                sum1[i] = sum1[i - 1];
                sum2[i] = sum2[i - 1];
            }
            else if (c[i] == -1)
            {
                sum1[i] = sum1[i - 1] + 1;
                sum2[i] = sum2[i - 1];
            }
            else
            {
                sum1[i] = sum1[i - 1];
                sum2[i] = sum2[i - 1] + 1;
            }
        }
        flag = false;
        for (i = 0; i < k; i++)
        {
            scanf("%d %d %d", &z[i].a, &z[i].b, &z[i].c);
            zz[i].a = z[i].a;
            zz[i].f = -1;
            zz[i].id = i;
            zz[i + k].a = z[i].b + 1;
            zz[i + k].f = 1;
            zz[i + k].id = i;
            n1 = sum1[z[i].b] - sum1[z[i].a - 1];
            n2 = sum2[z[i].b] - sum2[z[i].a - 1];
            x = z[i].c - (n2 - n1);
            wz = z[i].b - z[i].a + 1 - n1 - n2;
            if (x > wz)
            {
                flag = true;
            }
            else
            {
                z[i].d = (wz - x) / 2;
                zz[i].d = z[i].d;
                zz[i + k].d = z[i].d;
                //printf("z[i].d = %d
",z[i].d);
            }
        }
        if (flag)
        {
            printf("Impossible
");
            continue;
        }
        sort(zz, zz + k * 2, comp);
        qq = 0;
        pos = 0;
        /*for (i = 0;i < 2 * k;i++)
        {
            printf("   %d %d %d %d %d
",i,zz[i].a,zz[i].id,zz[i].d,zz[i].f);
        }*/
        for (i = 1; i <= n; i++)
        {
            while (pos < 2 * k && zz[pos].a == i)
            {
                if (zz[pos].f == -1)
                {
                    st.insert({ zz[pos].d + qq,zz[pos].id });
                    mp[zz[pos].id] = zz[pos].d + qq;
                }
                else
                {
                    //printf("     %d %d
", i, st.empty());
                    st.erase({ mp[zz[pos].id],zz[pos].id });
                    //printf("     %d %d
",i,st.empty());
                }
                pos++;
            }
            if (st.empty())
            {
                if (c[i] == 0)
                {
                    //printf("ii = %d
",i);
                    ans[i] = -1;
                }
                else
                {
                    //printf("iii = %d
", i);
                    ans[i] = c[i];
                }
            }
            else if (c[i] == 0 && (*st.begin()).v - qq > 0)
            {
                //printf("i = %d
",i);
                qq++;
                ans[i] = -1;
            }
            else if (c[i] == 0)
            {
                //printf("iiiii = %d
", i);
                ans[i] = 1;
            }
            else
            {
                //printf("iiii = %d
", i);
                ans[i] = c[i];
            }
        }
        for (i = 1; i <= n; i++)
        {
            printf("%d", ans[i]);
            if (i != n)
            {
                printf(" ");
            }
        }
        printf("
");
    }
    return 0;
}
View Code

I. Lie Detector

Thinking

Code:pai爷

  签到。

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<queue>
#include<vector>
#include<stack>
#define ll long long
#define maxn 4001000
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int n,p;
char s[100100][10];
ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void init()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%s",s[i]); 
}
void work()
{
    if(s[1][0]=='L') p=0;
    else p=1;
    for(int i=2;i<=n;i++)
       if(s[i][0]=='L') p=!p;
    if(p==0) printf("LIE
");
    else printf("TRUTH
");
}
int main()
{
    init();
    work();
}
View Code

J. Future Generation

题解:用F【I】【J】.c表示 处理到第i个字符串,能获得总长度为j的最小的子串(第i个字符串的子串),

      预处理q数组,每个字符串的子串。代码有点丑。

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
#define fpn() freopen("simple.in","r",stdin)
#define rd read()
using namespace std;
const int maxn=2010;
typedef long long ll;

struct node{
    char c[20];
    int l;
}q[20][40000];

struct nod{
    int p=0;
    char c[20];
}f[20][400];

int len[20],n,flag[20];
char s[20][20],c[20][20];

void work(int x)
{
    int l=strlen(s[x]),z=0;
    for(int i=1;i<=(1<<l)-1;i++)
    {
        len[x]++;z=0;
        for(int k=0;k<l;k++)
        {
            if((i&(1<<k))!=0)
            {
//                printf("i=%d k=%d
",i,k);
                q[x][len[x]].c[++z]=s[x][k];
            }
        }
        q[x][len[x]].l=z;
//        for(int j=1;j<=z;j++) printf("%c",q[x][len[x]].c[j]);
//        printf("
");
    }
}
void init()
{
    for(int i=1;i<=n;i++) work(i);
}
int check(int x,int j)
{
    for(int i=1;i<=x;i++)
       if(f[1][x].c[i]>q[1][j].c[i]) return 1;
       else if(f[1][x].c[i]<q[1][j].c[i]) return 0;
       return 0;
}
void dodo()
{
    for(int j=1;j<=len[1];j++) 
        {
            int len1=q[1][j].l;
            if(f[1][len1].p==0) 
            {
                f[1][len1].p=1;
                for(int k=1;k<=len1;k++) f[1][len1].c[k]=q[1][j].c[k];
            }
            else{
                if(check(len1,j))
                {
                    for(int k=1;k<=len1;k++) f[1][len1].c[k]=q[1][j].c[k];
                }
            }
        }
    for(int i=2;i<=n;i++)
       for(int j=1;j<=16*16;j++)
       {
          if(f[i-1][j].p!=0)
          {
                  for(int i1=1;i1<=16;i1++)
                      for(int j1=1;j1<=i1;j1++)
                          c[i1][j1]='z';
                  for(int i1=1;i1<=16;i1++) flag[i1]=1; 
                  for(int k=1;k<=len[i];k++) 
                  {
                      int l=q[i][k].l;
                      if(strncmp(&(q[i][k].c[1]),&(f[i-1][j].c[1]), l)>0)
                      {
                           if(strncmp(&(c[l][1]),&(q[i][k].c[1]), l)>0)
                           {
                                 flag[l]=0;
                                 for(int kk=1;kk<=l;kk++) c[l][kk]=q[i][k].c[kk];
                         }
                    }
                  }
                  for(int pq=1;pq<=16;pq++)
                        if(flag[pq]==0)
                        {
//                            printf("i=%d j=%d
",i,j);
//                            for(int kk=1;kk<=pq;kk++) printf("%c",c[pq][kk]);
//                            printf("
");
                            if(f[i][j+pq].p==0)
                            {
                            for(int kk=1;kk<=pq;kk++) 
                                 f[i][j+pq].c[kk]=c[pq][kk],f[i][j+pq].p=1;
                            }
                            else{
                                if(strncmp(&(f[i][j+pq].c[1]),&(c[pq][1]), pq)>0)
                                for(int kk=1;kk<=pq;kk++) 
                                 f[i][j+pq].c[kk]=c[pq][kk],f[i][j+pq].p=1;
                            }
                        
                        }
          }
       }
    int ans=-1;
    for(int i=1;i<=16*16;i++)
        if(f[n][i].p!=0) ans=i;
    printf("%d
",ans);
}
int main()
{
    freopen("1.txt","r",stdin);
    freopen("1.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%s",s[i]);
    init();
    dodo();
}
View Code

K. Boomerangs

待补 k

L:

  题意:给定一个只包含-1,0,1的数列,-1和1的位置的数不能改变,0的位置必须改变成-1或者1,并且使得改变后的数列满足k个条件,这k个条件都是一段区间里的数的和要大于等于某个数,如果这样的序列不存在就输出Impossible,否则输出字典序最小的序列。

       思路:可以根据初始的数列和每个给定的条件算出这k个区间每个区间最多有多少个-1(原本的-1不算),如果算出来的数量是小于0的,就肯定不存在;然后建立一个set,初始为空,根据最多能有都少个-1从小到大排序,枚举1到n位,枚举到第i位时,把不包含这个数的区间从set中删除,把起点为i的区间放到set里。如果原位子上的数为1或-1,就不动;如果set为空,这个位子上就放-1;如果set里有数,如果set里的第一个数大于0,就放-1,并且set里的数全减1,反之放1。

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
#define fpn() freopen("simple.in","r",stdin)
#define rd read()
using namespace std;
const int maxn=2010;
typedef long long ll;
char c[66];
long long k,len;
int v[66];
inline long long f(void)
{
    long long i,s = 0;
    for(i = 0;i < len;i++)
    {
        if(!v[i])
        {
            s *= 2;
            if(c[i] == '1')
            {
                s += 1;
            }
        }
    }
    return s;
}
int main(){
    long long i;
    int ans;
    while(~scanf("%lld",&k))
    {
        scanf("%s",c);
        len = strlen(c);
        memset(v,0,sizeof(v));
        ans = 0;
        while(1)
        {
            if(f() <= k)
            {
                break;
            }
            bool flag = true;
            for(i = 1;i < len;i++)
            {
                if(c[i] == '1' && v[i] == 0)
                {
                    v[i] = 1;
                    flag = false;
                    break;
                }
            }
            if(flag)
            {
                for(i = 1;i < len;i++)
                {
                    if(c[i] == '0' && v[i] == 0)
                    {
                        v[i] = 1;
                        break;
                    }
                }
            }
            ans++;
        }
        printf("%d
",ans);
    }
}
View Code

总结:

  kk:今天比赛中途吃了个外卖(下课食堂人太多了吧)。所以中间稍微耽搁了一下下,A题一眼想到假算法,发现wa了那么多,所以等了等,果然hack了假算法,和pai爷讨论后ac,吃外卖的时候看了d题,回来稍微画了画想到正解,pai爷写的代码。后面的题目和zz、pai爷分别讨论了两道题,一道题感觉时间复杂度不对,没写完就让电脑了,一道题和zz讨论的似乎是正解,时间不够了没写。今天主要是被代码实现能力卡住了,还有没有提早提醒队友卡题的时候看新题,本来H题应该是能做的。

  pai爷:喵喵喵?口胡选手?我好菜啊?字符串的操作不会?努力!

  zz:去晚了一点,写了个L,J写完以后发现搜索写崩了,还不如直接二进制枚举,H想了个贪心,没时间写了。

原文地址:https://www.cnblogs.com/mountaink/p/10472719.html