A. Exercising Walk
刚开始一看,cf,div2的A题居然这么长,是很难么,结果仔细才发现这就是一道水题,div2A题的通性就是判断。题目的大概意思就是主人公养了一只猫,这天想让这只猫在一个矩阵里面走动,且不能超出矩阵,是否能完成训练。那么我们可以把上下走动抵消掉只看成向下或者向上走,向左向右同理,而这里只需要一个特判,即矩阵的行或者列只有1格,但猫不需要向上或下走。
主要代码如下:
int T;cin>>T;
while(T--){
bool flag1 = false,flag2 = false;//分别判断猫是否能左右走和上下走
int a,b,c,d;cin>>a>>b>>c>>d;
int x,y,x1,y1,x2,y2;cin>>x>>y>>x1>>y1>>x2>>y2;
int cc = b-a,rr = d-c;//猫向左or右,走上or下。
//特判是否本身就不需要左右走
if(a+b){
//判断矩阵列是否只有一格
if(x2-x1!=0&&x1<=cc+x&&x2>=cc+x)flag1 =true;
}else flag1 = true;
//特判是否本身就不需要上下走
if(c+d){
if(y2-y1!=0&&y1<=rr+y&&y2>=rr+y)flag2 = true;
}else flag2 = true;
if(flag1&&flag2)cout<<"YES\n";
else cout<<"NO\n";
}
B.Composite Coloring
这题的大致意思就是,给你一些合数,要你把他们涂上颜色,总共可以用的颜色种类不能超过11种,且所用的颜色的编号必须连续,不能中断,且gcd(a,b)!=1的可以涂上相同或者说不同的颜色。那由唯一分解定理我们可以知道,任何一个合数都可以分解成若干个素数相乘,颜色种类不能超过11种即素数的数目不超过11个,那么就可以暴力从小到大枚举每个合数的质因子,并给枚举到的质因子附上编号,按出现的先后顺序赋值,这样就不会中断。
主要代码如下:
int b[1005];
int T;cin>>T;
const int primes[]{2,3,5,7,11,13,17,19,23,29,31};//11个由小到大的质因子
int col[32];//质因子最大为31,初始全部赋值为零
while(T--){
int cnt =0;
int n;cin>>n;
int ans = 0;//统计用了几种颜色
for(int i = 0;i<32;++i)col[i]=0;
for(int i =0;i<n;++i){
cin>>b[i];
for(int j = 0;j<11;++j){
//判断是否能被这个质因子整除,若能,染色退出
if(b[i]%primes[j]==0){
//判断质因子是否在之前出现过
if(!col[primes[j]]){
++ans;
col[primes[j]] = ++cnt;
b[i] = col[primes[j]];
}else b[i] = col[primes[j]];
break;
}
}
}
cout<<ans<<'\n';
for(int i =0;i<n;++i)cout<<b[i]<<" \n"[i==n-1];
}
C.K-Complete Word
题意大概就是,给你一个长度为n字符串,要你更改其中的一些字符,使其能变成一个回文串,且是一个周期为k的周期串,求更改字符的最小数目。
预处理+贪心
首先我们要让这个串变成一个周期为k的回文串的话,那么将这个串分为n/k段,每一段都要是回文串,我们只需要统计一下每一段对应的每一位中,每种字符的贡献,再将这一整串看成一段长度为k的字符串,因为这是一个回文串,所以从两端开始遍历,贪心去求,求出贡献最大的字符,用n/k*2-贡献即为这一位对应的所有段需要更改的字符数,要注意段长为奇数偶数的情况。(每次不要用memset去清零统计数组,笔者深受memset毒害 )
主要代码如下:
const int INF = INT_MAX;
const int N = 2e5+5;
int cnt[N][26]
int T; cin >> T;
while (T--) {
int k, n; cin >> n >> k;
string s; cin >> s;
int cut = n / k;//一共有多少段
//手动清零统计数组
for(int i =0;i<n;++i){
for(int j = 0;j<26;++j)
cnt[i][j]=0;
}
//统计每一段对应的位上,每种字符的贡献
for(int i = 0;i<n;++i) {
cnt[i % k][s[i] - 'a']++;//i%k的原因是因为,将一串化为一段
}
ll ans = 0;
//两端开始遍历
for (int i = 0, j = k - 1; i <= j; i++, j--) {
int ma = -INF;
//遍历26个字符,取贡献最大的
for(int k = 0;k<26;++k){
int cost = cnt[i][k]+(i==j?0:cnt[j][k]);//段长为奇数时,会同时遍历到中间
ma = max(ma,cost);
}
if(i==j)ans += cut - ma;
else ans += cut*2 - ma;
}
cout << ans << '\n';
}
D.Walk on Matrix
粗略的一看,哇,这题dp,不想写了。
这题的题意,确实和dp有关,但是实现的时候,不关dp的事。
他给了你一个矩阵nxm,矩阵的每个格子都有权值,玩家的初始分数是
a
1
,
1
a_{1,1}
a1,1,然后从
a
1
,
1
a_{1,1}
a1,1开始,每一步只能向下或者向右走,走到
a
n
,
m
a_{n,m}
an,m,每走一格,玩家的分数为(自身的&该位置的权值),求玩家可获得最大的分数,题目给出了dp的算法,但这个算法是错的,要求你构造一个矩阵,使正确答案的值与错误答案的值相差为k.
如果说用dp来写这道题,必然会出现忽略了更大的值这种情况,因为按位取与和直接相加是不一样的,你可能取与的时候,忽略了更大位数的值。解题大致思想就是诱导致使其只能获得你的值。
因为k<1e5 则我们可以取一个k达不到的数base = 1<<17(二进制数位中,base只有一个1)
如下矩阵
base+k base k
k base+k k
主代码如下:
int k;cin>>k;
int inf = 1<<17;
cout<<2<<' '<<3<<'\n';
cout<<inf+k<<' '<<inf<<' '<<k<<'\n';
cout<<k<<' '<<inf+k<<' '<<k<<'\n';