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(""); } } }
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\n",ans); }
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&°[i]+deg[u]>=k){ q.push({i,u}); a[i][u]=a[u][i]=1; } if(a[i][v]==0&°[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\n",ans); } }
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\n",z[i].d); } } if (flag) { printf("Impossible\n"); continue; } sort(zz, zz + k * 2, comp); qq = 0; pos = 0; /*for (i = 0;i < 2 * k;i++) { printf(" %d %d %d %d %d\n",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\n", i, st.empty()); st.erase({ mp[zz[pos].id],zz[pos].id }); //printf(" %d %d\n",i,st.empty()); } pos++; } if (st.empty()) { if (c[i] == 0) { //printf("ii = %d\n",i); ans[i] = -1; } else { //printf("iii = %d\n", i); ans[i] = c[i]; } } else if (c[i] == 0 && (*st.begin()).v - qq > 0) { //printf("i = %d\n",i); qq++; ans[i] = -1; } else if (c[i] == 0) { //printf("iiiii = %d\n", i); ans[i] = 1; } else { //printf("iiii = %d\n", i); ans[i] = c[i]; } } for (i = 1; i <= n; i++) { printf("%d", ans[i]); if (i != n) { printf(" "); } } printf("\n"); } return 0; }
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\n"); else printf("TRUTH\n"); } int main() { init(); work(); }
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\n",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("\n"); } } 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\n",i,j); // for(int kk=1;kk<=pq;kk++) printf("%c",c[pq][kk]); // printf("\n"); 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\n",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(); }
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\n",ans); } }
总结:
kk:今天比赛中途吃了个外卖(下课食堂人太多了吧)。所以中间稍微耽搁了一下下,A题一眼想到假算法,发现wa了那么多,所以等了等,果然hack了假算法,和pai爷讨论后ac,吃外卖的时候看了d题,回来稍微画了画想到正解,pai爷写的代码。后面的题目和zz、pai爷分别讨论了两道题,一道题感觉时间复杂度不对,没写完就让电脑了,一道题和zz讨论的似乎是正解,时间不够了没写。今天主要是被代码实现能力卡住了,还有没有提早提醒队友卡题的时候看新题,本来H题应该是能做的。
pai爷:喵喵喵?口胡选手?我好菜啊?字符串的操作不会?努力!
zz:去晚了一点,写了个L,J写完以后发现搜索写崩了,还不如直接二进制枚举,H想了个贪心,没时间写了。