Codeforces Round #836 (Div. 2)
Preface
补题,上周末没比赛很难受啊,而且这周要考CET-4,这周的模考听力只错了2pts,感觉自信满满flag~~
嘛值得一提的是学校还是沦陷了,让我们自愿返乡了
但是我知道以我的自制力现在回去明年来缓考肯定是寄的,所以在得知学校大概率有阳的情况下继续留守
嘛感觉不管回不回去对我没太大影响的说
这场一晚上只做了ABCD,主要是D我写完自己感觉有问题在改,结果交上去直接过了?
A. SSeeeeiinngg DDoouubbllee
SB题
#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
int t,n,c[26]; char s[105];
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i,j; for (scanf("%s",s+1),n=strlen(s+1),i=1;i<=n;++i) ++c[s[i]-'a'];
for (i=0;i<26;++i) for (j=1;j<=c[i];++j) putchar(i+'a');
for (i=25;~i;--i) for (j=1;j<=c[i];++j) putchar(i+'a');
for (putchar('\n'),i=0;i<26;++i) c[i]=0;
}
return 0;
}
B. XOR = Average
好像我的构造方法很奇怪,让我构思了挺久
首先我们发现当\(n\)为奇数时直接令所有数等于\(1\)即可,接下来浅显地考虑下\(n=2\)的情况,发现用1 3
即可
然后我们发现这样当\(n\)质因数分解后仅有一个\(2\)的话,我们只要用\(\frac{n}{2}\)组1 3
即可
这就启发了我们,我们只要找出所有\(2^k\)的分法,然后让\(\frac{n}{2^k}\)为奇数构造即可
手玩一下不难发现\(n=4\)的时候可以用3 3 3 7
来构造,同理\(n=8\)有7 7 7 7 7 7 7 15
以此类推,\(2^k\)的话就是由\(2^k-1\)个\(2^k-1\)和一个\(2^{k+1}-1\)构成
#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
int t,n;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i,j; scanf("%d",&n); int p=1,tmp=n;
while (!(tmp&1)) tmp>>=1,p<<=1;
for (i=1;i<=n/p;++i) for (printf("%d ",(p<<1)-1),j=1;j<p;++j)
printf("%d ",p-1); putchar('\n');
}
return 0;
}
C. Almost All Multiples
刚开始没看到字典序最小的限制,以为是随便输一种(那不是SB题嘛)
然后随便写了个发现一直WA,后来去仔细看了眼题目才发现是字典序最小
首先我们发现用\(n\)放在\(a_x\)上,其它\(2\sim n-1\)都等于自己是一定可行的,那么我们考虑优化字典序
不难发现\(2\sim x-1\)的数不能变,因为它们已经足够小
那么考虑在\(x+1\sim n-1\)中找一个数可以替换\(x\),不难发现这个数\(y\)要满足\(x|y\and y|n\)
同时如果用\(y\)替换了\(a_x\)那后面依然可以如法炮制,看在后面能否找一个数替换\(a_y\)即可
重复这个过程即可
#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
int t,n,x,ans[N];
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i; scanf("%d%d",&n,&x); ans[1]=x; ans[n]=1;
if (n%x) { puts("-1"); continue; }
if (n==x)
{
for (printf("%d ",n),i=2;i<n;++i) printf("%d ",i);
printf("1\n"); continue;
}
for (i=2;i<x;++i) ans[i]=i; int cur=x; for (i=x+1;i<n;++i)
if (i%cur==0&&n%i==0) ans[cur]=i,cur=i; else ans[i]=i;
for (ans[cur]=n,i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]);
}
return 0;
}
D. Range = √Sum
莫名奇妙的乱搞做法,但就是能过的说
首先一个naive的想法就是确定\(x\),令\(\sqrt {\sum_{i=1}^n a_i}=x\),然后考虑确定\(y\)作为最小数,\(y+x\)作为最大数
不难发现这样我们只要在剩下的\([y+1,y+x-1]\)中找出\(n-2\)个不相同的数,使得这\(n-2\)个数的和等于\(x^2-y-(y+x)\)即可
从\([y+1,y+x-1]\)取数是存在范围的,如果拿最小的\(n-2\)个就是\((n-2)\times (y+1)+\frac{(n-3)(n-2)}{2}\),拿最大的\(n-2\)个就是\((n-2)\times (y+x-1)-\frac{(n-3)(n-2)}{2}\)
不难发现在这中间的任意一个数都能被表示出来,那么我们现在就考虑怎样在确定\(x\)的情况下求出\(y\),即要求不等式成立:
即
那么我们只要枚举\(x\),然后看是否存在一个整数\(y\)满足上式即可
当\(x,y\)都确定后构造答案就非常简单了,先设\(n-2\)个数都取最小的
然后从后往前考虑每个数,差多少就往后移即可,如果不够的话就移到当前没用过的最大的数,这个具体看代码很好理解
综上,虽然我们不知道枚举\(x\)的上界是多少,但是根据直觉这个东西应该不大(毕竟区间的变化是平方级别的),因此可以通过
#include<cstdio>
#include<iostream>
#include<cmath>
#define RI register int
#define CI const int&
using namespace std;
const int N=300005;
int t,n,ans[N];
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i,j; if (scanf("%d",&n),n==2) { puts("1 3"); continue; }
for (i=n-1;;++i)
{
int L=ceil((1.0*i*i+1.0*i*(1-n)+0.5*n*n-1.5*n+1)/n);
int R=floor((1.0*i*i-i-0.5*n*n-1.5*n+1)/n); if (L>R) continue;
long long sum=1LL*(n-2)*(L+1)+1LL*(n-3)*(n-2)/2LL;
long long tar=1LL*i*i-i-2LL*L; int lim=L+i-1;
for (j=2;j<=n-1;++j) ans[j]=L+j-1;
for (j=n-1;j>=2&&sum<tar;--j)
if (lim-ans[j]>=tar-sum) ans[j]+=tar-sum,sum=tar;
else sum+=lim-ans[j],ans[j]=lim,--lim;
ans[1]=L; ans[n]=L+i; break;
}
for (i=1;i<=n;++i) printf("%d%c",ans[i]," \n"[i==n]);
}
return 0;
}
E. Tick, Tock
意外的simple啊这题
首先不难发现我们可以先单独考虑每一列\(j\),对于其中两个不同行且有初始值的元素\(a_{p,j},a_{q,j}\),我们显然可以确定下第\(p\)行和第\(q\)行的相对操作次数
那么我们把所有这样的关系建成一个图,每一行就是一个点,两点\(p,q\)之间的边权就是上面提到的两个对应元素的差值
那么我们只需要通过一个DFS就能判断是否合法了,下面考虑求出答案的个数
不难发现如果所有点都在一个联通块里,答案是唯一的
那么考虑如果多了一个联通块,那么显然此时答案为\(h\),因为同一个联通块内的状态其实都是紧密统一的,整个联通块的方案数乘上\(h\)就相当于所有位置的方案乘上\(h\)
因此设联通块个数为\(cnt\),则答案为\(h^{cnt-1}\)
但是有一个细节就是一整列都是空的这种情况,此时这一列的取法可以任意的整体增减一个数
因此设空列的个数为\(empty\),答案再乘上\(h^{empty}\)即可
#include<cstdio>
#include<utility>
#include<vector>
#define RI register int
#define CI const int&
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
const int N=200005,mod=1e9+7;
typedef pair <int,int> pi;
int t,n,m,h,d[N]; vector <int> a[N]; vector <pi> v[N]; bool flag;
inline void DFS(CI now,CI sum)
{
d[now]=sum; for (auto it:v[now])
if (!~d[it.fi]) DFS(it.fi,(sum+it.se)%h);
else if (d[it.fi]!=(sum+it.se)%h) flag=0;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%d",&t);t;--t)
{
RI i,j; for (scanf("%d%d%d",&n,&m,&h),i=1;i<=n;++i)
d[i]=-1,a[i].resize(m+1),v[i].clear();
for (i=1;i<=n;++i) for (j=1;j<=m;++j) scanf("%d",&a[i][j]);
int cnt=0; for (j=1;j<=m;++j)
{
bool all_empty=1; int pre=-1;
for (i=1;i<=n;++i) if (~a[i][j])
{
if (~pre)
{
v[pre].pb(mp(i,(a[i][j]-a[pre][j]+h)%h));
v[i].pb(mp(pre,(a[pre][j]-a[i][j]+h)%h));
}
all_empty=0; pre=i;
}
if (all_empty) ++cnt;
}
for (flag=1,i=1;i<=n;++i) if (!~d[i]) ++cnt,DFS(i,0);
if (!flag) { puts("0"); continue; }
int ans=1; for (i=1;i<=cnt-1;++i) ans=1LL*ans*h%mod;
printf("%d\n",ans);
}
return 0;
}
Postscript
F怎么又是3000分的题目啊,看都不敢看一眼
我现在感觉自己可能已经阳了,这周末准备润回家了
学校的nt决策导致现在电专已经沦陷了,而且决定留校考试的都成了小丑
唉只能是世事难料,命途多舛啊
锦城虽云乐,不如早还家。