2017 ACM Jordanian Collegiate Programming Contest

时间:2022-01-06 14:16:52

A. Chrome Tabs

当$n=1$时答案为$0$,当$k=1$或$k=n$时答案为$1$,否则答案为$2$。

#include<cstdio>
int T,n,k;
int main(){
freopen("tabs.in","r",stdin);
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
if(n==1)puts("0");
else if(k==1||k==n)puts("1");
else puts("2");
}
}

  

B. OverCode

按题意模拟即可。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n;
int a[505];
int main()
{
freopen("overcode.in","r",stdin);
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
int p = 1;
int ans = 0;
while(p <= n - 5)
{
if(a[p + 5] - a[p] <= 1000)
{
++ans;
p += 6;
}
else p += 1;
}
printf("%d\n", ans);
}
return 0;
}
/*
【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */

  

C. A message for you!

按题意模拟即可。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 0, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n;
char s[20];
int a[20];
bool e[20];
int main()
{
freopen("scoreboard.in","r",stdin);
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
scanf("%d", &n);
scanf("%s", s);
MS(e, 0);
for(int i = 0; i < n; i ++){
e[s[i] - 'A' + 1] = 1;
}
for(int i = 1; i <= 13; ++i)scanf("%d", &a[i]);
int ans = 0, tmp = 0;
for(int i = 1; i <= 13; i ++){
if(e[i] == 0 && a[i] > tmp){
tmp = a[i];
ans = i;
}
}
printf("%c\n", ans + 'A' - 1);
}
return 0;
}
/*
【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */

  

D. Test Cases

枚举左端点,往右枚举右端点,同时维护每个数字出现次数的奇偶性。

时间复杂度$O(n^2)$。

#include<cstdio>
int T,n,i,j,odd,ans,v[1000010],a[5555];
int main(){
freopen("cases.in","r",stdin);
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
ans=0;
for(i=1;i<=n;i++){
for(j=i;j<=n;j++)v[a[j]]=0;
odd=0;
for(j=i;j<=n;j++){
v[a[j]]?odd--:odd++;
v[a[j]]^=1;
if(odd==1)ans++;
}
}
printf("%d\n",ans);
}
}

  

E. Robot I - Instruction Reduction

按题意模拟即可。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 505, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m, q;
int y, x; char dir;
char s[N][N];
int f[N][N][4]; //real way
const int dy[4] = {-1,0,1,0};
const int dx[4] = {0,1,0,-1};
int a[1010 * 1010];
int Y, X, D;
pair<int,int> ord[1010];
int main()
{
freopen("reduce.in","r",stdin);
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
char dir;
scanf("%d%d%d", &n, &m, &q);
scanf("%d%d %c", &Y, &X, &dir);
if(dir == 'U')D = 0;
else if(dir == 'R')D = 1;
else if(dir == 'D')D = 2;
else D = 3;
int sty = Y, stx = X, stdir = D;
for(int i = 1; i <= n; ++i)
{
scanf("%s", s[i] + 1);
}
for(int i = 2; i < n; ++i)
{
for(int j = 2; j < m; ++j)if(s[i][j] == '.')
{
for(int d = 0; d < 4; ++d)
{
for(int k = 0; k < 4; ++k)
{
int dd = (d + k) % 4;
if(s[i + dy[dd]][j + dx[dd]] == '.')
{
f[i][j][d] = dd;
break;
}
}
}
}
}
//printf("f:%d\n", f[3][2][1]);
int g = 0;
for(int i = 1; i <= q; ++i)
{
char op; scanf(" %c", &op);
if(op == 'R')
{
D = (D + 1) % 4;
}
else
{
int step;
scanf("%d", &step);
while(step--)
{
D = f[Y][X][D];
Y += dy[D];
X += dx[D];
a[++g] = D;
}
}
}
Y = sty, X = stx, D = stdir; //Y X D
int o = 0;
for(int i = 1; i <= g; ++i)
{
/*
printf("step = %d, status = %d %d %d, aimdir = %d\n", i, Y, X, D, a[i]);
printf("f[%d][%d][%d] = %d, a[i] = %d\n", Y, X, D, f[Y][X][D], a[i]);
getchar();
*/
for(int k = 0; k < 4; ++k)
{
if(f[Y][X][D] == a[i])
{
D = a[i];
Y += dy[D]; X += dx[D];
if(o && ord[o].first == 1)
{
++ord[o].second;
}
else ord[++o] = {1, 1};
break;
}
else
{
ord[++o] = {2, -99999999};
D = (D + 1) % 4;
}
}
}
printf("%d\n", o);
}
return 0;
}
/*
【trick&&吐槽】 【题意】 【分析】 【时间复杂度&&优化】 */

  

