题目链接:http://codeforces.com/contest/1138
A. Sushi for Two
解题思路:
利用"缩点"变成1,2,1,2...或者2,1,2,1...的模式然后每个点都有权值,权值就是原序列相同连续段的总数。最后枚举相邻的两个取max就好了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
const int mx = 1e5 + 10;
struct node
{
int a,k;
}s[mx];
int main(){
int n,m = 1;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&s[i].a),s[i].k = 1;
for(int i=2;i<=n;i++){
if(s[i].a==s[m].a) s[m].k++;
else s[++m] = s[i];
}
int ans = 0;
for(int i=2;i<=m;i++) ans = max(ans,2*min(s[i-1].k,s[i].k));
printf("%d\n",ans);
return 0;
}
B. Circus
解题思路:
设(1,0)的有x人,(0,1)的有y人,那么当(y或者x)>n/2时,肯定是无解的。这个很好证明。
那么当x>=y时:
我们暂时把(1,0)都分配参加一次场表演,把(0,1)都分配给第二场,那么当前第一场愿意且参加的有x人,第二场有y人。
剩下的就是考虑(1,1)和(0,0)的情况了。设(1,1)有z个人,因为x<=y,所以有:
当z+x>=y时:
在z中取w个人,使得x+w==y,这样当前两场愿意且参加的人数暂时一样了,然后剩下z-w个(1,1),如果它是偶数那么就好办了,直接对分就可以了,最后再对分(0,0)就完事了。但如果他是奇数,我们就可以把剩一个(1,1)扔到第二场,把一个(0,1)换到第一场就好了。
当z+x<y时:
那么就让第一场愿意且参加得人是x+z,就是(1,0)和(1,1)都去第一场,那么第二场当然也留下x+z个人(0,1),剩下y - (x+z)个(9,1)都去第一场就好了,因为y<=n/2所以肯定是可以办到的。
同理当x>=y时,处理方法是一样的。只不过对象对调而已。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
const int mx = 5e3 + 10;
int wa[mx],wb[mx],pa,pb;
int one[mx],fi[mx],zer[mx],se[mx];
char str[mx];
bool vis[mx];
int main(){
int n,x = 0,y = 0,z = 0,w = 0;
bool ok = 0;
scanf("%d",&n);
scanf("%s",str);
for(int i=1;i<=n;i++) wa[i] = str[i-1] - '0';
scanf("%s",str);
for(int i=1;i<=n;i++) wb[i] = str[i-1] - '0';
int *f = fi,*s = se;
for(int i=1;i<=n;i++){
if(wa[i]&&wb[i]){
one[z++] = i;
}else if(wa[i]){
fi[x++] = i;
}else if(wb[i]){
se[y++] = i;
}else zer[w++] = i;
}
if(x+y==0&&(z&1)) return 0*puts("-1");
if(x+y==0){
for(int i=0;i<z/2;i++) vis[one[i]] = 1;
for(int i=0;i<n/2-z/2;i++) vis[zer[i]] = 1;
}
if(y>n/2||x>n/2) return 0*puts("-1");
if(x>y){
swap(x,y);
swap(f,s);
ok = 1;
}
if(x+z>=y){
int c = n/2 - x;
for(int i=0;i<x;i++) vis[f[i]] = 1;
int j = z - (y - x);
if(j&1) vis[s[0]] = 1,c--;
j = j/2 + (y-x),c -= j;
for(int i=0;i<j;i++) vis[one[i]] = 1;
for(int i=0;i<c;i++) vis[zer[i]] = 1;
}else{
for(int i=0;i<x;i++) vis[f[i]] = 1;
for(int i=0;i<z;i++) vis[one[i]] = 1;
int j = y - z - x,c = n/2 - x - z - j;
for(int i=0;i<j;i++) vis[s[i]] = 1;
for(int i=0;i<c;i++) vis[zer[i]] = 1;
}
for(int i=1;i<=n;i++) if(ok^vis[i]) printf("%d ",i);
return 0*puts("");
}
C. Skyscrapers
解题思路:
这题简单直接,排序去重然后二分找排名,a[i][j]在两个数组中的排名小的那个补到跟另一个大的一样大就好了,最后就是去小的排名中最大排名加上排名差和大的那个最大排名取MAX就好了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
const int mx = 1e3 + 10;
int a[mx][mx];
int r[mx][mx],c[mx][mx];
int lr[mx],lc[mx];
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++)
for(int j=0;j<m;j++) scanf("%d",a[i]+j);
for(int i=0;i<n;i++){
for(int j=0;j<m;j++) r[i][j] = a[i][j];
sort(r[i],r[i]+m);
lr[i] = unique(r[i],r[i]+m) - r[i];
}
for(int j=0;j<m;j++){
for(int i=0;i<n;i++) c[j][i] = a[i][j];
sort(c[j],c[j]+n);
lc[j] = unique(c[j],c[j]+n) - c[j];
}
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
int v = lower_bound(r[i],r[i]+lr[i],a[i][j]) - r[i];
int u = lower_bound(c[j],c[j]+lc[j],a[i][j]) - c[j];
if(v<u) v = lr[i] + u - v,u = lc[j];
else u = lc[j] + v - u,v = lr[i];
printf("%d ",max(u,v));
}
puts("");
}
return 0;
}
D. Camp Schedule
解题思路:
用KMP解决t串的后缀和前缀的最大公共长度,之后就一直复制下去就好了。。。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<cstring>
using namespace std;
typedef long long ll;
const int mx = 5e5 + 10;
char str[mx],Ts[mx];
int one,zer,nxt[mx];
void kmp(char *p)
{
int i = -1,j = 0;
nxt[0] = -1;
while(p[j]){
if(i==-1||p[i]==p[j])
{
nxt[++j] = ++i;
}else i = nxt[i];
}
}
int main(){
scanf("%s",str);
for(int i=0;str[i];i++)
if(str[i]=='1') one++; else zer++;
scanf("%s",Ts);
int len = strlen(Ts),a = 0,b = 0;
for(int i=0;Ts[i];i++)
if(Ts[i]=='1') a++; else b++;
kmp(Ts);
if(one<a||zer<b) return 0*puts(str);
one -= a,zer -=b,a = b = 0;
for(int i=nxt[len];Ts[i];i++)
if(Ts[i]=='1') a++; else b++;
printf("%s",Ts);
while(one>=a&&zer>=b)
{
printf("%s",Ts+nxt[len]);
one -= a,zer -= b;
}
for(int i=0;i<one;i++) putchar('1');
for(int i=0;i<zer;i++) putchar('0');
return 0*puts("");
}
E. Museums Tour
解题思路:
可想而知一个点一共会有d种状态,那么我们何不把这些状态都拆分出来,这样既变成了n*d个点,m*d条边。
然后tarjan缩点之后跑dp就可以了。但是5e6的点会递归爆栈,那么我们就把他弄成inline内联函数,节省一下栈内存。
#include <bits/stdc++.h>
using namespace std;
const int mx = 5e6 + 10;
int n,m,d;
int hd1[mx],tot1,hd2[mx],tot2;
struct node
{
int v,nxt;
}e1[mx],e2[mx];
char str[100010][55];
int dfn[mx],id[mx],is,siz,q[mx];
int ty[mx],type,cnt[mx],in[mx];
bool vis[mx];
void add1(int u,int v)
{
e1[tot1] = {v,hd1[u]};
hd1[u] = tot1++;
}
void add2(int u,int v)
{
e2[tot2] = {v,hd2[u]};
hd2[u] = tot2++;
}
inline void tarjan(int u)
{
dfn[u] = id[u] = ++is;
vis[u] = 1;q[++siz] = u;
for(int i=hd1[u];~i;i=e1[i].nxt)
{
int v = e1[i].v;
if(!dfn[v]){
tarjan(v);
id[u] = min(id[v],id[u]);
}else if(vis[v])
id[u] = min(id[u],dfn[v]);
}
if(dfn[u]==id[u]){
type++;
do{
ty[q[siz]] = type;
int r = q[siz] / d, c = q[siz] % d;
if(in[r]!=type&&str[r][c]=='1')
cnt[type]++,in[r] = type;
vis[q[siz]] = 0;
}while(q[siz--]!=u);
}
}
int main()
{
scanf("%d%d%d",&n,&m,&d);
int u,v;
memset(hd1,-1,sizeof(hd1));
memset(hd2,-1,sizeof(hd2));
for(int i=0;i<m;i++){
scanf("%d%d",&u,&v);
for(int j=0;j<d;j++)
add1((u-1)*d+j,(v-1)*d+(j+1)%d);
}
for(int i=0;i<n;i++) scanf("%s",str[i]);
tarjan(0);
for(int i=0;i<n*d;i++){
if(ty[i])
for(int j=hd1[i];~j;j=e1[j].nxt){
v = e1[j].v;
if(ty[i]!=ty[v]) add2(ty[i],ty[v]);
}
}
int ans = 0;
for(int i=1;i<=type;i++){//拓扑排序
int ma = 0;
for(int j=hd2[i];~j;j=e2[j].nxt)
ma = max(ma,cnt[e2[j].v]);
cnt[i] += ma;
}
printf("%d\n",cnt[type]);
return 0;
}
F. Cooperative Game
解题思路:
(交互机模式还是第一次做。。。)
交互机会告诉你你每次移动之后的结果,哪几个点是在一起的,但它最多只跟你说3(t+c)次。
而我们也不知道t和c。所以就算10个点最后再一次了,我们还要去判断是不是在终点集合。
所以只能试着想用解方程来解决这个问题。并且尽量把移动弄的简单一点。(真的可以在三种移动情况下解决它)
1.next 0 1 2.next 0 3.next 0 1 2 3 4 5 6 7 8 9
当当讨论前两种,发现0走的步数是1的两倍,假设他们会在c的某个点相遇,又s为1走的步数。那么有s = t + y*c + a(a<c)
又设w为0走的步数,w = t + x*c + a(a<c)。那么有2*s ==w ,2*t + 2*y*c + 2*a == t + x*c + a。
t = (x-2*y)*c - a。w + t = t + x*c + a + (x-2*y)*c - a = t + 2*(x-y)*c。
依次可得当0走了w步,1走了s步两点在c中相遇时,再走t步就可以达了终点,那么我们另剩下的2,3,4,5,6,7,8,9一起从开始点开始走,他们就一起到达了终点。
步数证明:
所以他们最后走了t + 2*(x-y)*c步。
当t<=c时,所以答案会是t + 2*c。(x-y肯定为1)(证明简单略过,自己证明吧)
当t>c时,当1第一次到达终点时,0走了2*t步,此时0最多走两圈和1相遇(也就是同在终点时),就是2*c,最后再走t步全部到达终点。所以总步数为3*t+2*c。
#include <algorithm>
#include <cstdio>
#include <iostream>
#include <map>
#include <cstring>
#include <vector>
#define fi first
#define se second
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair <int,int> pa;
const int mx = 1e5 + 10;
const int mod = 1e9 + 7;
int main()
{
int val;
do{
puts("next 0 1");fflush(stdout);
scanf("%d %*[^\n]",&val);
puts("next 0");fflush(stdout);
scanf("%d %*[^\n]",&val);
}while(val==3);
do{
puts("next 0 1 2 3 4 5 6 7 8 9");fflush(stdout);
scanf("%d %*[^\n]",&val);
}while(val==2);
puts("done");fflush(stdout);
return 0;
}