目录
- A Little Artem
- B Kind Anton
- C Eugene and an array
- D Challenges in school №41
- E Road to 1600
- F Kate and imperfection
题目地址:https://codeforces.com/contest/1333
A Little Artem
题意:给出一个n*m的网格(2≤n,m≤100),要把每个格子涂成黑色或者白色。记存在边相邻白格子的黑格子个数为B,存在边相邻黑格子的白格子个数为W,要求给出涂色方案,使得 B=W+1 。
思路:因为数据限制(大于等于2),故很容易想到只要把前 n-1 行全部涂成黑色,最后一行第一个黑色其它全部白色即可。
代码:
#pragma GCC optimize(2)
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
int main()
{
int t=read();
while(t--)
{
int n=read(),m=read();
REP(i,1,n)
{
if(i==n)
{
putchar('B');
REP(i,2,m) putchar('W');
}
else
REP(i,1,m) putchar('B');
puts("");
}
}
return 0;
}
B Kind Anton
题意:a 数组由 {-1, 0, 1} 组成,b数组由整数组成,在 a 数组上操作,每次操作可以选 i
思路:不难发现,对于每个 i>1 ,如果 b[i]>a[i],那么 a 数组中 i 前面的位置只要存在 1 就可以得到 b[i],其它情况同理,对于 i=1,必须要 a[1]=b[1] 。所以先处理每个位置前面是否存在 -1 和 1,然后就可以判断了。
代码:
#pragma GCC optimize(2)
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=1e5+5;
int a[maxn],f[maxn][2],b[maxn];
int main()
{
int T=read();
while(T--)
{
int n=read();
REP(i,1,n) a[i]=read();
REP(i,1,n) b[i]=read();
REP(i,1,n)
{
if(f[i-1][0] || a[i-1]==-1) f[i][0]=1;
else f[i][0]=0;
if(f[i-1][1] || a[i-1]==1) f[i][1]=1;
else f[i][1]=0;
}
int flag=1;
REP(i,1,n)
{
if(b[i]>a[i] && !f[i][1]) flag=0;
if(b[i]<a[i] && !f[i][0]) flag=0;
}
puts(flag?"YES":"NO");
}
return 0;
}
C Eugene and an array
题意:一个数列是 good 的,当且仅当它不存在某个子列(子列定义为原数列前后各删去若干个数(可以为0个)所得到的数列),使得这个子列求和为 0 。现在给出一个数列,问它有多少个 good 的子列。
思路:一开始思路有点混乱,后来想到只要对于每个左边界 L,能够计算出最小的 R,使得 [L, R] 中存在坏数列,那么就可以计算出反面的答案了。不过这种思路还是有点问题,就是很难算出 R。再后来换一个方向,对于每个右边界 R,计算出最大的 L,然后发现这样就容易很多。对于某个 R,假设原数列的前缀和为 s 数列,那么找到最右边的 L,使得 s[R]==s[L-1],则 [L, R] 这个区间的子列为 bad 的,进一步维护一直以来 L 的最大值 maxL,那么只要 l
题解给出的做法是对于每个 L,求出最大的 R,使得 [L, R] 中没有重复元素,这样好像简单一点点。
代码:
#pragma GCC optimize(2)
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=2e5+5;
LL s[maxn];
map<LL,int> t;
int main()
{
int n=read(),maxl=0;
LL ans=0;
REP(i,1,n) s[i]=s[i-1]+read();
REP(r,0,n)
{
maxl=max(maxl,t[s[r]]);
ans+=maxl;
t[s[r]]=max(t[s[r]],r+1);
}
cout<<(1ll*n*(n+1)/2-ans);
return 0;
}
D Challenges in school №41
题意:n(2≤n≤3000) 个人坐成一排,每个人的头要么向右看,要么向左看。在每一秒中,互看(左边向右,右边向左)的两个人可以一起把头扭过去(反向),这一秒钟每个人最多扭一次头,多个人可以同时扭。给出正整数 k,问是否存在一个方案,使得刚好 k 秒钟过去后,不存在相互看的人。
思路:简化题意,就是给出01串,每次可以把 10
变成 01
,最后要变成 00...0011...111
。如果没有刚好 k 秒的限制,那么这题暴力 O ( n 2 ) O(n^2) O(n2) 就可以做出来了,现在麻烦的是要刚好 k 次。先按照最优策略,也就是每次处理尽量多的对儿,求出一个次数 minx,然后如果把每对儿都当做一次来处理,又有一个答案 maxx,如果 m i n x ≤ k ≤ m a x x minx\leq k \le maxx minx≤k≤maxx,那么说明存在方案。
尽管我知道应该怎么分配,前面尽可能拆,后面尽可能不拆,但是写起来真的是难写。
代码:
#pragma GCC optimize(2)
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=3e6+5;
int a[maxn],n,k,tot,sum;
vector<int> ans[maxn];
char s[maxn];
bool solve()
{
tot++;
REP(i,1,n-1) if(a[i] && !a[i+1])
{
ans[tot].push_back(i);
swap(a[i],a[i+1]);
i++;
}
sum+=ans[tot].size();
return ans[tot].size()!=0;
}
int main()
{
n=read(),k=read();
scanf("%s",s+1);
REP(i,1,n) a[i]=s[i]=='R';
while(solve()); tot--;
E Road to 1600
题意:给定一个 N,要求一个 N*N 的网格,里面的数字是从 1 到 N*N,并且满足:一颗棋子从 1 开始走,每次选择这颗棋子没有走过,能走到,并且数字最小的位置,作为下一次走到的位置,如果某些时候不存在能走到并且没走过的位置,那么就花费 1 直接跳转到最小的没有走过的位置,这里 “能走到” 是由这个棋子的属性定义的,车可以横竖走,皇后可以横竖撇捺走。要求给出一个填充数字的策略,使得车走完全部格子的花费严格小于皇后。
思路:这些构造题一般都显得比较巧妙。
如果能找到一个最小的存在方案的边长 M,对于边长大于M的时候,只要把最大的 M*M 个数以相同的规律放在右上角,然后其它位置填充更小的数,保证能不用任何花费就可以到达右上角就行了。问题就在于如果寻找这个最小的 M。
我是猜测当 N=3 时存在方案(1,2肯定不存在),因为就 9 个数,我写了一份代码去寻找 N=3 时的方案:
#pragma GCC optimize(2)
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
int vis[12],b[12],a[4][4];
int visr[4][4],visq[4][4];
void print()
{
REP(i,1,3)
{
REP(j,1,3) cout<<a[i][j]<<' ';
cout<<endl;
}
cout<<endl;
}
bool can()
{
int cr=0,cq=0,xr,yr,xq,yq,nxr,nyr,nxq,nyq;
REP(i,1,3) REP(j,1,3) if(a[i][j]==1) xr=xq=i,yr=yq=j;
mem(visr,0); mem(visq,0);
visr[xr][yr]=visq[xq][yq]=1;
REP(t,2,9)
{
int mr=100,mq=100;
REP(i,1,3) REP(j,1,3)
{
if(!visr[i][j] && (i==xr || j==yr) && a[i][j]<mr) mr=a[i][j],nxr=i,nyr=j;
if(!visq[i][j] && (i==xq || j==yq || abs(i-xq)==abs(j-yq)) && a[i][j]<mq)
mq=a[i][j],nxq=i,nyq=j;
}
if(mr<10) xr=nxr,yr=nyr;
else
{
REP(i,1,3) REP(j,1,3) if(!visr[i][j] && a[i][j]<mr)
mr=a[i][j],xr=i,yr=j;
cr++;
}
if(mq<10) xq=nxq,yq=nyq;
else
{
REP(i,1,3) REP(j,1,3) if(!visq[i][j] && a[i][j]<mq)
mq=a[i][j],xq=i,yq=j;
cq++;
}
visr[xr][yr]=visq[xq][yq]=1;
}
return cr<cq;
}
void dfs(int x)
{
if(x>9)
{
REP(i,1,3) REP(j,1,3) a[i][j]=b[(i-1)*3+j];
if(can()) print();
return;
}
for(int i=1;i<=9;i++) if(!vis[i])
{
vis[i]=1; b[x]=i;
dfs(x+1);
vis[i]=0;
}
}
int main()
{
dfs(1);
return 0;
}
然后发现有好多好多方案都满足,比如:
1 2 4
5 3 8
9 6 7
1 2 5
4 3 8
9 7 6
8 6 2
4 1 7
9 5 3
这些都可以。
代码:
#pragma GCC optimize(2)
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
int n,a[505][505];
int ker[3][3]={1,2,4,5,3,8,9,6,7};
int main()
{
n=read();
if(n<3) return puts("-1"),0;
int dir=n&1,t=0;
REP(x,1,n-3)
{
if(dir)
{
REP(i,1,n-x+1) a[i][x]=++t;
REP(j,x+1,n) a[n-x+1][j]=++t;
}
else
{
REP_(j,n,x+1) a[n-x+1][j]=++t;
REP_(i,n-x+1,1) a[i][x]=++t;
}
dir^=1;
}
REP(i,0,2) REP(j,0,2) a[i+1][n-2+j]=t+ker[i][j];
REP(i,1,n)
{
REP(j,1,n) printf("%d ",a[i][j]);
puts("");
}
return 0;
}
F Kate and imperfection
题意:给定一个集合 M,这个集合的 imperfection 定义为里面两两最大公因数的最大的那一个,也就是 i m p e r f e t i o n ( M ) = max i , j ∈ M , i ≠ j g c d ( i , j ) imperfetion(M) = \max\limits_{i,j\in M,i\neq j}gcd(i,j) imperfetion(M)=i,j∈M,i=jmaxgcd(i,j) 。给出一个集合 S, I k I_k Ik 定义为 S 的大小为 k 的子集,且 imperfection 的最小值。现给出 S={1, 2, …, n},求 I 2 , I 3 , . . . , I n I_2,I_3,...,I_n I2,I3,...,In 。
思路:可以看出 I k I_k Ik 就是在 I k − 1 I_{k-1} Ik−1 的方案上增加一个数得来的,所以是一个从小到大的处理。一开始肯定把 1 和所有质数选择,这样这些数的 imperfection就是1,然后考虑加哪些数使得 imperfection 为 2,然后是 3、4等等。注意到按照贪心策略每次添加一个非质数的时候,这个数的除本身外的最大因数一定是已选集合中某个数的因子,所以我们就把所有数按照最大因子排序,依次添加,每次添加后答案就更新为这个最大因子。
换个方向想,从前往后枚举 i,那么当前 i*2, i*3, … 的最大因子都是 i,这不就是埃氏筛的过程吗。筛一遍,排个序,就可以了。
代码:
#pragma GCC optimize(2)
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define REP(i,a,b) for(int i=(a);i<=(int)(b);i++)
#define REP_(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
typedef long long LL;
int read()
{
int x=0,flag=1;
char c=getchar();
while((c>'9' || c<'0') && c!='-') c=getchar();
if(c=='-') flag=0,c=getchar();
while(c<='9' && c>='0') {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
return flag?x:-x;
}
const int maxn=5e5+5;
int a[maxn];
int main()
{
int n=read();
REP(i,2,n) for(int j=i*2;j<=n;j+=i) a[j]=i;
REP(i,1,n) if(!a[i]) a[i]=1;
sort(a+1,a+n+1);
REP(i,2,n) printf("%d ",a[i]);
return 0;
}