F. Robot II - Colorful Grid

留坑。

G. WiFi Password

枚举区间$OR$本质不同的$O(n\log w)$段区间,更新答案即可。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=100010;
int T,n,lim,i,j,v[N],a[N],l[N],ans;
inline int fun(int a,int b){return a|b;}
int main(){
freopen("wifi.in","r",stdin);
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&lim);
ans=0;
for(i=1;i<=n;i++)scanf("%d",&a[i]);
for(i=1;i<=n;i++)for(v[i]=a[i],j=l[i]=i;j;j=l[j]-1){
v[j]=fun(v[j],a[i]);
while(l[j]>1&&fun(a[i],v[l[j]-1])==fun(a[i],v[j]))l[j]=l[l[j]-1];
if(v[j]<=lim)ans=max(ans,i-l[j]+1);
}
printf("%d\n",ans);
}
}

  

H. Gas Stations

枚举去掉哪一个加油站,那么最优策略要么是在一开始插入,要么是在末尾插入,要么是将间隔最大的空段劈成两半,维护前$4$大即可快速询问。

时间复杂度$O(n)$。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100010;
int bestside[N];
int T,n,m,ans,i,j,q[N];
int pre[N],nxt[N];
char a[N];
int w[N],cnt;
int now[N],tmp;
inline bool cmp(int x,int y){return x>y;}
inline int cal(int l,int r){
int dis=r-l-1;
if(l==0||r==n+1)return dis;
return (dis+1)/2;
}
inline int calleft(int mark){
for(int i=2;i<=m;i++)if(mark!=q[i])return q[i]-1;
}
inline int calright(int mark){
for(int i=m-1;i;i--)if(mark!=q[i])return n-q[i];
}
inline void gao(int l,int r){
if(l==0||r==n+1)return;
w[++cnt]=r-l-1;
}
inline void del(int l,int r){
if(l==0||r==n+1)return;
int dis=r-l-1;
int i;
for(i=1;i<=tmp;i++)if(now[i]==dis)break;
if(i>tmp)return;
for(;i<tmp;i++)now[i]=now[i+1];
tmp--;
}
inline void add(int l,int r){
if(l==0||r==n+1)return;
int dis=r-l-1;
int i;
now[++tmp]=dis;
sort(now+1,now+tmp+1,cmp);
if(tmp>4)tmp--;
}
inline void work(int x){
int i=pre[x],j=nxt[x];//i x j
tmp=cnt;
for(int _=1;_<=cnt;_++)now[_]=w[_];
del(i,x);
del(x,j);
add(i,j);
int left=calleft(x),right=calright(x);
int maxgap=0,secgap=0;
if(tmp>=1)maxgap=now[1];
if(tmp>=2)secgap=now[2];
//case 1 add one in left
ans=min(ans,max(bestside[left],max(right,(maxgap+1)/2)));
//case 2 add one in right
ans=min(ans,max(left,max(bestside[right],(maxgap+1)/2)));
//case 3 add one in maxgap
ans=min(ans,max(max(left,right),max((maxgap/2+1)/2,(secgap+1)/2)));
}
int main(){
freopen("stations.in","r",stdin); for(i=j=1;i<N;i++){
while(j<i&&max(j-1,(i-j+1)/2)>max(j,(i-(j+1)+1)/2))j++;
bestside[i]=max(j-1,(i-j+1)/2);
//i=3 |___+
} scanf("%d",&T);
while(T--){
scanf("%d%s",&n,a+1);
ans=n;
q[m=1]=0;
for(i=1;i<=n;i++)if(a[i]=='+')q[++m]=i;
q[++m]=n+1; if(m==3){
for(i=1;i<=n;i++)ans=min(ans,max(cal(0,i),cal(i,n+1)));
printf("%d\n",ans);
continue;
} pre[0]=0;
nxt[n+1]=0;
for(i=1;i<=m;i++){
if(i>1)pre[q[i]]=q[i-1];
if(i<m)nxt[q[i]]=q[i+1];
}
cnt=0;
for(i=1;i<m;i++)gao(q[i],q[i+1]);
sort(w+1,w+cnt+1,cmp);
cnt=min(cnt,4);
for(i=2;i<m;i++)work(q[i]);
printf("%d\n",ans);
}
}

  

