A. Sum of Odd Integers (CF 1327 A)
题目大意
给定(n,k),问(n)是否能表示成(k)个互不相同的奇数的和。
解题思路
首先(n)和(k)的奇偶性要相同,其次(k)个互不相同的奇数的和的最小值为(1+3+5+...+2k-1=k^{2}),所以需要(n geq k^{2})
神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(void) {
int t;
cin>>t;
while(t--){
LL n,k;
cin>>n>>k;
bool qwq=false;
if ((n-k)%2==0&&n>=k*k) puts("YES");
else puts("NO");
}
return 0;
}
B. Princesses and Princes (CF 1327 B)
题目大意
(n)个公主与(n)个王子配对,每个公主有几个王子的配对意愿。对于标号从小到大的每个公主,找公主有配对意愿的王子中最小编号的且未配对的与公主配对。问能否再增加一条配对意愿,使得最终得以配对的公主数增加,能则输出任意一条配对意愿。
解题思路
直接模拟,最后看是否(n)个公主都配对了,不是则找一个未配对的公主和未配对的王子给他们增加一条配对意愿即可。
神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
int main(void) {
int t;
read(t);
while(t--){
int n;
read(n);
bool sign[n+1]={0};
int dau=-1;
for(int i=1;i<=n;++i){
int k;
read(k);
bool qwq=false;
for(int u,j=1;j<=k;++j){
read(u);
if (!qwq&&!sign[u]) {sign[u]=true; qwq=true;}
}
if (!qwq) dau=i;
}
if (dau==-1) puts("OPTIMAL");
else{
int cur=1;
while(sign[cur]) ++cur;
puts("IMPROVE");
printf("%d %d
",dau,cur);
}
}
return 0;
}
C. Game with Chips (CF 1327 C)
题目大意
(n*m)的网格,有(k)个chips。给出了它们的初始位置,然后分别给出每个chip的特定位置,可以进行四种操作:上、下、左、右,即将全部chips向上、下、左、右移动一格,若对于移动会超出边界的chips则在原地不动。问能否在(2mn)的操作次数内,使得每个chip都能至少达到特定位置一次,能则输出操作。
解题思路
一道滑稽构造题。把全部chips移动到左上角然后再遍历整个网格即可。操作数((n-1)+(m-1)+(n-1)*(m-1)=nm-1)
神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main(void) {
int n,m;
cin>>n>>m;
string s;
string quq(n-1,'U');
string qlq(m-1,'L');
string qrq(m-1,'R');
s+=quq;
s+=qlq;
for(int ss=1,i=1;i<=n;++i){
if (ss==1){
s+=qrq;
if (i!=n) s+='D';
ss^=1;
}else{
s+=qlq;
if (i!=n) s+='D';
ss^=1;
}
}
int len=s.size();
cout<<len<<endl<<s<<endl;
return 0;
}
D. Infinite Path (CF 1327 D)
题目大意
给定一个排列(p_1, p_2, dots, p_n),以及每个元素的颜色(c_1, c_2, dots, c_n),同时定义排列的乘法(c = a imes b),即(c[i] = b[a[i]]),其中(a,b)是两个排列。因此我们定义排列的(k)次方(p^k=underbrace{p imes p imes dots imes p}_{k ext{ times}})。求一个最小的正整数(k),使得(p^{k})排列里存在一个无穷序列。所谓无穷序列就是(i, p[i], p[p[i]], p[p[p[i]]] dots),且(c[i] = c[p[i]] = c[p[p[i]]] = dots)
解题思路
很显然,如果有自环的话(k)最小为(1)。
一个排列所形成的置换形成一个个环,我们观察(p)的次方的定义可知,一个个环之间是相互独立的,所以我们分别考察每个环,看看每个点的颜色是否都相同。
对于一个环来说,有(m)个点,如果是(p^{1}),就相当于对于一个点,它往前一位选一个点,直到已经选到的点为止。而如果是(p^{2}),则它往前两位选一个点,直到已经选到的点为止。
如果(k)选得好,一个环会被拆成(k)个小环,而如果原来的大环不符合要求,而小环有可能符合要求。至于是哪些(k)能把大环拆成小环,很容易得知当(k)是(m)的因数的话就能拆成小环。所以我们枚举(m)的因数分别去判断是否可行,取最小值即可。
第一次在传参时vector没加&然后MLE……
神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
bool check(vector<int> &ji,int st,int step,int c[]){
for(size_t i=st+step;i<ji.size();i+=step){
if (c[ji[i]]!=c[ji[i-step]]) return false;
}
return true;
}
void work(vector<int> &ji,int c[],int &ans){
int cnt=ji.size();
int up=sqrt(cnt);
for(int i=1;i<=up;++i){
if (cnt%i!=0) continue;
for(int j=0;j<i;++j)
if (check(ji,j,i,c)) {
ans=min(ans,i);
break;
}
if (i!=cnt/i) {
for(int j=0;j<cnt/i;++j)
if (check(ji,j,cnt/i,c)) {
ans=min(ans,cnt/i);
break;
}
}
}
}
void DFS(int u,int nxt[],vector<int> &ji,int c[],bool sign[],int &ans){
sign[u]=true;
ji.push_back(u);
if (sign[nxt[u]]) work(ji,c,ans);
else DFS(nxt[u],nxt,ji,c,sign,ans);
}
int main(void) {
int t;
read(t);
while(t--){
int n;
read(n);
bool qwq=false;
int nxt[n+1]={0};
for(int i=1;i<=n;++i){
read(nxt[i]);
if (nxt[i]==i) qwq=true;
}
int c[n+1]={0};
for(int i=1;i<=n;++i){
read(c[i]);
}
if (qwq){
puts("1");
continue;
}
int ans=1e9+7;
bool sign[n+1]={0};
for(int i=1;i<=n;++i){
if (!sign[i]) {
vector<int> ji;
DFS(i,nxt,ji,c,sign,ans);
}
}
write(ans,'
');
}
return 0;
}
E. Count The Blocks (CF 1327 E)
题目大意
给定(n),有(10^{n})个数字从(0)到(10^{n}-1),包括前导零使得每个数字都有(n)位。现在要分别统计长度从(1)到(n)的数字block的数量。所谓数字block指的是一串连续的相同数位,它不能再往左边或右边拓展(到边界了或者拓展后这串数位不是相同的)
解题思路
一开始以为是数位DP,稍加思索后发现,对于数字block长度为i的数量,分别考虑以每一位开始的长度为i的block对答案的贡献即可。
(i<n-1)时,(ans=81 imes (n-i-1) imes 10^{n-i-1}+18 imes 10^{n-i})
(i=n-1)时,(ans=18 imes 10^{n-i}=180)
(i=n)时,(ans=10)
神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
const LL mo=998244353;
LL qpower(int a,int b){
LL aa=a;
LL qwq=1;
while(b){
if (b&1) qwq=qwq*aa%mo;
aa=aa*aa%mo;
b>>=1;
}
return qwq;
}
int main(void) {
int n;
read(n);
for(int i=1;i<=n;++i){
LL ans=0;
if (i<n-1) ans=(n-i-1)*81%mo*qpower(10,n-i-1)%mo;
if (i==n) ans=10;
else ans=(ans+18*qpower(10,n-i)%mo)%mo;
write(ans);
}
return 0;
}
F. AND Segments (CF 1327 F)
题目大意
给定(n,k,m),有(m)个要求,表示为((l_i,r_i,x_i))。先要求构造一个长度为(n)的数组(a),满足
- (0 leq a_i leq 2^k, forall i in [1,n])
- (a_{l_i}&a_{l_i+1}&...&a_{r_i}=x_{i},forall i in [1,m])
求满足上述两个要求的数组(a)的数量模(998244353)
解题思路
(&)是位运算,我们注意到不同位之间是相互独立的,那么我们就分别计算每一位,每个数在该位的取值的所有情况,最后每一位之间相乘即可得到答案。
对于当前考虑的某一位,对于一个要求((l_i,r_i,x_i)).
如果(x_i)在该位是(1),那么(a_{l_i}、a_{l_i+1}、...、a_{r_i})在该位都一定是(1)。
如果(x_i)在该位是(0),那么(a_{l_i}、a_{l_i+1}、...、a_{r_i})在该位不能全是(1)。
那么现在就要统计一个长度为(n)的序列数量,该序列的每个数字是(0)或(1),且有部分区间的数字全为(1),部分区间的数字不能全为(1)。
注意到(0)是极为特殊的数,我们设(dp[i])表示当前是第(i)个数字,且这个数字为(0)(或最后一个填(0)的位置是(i))的方案数。
如果这个数字是一定为(1)的,则(dp[i]=0)
如果这个数字可以不为(1),我们则枚举上一个填(0)的位置(j),由于(j+1)到(i-1)都是填(1)的,我们不能让这个区间有 不能全为(1)的区间,所以(j)应该从(i-1)到 不能全为(1)的区间 的右端点小于(i)的那些区间 的最大左端点,记为(k),即(k=maxlimits_{r_{j}<i atop x_i在该位为0}left( l_j ight)),所以(dp[i]=sumlimits_{j=k}^{i-1}dp[j])。这个用前缀和维护就好了。
初始化dp[0]=1。
最后把所有位的dp[n+1]相乘即可。
神奇的代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
template <typename T>
void read(T &x) {
int s = 0, c = getchar();
x = 0;
while (isspace(c)) c = getchar();
if (c == 45) s = 1, c = getchar();
while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
if (s) x = -x;
}
template <typename T>
void write(T x, char c = ' ') {
int b[40], l = 0;
if (x < 0) putchar(45), x = -x;
while (x > 0) b[l++] = x % 10, x /= 10;
if (!l) putchar(48);
while (l) putchar(b[--l] | 48);
putchar(c);
}
const LL mo=998244353;
int main(void) {
int n,m,k;
read(n);
read(k);
read(m);
vector<pair<pair<int,int>,int>> q(m);
LL ans=1;
for(int l,r,x,i=0;i<m;++i){
read(l);
read(r);
read(x);
q[i]=make_pair(make_pair(l,r),x);
}
for(int i=0;i<k;++i){
int sign[n+5]={0};
int qwq[n+5]={0};
for(auto & j:q){
if (j.second&1){
sign[j.first.first]++;
sign[j.first.second+1]--;
}else{
qwq[j.first.second+1]=max(j.first.first,qwq[j.first.second+1]);
}
j.second>>=1;
}
LL sum[n+5]={0};
sum[0]=1;
int cnt=0;
int pos=0;
for(int j=1;j<=n+1;++j){
cnt+=sign[j];
pos=max(pos,qwq[j]);
if (cnt) sum[j]=sum[j-1];
else if (pos!=0) sum[j]=((sum[j-1]+sum[j-1])%mo-sum[pos-1]+mo)%mo;
else sum[j]=(sum[j-1]+sum[j-1])%mo;
}
ans=((sum[n+1]-sum[n]+mo)%mo*ans)%mo;
}
write(ans,'
');
return 0;
}