I. Counting Votes

留坑。

J. Efficiency Test

将环倍长,设$f[i][j]$表示在$i$点按$j$步长往右会跳到哪里,可以得到一棵树的结构,在树上DFS,询问时二分即可。

常数优化:将失败的父亲合并,同时剔除子树内不含询问的点。

时间复杂度$O(nk+n\log n)$。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200010,M=55,E=N*M;
int Case,n,m,nn,i,j,cnt,s[N];
int goal[N],ans[N];
int g[E],nxt[E],f[E],q[E],t;
bool vip[E];
char a[N];
inline bool canjump(int x,int y){
if(x+y>nn)return 0;
if(a[x+y]=='#')return 0;
return s[x+y]-s[x-1]<=1;
}
inline void gao(int x){
//printf("gao %d\n",x);
//for(int i=2;i<=t;i++)printf("%d %d\n",q[i],w[i]);
//[2..t]
int y=goal[x];
if(q[2]<y){ans[x]=-1;return;}
int l=3,r=t,mid,o=2;
while(l<=r){
mid=(l+r)>>1;
if(q[mid]>=y)l=(o=mid)+1;else r=mid-1;
}
ans[x]=f[t]-f[o];
}
void dfs(int x){
//printf("dfs %d\n",x);
if(!vip[x])return;
int A=x/(m+1);
q[++t]=A;
f[t]=f[t-1];
if(q[t-1]!=A)f[t]++;
if(A&&A<=n&&x%(m+1)==m)gao(A);
for(int i=g[x];i;i=nxt[i])dfs(i);
t--;
}
int main(){
freopen("jumps.in","r",stdin);
scanf("%d",&Case);
while(Case--){
scanf("%d%d%s",&n,&m,a+1);
//for(i=1;i<=n;i++)a[i]='.';
//a[1]='#';
nn=n*2;
for(i=1;i<=n;i++)a[i+n]=a[i];
for(i=1;i<=nn;i++)s[i]=s[i-1]+(a[i]=='#');
//[2..m] is valid
cnt=nn*(m+1)+m;
for(i=0;i<=cnt;i++)g[i]=vip[i]=0;
for(i=nn;i;i--)if(a[i]=='.'){
int y=0;
for(j=2;j<=m;j++){
int x=i*(m+1)+j;
if(canjump(i,j))y=(i+j)*(m+1)+j;
f[x]=y;
//printf("f[%d][%d]=%d\n",i,j,y);
//nxt[x]=g[y];
//g[y]=x;
}
int x=i*(m+1)+m;
while(!vip[x]){
vip[x]=1;
x=f[x];
}
}
for(i=1;i<=nn;i++)if(a[i]=='.'){
for(j=2;j<=m;j++){
int x=i*(m+1)+j;
if(!vip[x])continue;
int y=f[x];
nxt[x]=g[y];
g[y]=x;
}
}
for(i=j=1;i<=n;i++){
while(s[j]-s[i-1]<s[n])j++;
goal[i]=j;//>=j
//printf("goal[%d]=%d\n",i,j);
}
dfs(0);
for(i=1;i<=n;i++){
if(a[i]=='#')printf("0 ");
else printf("%d ",ans[i]);
}
puts("");
}
}

  

K. Running Threads

考虑费用流建图:

  • $S$向每条记录连边,流量$[0,1]$,费用$0$,表示一组的开始。
  • 记录$i$向记录$j$连边,流量$[0,1]$,费用$0$,表示一组延续。
  • 记录向线程连边,流量$[0,1]$,费用$w$,表示一组的结束。
  • 每条线程向$T$连边,流量$[0,1]$,费用$0$,表示只接受最多一组记录。
  • 每条记录拆点,中间连边,流量$[1,1]$,费用$0$。

那么两问分别对应这个有上下界的网络的最小/最大费用可行流。

#include<cstdio>
typedef long long ll;
const int N=1000,M=1000000;
const ll inf=1LL<<60;
int Case,n,m,i,j,a[N],b[N];
ll sum;
int u[M],v[M],c[M],co[M],nxt[M],t,S,T,SS,TT,l,r,q[M];
int g[N],f[N];
bool vis[N];
ll d[N];
int in[N];
int tmp;ll ans;
inline void add(int x,int y,int l,int r,int zo){
r-=l;
in[x]-=l;
in[y]+=l;
if(!r)return;
u[++t]=x;v[t]=y;c[t]=r;co[t]=zo;nxt[t]=g[x];g[x]=t;
u[++t]=y;v[t]=x;c[t]=0;co[t]=-zo;nxt[t]=g[y];g[y]=t;
}
bool spfa(){
int x,i;
for(i=1;i<=TT;i++)d[i]=inf,vis[i]=0;
d[SS]=0;
vis[SS]=1;
l=r=M>>1;
q[l]=SS;
while(l<=r){
int x=q[l++];
if(x==TT)continue;
for(i=g[x];i;i=nxt[i])if(c[i]&&co[i]+d[x]<d[v[i]]){
d[v[i]]=co[i]+d[x];f[v[i]]=i;
if(!vis[v[i]]){
vis[v[i]]=1;
if(d[v[i]]<d[q[l]])q[--l]=v[i];else q[++r]=v[i];
}
}
vis[x]=0;
}
return d[TT]<inf;
}
int main(){
freopen("threads.in","r",stdin);
scanf("%d",&Case);
while(Case--){
scanf("%d%d",&n,&m);
sum=0;
for(i=1;i<=m;i++)scanf("%d",&a[i]),sum+=a[i];
for(i=1;i<=n;i++)scanf("%d",&b[i]); S=n*2+m+1;
T=S+1;
SS=T+1;
TT=SS+1;
for(t=i=1;i<=TT;i++)g[i]=in[i]=0;
add(T,S,0,N,0);
for(i=1;i<=n;i++){
add(i,i+n,1,1,0);
add(S,i,0,1,0);
for(j=1;j<=m;j++)if(b[i]<=a[j])add(i+n,j+n*2,0,1,-b[i]);
for(j=i+1;j<=n;j++)if(b[i]<b[j])add(i+n,j,0,1,0);
}
for(i=1;i<=m;i++)add(i+n*2,T,0,1,0);
for(i=1;i<=TT;i++){
if(in[i]>0)add(SS,i,0,in[i],0);
if(in[i]<0)add(i,TT,0,-in[i],0);
}
ans=0;
while(spfa()){
for(tmp=N,i=TT;i!=SS;i=u[f[i]])if(tmp>c[f[i]])tmp=c[f[i]];
for(ans+=d[i=TT]*tmp;i!=SS;i=u[f[i]])c[f[i]]-=tmp,c[f[i]^1]+=tmp;
}
ans*=-1;
printf("%lld ",sum-ans); S=n*2+m+1;
T=S+1;
SS=T+1;
TT=SS+1;
for(t=i=1;i<=TT;i++)g[i]=in[i]=0;
add(T,S,0,N,0);
for(i=1;i<=n;i++){
add(i,i+n,1,1,0);
add(S,i,0,1,0);
for(j=1;j<=m;j++)if(b[i]<=a[j])add(i+n,j+n*2,0,1,b[i]);
for(j=i+1;j<=n;j++)if(b[i]<b[j])add(i+n,j,0,1,0);
}
for(i=1;i<=m;i++)add(i+n*2,T,0,1,0);
for(i=1;i<=TT;i++){
if(in[i]>0)add(SS,i,0,in[i],0);
if(in[i]<0)add(i,TT,0,-in[i],0);
}
ans=0;
while(spfa()){
for(tmp=N,i=TT;i!=SS;i=u[f[i]])if(tmp>c[f[i]])tmp=c[f[i]];
for(ans+=d[i=TT]*tmp;i!=SS;i=u[f[i]])c[f[i]]-=tmp,c[f[i]^1]+=tmp;
}
printf("%lld\n",sum-ans);
}
}
/*
3
6 3
8 12 7
5
3
7
3
4
9
3 3
1000000000 999999999 999999998
1
10
1000
5 3
9 5 8
9
3
6
5
8 */

  

L. Knights

设$f[i][a][b][c][d]$表示从上到下考虑前$i$行,最后$4$行的骑士跳跃方向分别为$a,b,c,d$的方案数。

需要根据可行性将$a$压缩到$3$,$b,c$压缩到$5$,$d$压缩到$7$才能通过。

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=550,P=1000000007;
int cnt,id[3][5][5][7],p[N][4],g[N][9];
int Case,n,m,w,i,j,k,a[N],f[N][N],ans;
char ch[N];
int v[N][N];
int dx[9]={0,2,2,1,1,-1,-1,-2,-2};
int dy[9]={0,-1,1,-2,2,-2,2,-1,1};
inline void up(int&a,int b){
a=a+b<P?a+b:a+b-P;
}
void init(){
for(int A=0;A<3;A++)for(int B=0;B<5;B++)for(int C=0;C<5;C++)for(int D=0;D<7;D++){
int now=cnt++;
id[A][B][C][D]=now;
p[now][0]=A;
p[now][1]=B;
p[now][2]=C;
p[now][3]=D;
}
for(int A=0;A<3;A++)for(int B=0;B<5;B++)for(int C=0;C<5;C++)for(int D=0;D<7;D++){
int now=id[A][B][C][D];
for(int i=1;i<9;i++){
int nA=B,nB=C,nC=D,nD=i;
if(nA>=3)nA=0;
if(nB>=5)nB=0;
if(nC>=5)nC=0;
if(nD>=7)nD=0;
g[now][i]=id[nA][nB][nC][nD];
}
}
}
int main(){
freopen("knights.in","r",stdin);
init();
scanf("%d",&Case);
while(Case--){
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++){
scanf("%s",ch+1);
for(j=1;j<=m;j++){
if(ch[j]=='*')a[i]=j;
v[i][j]=ch[j]!='.';
}
}
for(i=0;i<=n;i++)for(j=0;j<cnt;j++)f[i][j]=0;
f[0][0]=1;
for(i=0;i<n;i++)for(j=0;j<cnt;j++)if(f[i][j]){
w=f[i][j];
int A=p[j][0],B=p[j][1],C=p[j][2],D=p[j][3];
if(A)v[i-3+dx[A]][a[i-3]+dy[A]]++;
if(B)v[i-2+dx[B]][a[i-2]+dy[B]]++;
if(C)v[i-1+dx[C]][a[i-1]+dy[C]]++;
if(D)v[i+dx[D]][a[i]+dy[D]]++;
for(k=1;k<9;k++){
int x=i+1+dx[k],y=a[i+1]+dy[k];
if(x>=1&&x<=n&&y>=1&&y<=m&&!v[x][y])
up(f[i+1][g[j][k]],w);
}
if(A)v[i-3+dx[A]][a[i-3]+dy[A]]--;
if(B)v[i-2+dx[B]][a[i-2]+dy[B]]--;
if(C)v[i-1+dx[C]][a[i-1]+dy[C]]--;
if(D)v[i+dx[D]][a[i]+dy[D]]--;
}
ans=0;
for(j=0;j<cnt;j++)up(ans,f[n][j]);
printf("%d\n",ans);
}
}

  

M. Winning Cells

问题等价于两个$K-Nim$游戏,那么只要$A\bmod (K+1)\neq B\bmod (K+1)$则先手必胜。

反过来考虑计算必败态的个数,那么考虑$1$到$N$每个数模$K+1$的值,可以将这些数分成贡献不同的两组分别计算。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=100010;
int T,n,k;long long ans;
inline int fun(int a,int b){return a|b;}
int main(){
freopen("chess.in","r",stdin);
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);k++;
int m=n/k*k;
int base=n/k;
//[0..k-1] base
//[1..n%k] base+1
ans=1LL*n*n;
int A=n%k,B=k-A;
ans-=1LL*base*base*B;
base++;
ans-=1LL*base*base*A;
printf("%lld\n",ans);
}
}