Codeforces 1058 Div 2 (A-F)
在 Print 之前
突然发现已经很久没写过博客了,前一晚才搭好这个博客样式,正好晚上熬夜打一下 Div 2。
赛时3941分,排在471位,T5双指针写挂了,调了一个小时没调出来。
A - MEX Partition
水题一道。
不难发现,答案就是 \(MEX(A)\)。
Code
#include <bits/stdc++.h>
#define pll pair<ll,ll>
#define pld pair<ld,ld>
typedef long long ll;
typedef long double ld;
typedef int praise_long_long;
namespace io {
using namespace std;
inline ll read() {
char x=getchar();
ll ans=0,f=1;
while(x<'0'||x>'9') {
if(x=='-') {
f*=(-1);
}
x=getchar();
}
while(x>='0'&&x<='9') {
ans*=10;
ans+=(x-'0');
x=getchar();
}
return ans*f;
}
inline void print(ll x) {
if(x<0) {
putchar('-');
x=-x;
}
if(x>=10) {
print(x/10);
putchar(x%10+'0');
}
else {
putchar(x+'0');
}
}
}
using namespace io;
const ll N=2e5+5,mod=1e9+7,inf=2e18;
const ld eps=1e-6;
ll n,a[N];
inline void solve() {
n=read();
ll cnt=0;
for(ll i=1;i<=n;i++) {
a[i]=read();
}
sort(a+1,a+1+n);
for(ll i=1;i<=n;i++) {
if(a[i]!=cnt) {
break;
}
else {
while(a[i]==a[i+1]) {
i++;
}
cnt++;
}
}
print(cnt);
puts("");
}
praise_long_long main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ll T=1;
T=read();
while(T--) {
solve();
}
return 0;
}
/**/
B - Distinct Elements
其实不难发现,当 \(a_i\) 在之前没有出现过时,则有 \(b_i-b_{i-1}=i\),这样我们就可以给 \(a_i\) 安一个新的数。
反之若 \(b_i-b_{i-1} \not = i\),则 \(a_i\) 必定与 \(a_{i-(b_i-b_{i-1})}\) 相同。
Code
#include <bits/stdc++.h>
#define pll pair<ll,ll>
#define pld pair<ld,ld>
typedef long long ll;
typedef long double ld;
typedef int praise_long_long;
namespace io {
using namespace std;
inline ll read() {
char x=getchar();
ll ans=0,f=1;
while(x<'0'||x>'9') {
if(x=='-') {
f*=(-1);
}
x=getchar();
}
while(x>='0'&&x<='9') {
ans*=10;
ans+=(x-'0');
x=getchar();
}
return ans*f;
}
inline void print(ll x) {
if(x<0) {
putchar('-');
x=-x;
}
if(x>=10) {
print(x/10);
putchar(x%10+'0');
}
else {
putchar(x+'0');
}
}
}
using namespace io;
const ll N=2e5+5,mod=1e9+7,inf=2e18;
const ld eps=1e-6;
ll n,a[N],b[N];
set<ll> st;
inline void solve() {
n=read();
st.clear();
for(ll i=1;i<=n;i++) {
b[i]=read();
st.insert(i);
}
for(ll i=1;i<=n;i++) {
if(b[i]==b[i-1]) {
a[i]=a[i-1];
}
else {
a[i]=b[i]-b[i-1];
}
ll lt=i-a[i];
if(!lt) {
a[i]=(*st.begin());
st.erase(st.begin());
}
else {
a[i]=a[lt];
}
}
for(ll i=1;i<=n;i++) {
cout<<a[i]<<' ';
}
puts("");
}
praise_long_long main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ll T=1;
T=read();
while(T--) {
solve();
}
return 0;
}
/**/
C - Reverse XOR
拿 \(6\) 举例子,它的二进制是 \(110_2\),我们发现只需在开头加上一个 \(0\) 或在结尾减去一个 \(0\) 即可。
所以可以删去末尾的 \(0\) 来完成或者在开头添加 \(0\) 即可。
注意奇数的回文串中间一位应为 \(0\)。
Code
#include <bits/stdc++.h>
#define pll pair<ll,ll>
#define pld pair<ld,ld>
typedef long long ll;
typedef long double ld;
typedef int praise_long_long;
namespace io {
using namespace std;
inline ll read() {
char x=getchar();
ll ans=0,f=1;
while(x<'0'||x>'9') {
if(x=='-') {
f*=(-1);
}
x=getchar();
}
while(x>='0'&&x<='9') {
ans*=10;
ans+=(x-'0');
x=getchar();
}
return ans*f;
}
inline void print(ll x) {
if(x<0) {
putchar('-');
x=-x;
}
if(x>=10) {
print(x/10);
putchar(x%10+'0');
}
else {
putchar(x+'0');
}
}
}
using namespace io;
const ll N=2e5+5,mod=1e9+7,inf=2e18;
const ld eps=1e-6;
ll n,cnt;
string num,sum;
inline void solve() {
n=read();
cnt=0;
sum="";
num="";
while(n) {
num+=(char)(n%2+'0');
n/=2;
cnt++;
}
reverse(num.begin(),num.end());
for(ll i=cnt;i<=cnt*2;i++) {
string t=sum+num;
string kl=t;
reverse(kl.begin(),kl.end());
if(t==kl) {
if(i&1) {
if(t[i/2]=='0') {
puts("YES");
return ;
}
}
else {
puts("YES");
return ;
}
}
sum+='0';
}
puts("NO");
}
praise_long_long main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ll T=1;
T=read();
while(T--) {
solve();
}
return 0;
}
/**/
D - MAD Interactive Problem
我们可以发现当我们查询第 \(i\) 位之前的查询为 \(0\),但第 \(i\) 位查询时不为 \(0\),那么第 \(i\) 位肯定是查询所得的数。
同理,当我们确定了一个 \(n\) 的排列后可以用同样的方法求出 \(i\) 号元素第一次出现的位置。
用 \(2*n\) 次访问出第二个 \(i\) 的位置,然后再用 \(n\) 次查询来锁定第一个位置,总查询次数是 \(3*n\)。
Code
#include <bits/stdc++.h>
#define pll pair<ll,ll>
#define pld pair<ld,ld>
typedef long long ll;
typedef long double ld;
typedef int praise_long_long;
namespace io {
using namespace std;
inline ll read() {
char x=getchar();
ll ans=0,f=1;
while(x<'0'||x>'9') {
if(x=='-') {
f*=(-1);
}
x=getchar();
}
while(x>='0'&&x<='9') {
ans*=10;
ans+=(x-'0');
x=getchar();
}
return ans*f;
}
inline void print(ll x) {
if(x<0) {
putchar('-');
x=-x;
}
if(x>=10) {
print(x/10);
putchar(x%10+'0');
}
else {
putchar(x+'0');
}
}
}
using namespace io;
const ll N=6e2+5,mod=1e9+7,inf=2e18;
const ld eps=1e-6;
ll n,a[N],num[N],cnt,vis[N];
inline void input() {
cout<<"? "<<cnt;
cout.flush();
for(ll i=1;i<=cnt;i++) {
cout<<' '<<num[i];
cout.flush();
}
puts("");
cout.flush();
}
inline void input1() {
for(ll i=1;i<=cnt;i++) {
num[i]=vis[i];
}
input();
}
inline void solve() {
n=read();
cnt=0;
num[++cnt]=1;
a[1]=0;
for(ll i=2;i<=2*n;i++) {
a[i]=0;
num[++cnt]=i;
input();
ll sum=read();
if(sum) {
a[i]=sum;
vis[sum]=i;
cnt--;
}
}
cnt=n;
for(ll i=1;i<=2*n;i++) {
if(a[i]) {
continue;
}
vis[++cnt]=i;
input1();
ll sum=read();
a[i]=sum;
cnt--;
}
cout<<'!';
cout.flush();
for(ll i=1;i<=2*n;i++) {
cout<<' '<<a[i];
cout.flush();
}
puts("");
cout.flush();
}
praise_long_long main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ll T=1;
T=read();
while(T--) {
solve();
}
return 0;
}
/**/
E - Rectangles
事实上 \(O(n^2 m^2)\) 的复杂度还是挺好想的,只需确定矩形的上下界,然后找到这区间内的所有可以对应的点,更新它们两两之间的点的最小值。
但如何把复杂度压下来呢?
不难发现,最费事情的就是更新最小值,那有没有办法降低这个复杂度呢?
事实上,我们可以用一个十分经典的 \(min\) 前缀和来实现。
因为我们是从上往下来搜索的,所以上面的部分是不会被更新的。
而下面部分,如果说这一列的后面的值比当前位置的小,又因为当前位置到矩形的上方是可以被后面包含的,那么就把这一位更新为后一位。
这样的复杂度为 \(O(n^2 m)\)
但当 \(n\) 很大时复杂度爆表,但反向枚举 \(m\) 却更优,所以当 \(n>m\) 时就枚举 \(m\) 即可,思路与上方一致。
最终复杂度:\(O(min(n,m)nm)\)。
Code
#include <bits/stdc++.h>
#define pll pair<ll,ll>
#define pld pair<ld,ld>
typedef long long ll;
typedef long double ld;
typedef int praise_long_long;
namespace io {
using namespace std;
inline ll read() {
char x=getchar();
ll ans=0,f=1;
while(x<'0'||x>'9') {
if(x=='-') {
f*=(-1);
}
x=getchar();
}
while(x>='0'&&x<='9') {
ans*=10;
ans+=(x-'0');
x=getchar();
}
return ans*f;
}
inline void print(ll x) {
if(x<0) {
putchar('-');
x=-x;
}
if(x>=10) {
print(x/10);
putchar(x%10+'0');
}
else {
putchar(x+'0');
}
}
}
using namespace io;
const ll N=2.5e5+5,mod=1e9+7,inf=2e18;
const ld eps=1e-6;
ll n,m;
vector<ll> num;
inline void solve() {
n=read(),m=read();
vector<ll> v[min(n,m)+5];
ll ans[n+5][m+5];
for(ll i=1;i<=min(n,m);i++) {
v[i].clear();
}
for(ll i=1;i<=n;i++) {
for(ll j=1;j<=m;j++) {
ans[i][j]=inf;
}
}
for(ll i=1;i<=n;i++) {
for(ll j=1;j<=m;j++) {
char c;
cin>>c;
if(c=='1') {
if(n<=m) {
v[i].push_back(j);
}
else {
v[j].push_back(i);
}
}
}
}
if(n<=m) {
for(ll u=1;u<=n;u++) {
if(v[u].size()<2) {
continue;
}
for(ll d=u+1;d<=n;d++) {
if(v[d].size()<2) {
continue;
}
vector<ll> al=v[u],bl=v[d];
ll l=0,r=0;
num.clear();
while(l<al.size()&&r<bl.size()) {
if(al[l]==bl[r]) {
num.push_back(bl[r]);
l++;
r++;
}
else if(al[l]<bl[r]) {
l++;
}
else {
r++;
}
}
// cout<<u<<' '<<d<<endl;
// for(auto it : num) {
// cout<<it<<' ';
// }
// puts("");
if(num.size()<2) {
continue;
}
for(ll i=1;i<num.size();i++) {
for(ll j=num[i-1];j<=num[i];j++) {
ans[d][j]=min(ans[d][j],(d-u+1)*(num[i]-num[i-1]+1));
}
}
}
for(ll i=n-1;i>=u;i--) {
for(ll j=1;j<=m;j++) {
ans[i][j]=min(ans[i][j],ans[i+1][j]);
}
}
}
}
else {
for(ll u=1;u<=m;u++) {
if(v[u].size()<2) {
continue;
}
for(ll d=u+1;d<=m;d++) {
if(v[d].size()<2) {
continue;
}
vector<ll> al=v[u],bl=v[d],cl;
ll l=0,r=0;
num.clear();
while(l<al.size()&&r<bl.size()) {
if(al[l]==bl[r]) {
num.push_back(bl[r]);
l++;
r++;
}
else if(al[l]<bl[r]) {
l++;
}
else {
r++;
}
}
if(num.size()<2) {
continue;
}
for(ll i=1;i<num.size();i++) {
for(ll j=num[i-1];j<=num[i];j++) {
ans[j][d]=min(ans[j][d],(d-u+1)*(num[i]-num[i-1]+1));
}
}
}
for(ll i=m-1;i>=u;i--) {
for(ll j=1;j<=n;j++) {
ans[j][i]=min(ans[j][i],ans[j][i+1]);
}
}
}
}
for(ll i=1;i<=n;i++) {
for(ll j=1;j<=m;j++) {
if(ans[i][j]==inf) {
ans[i][j]=0;
}
print(ans[i][j]);
putchar(' ');
}
puts("");
}
}
praise_long_long main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ll T=1;
T=read();
while(T--) {
solve();
}
return 0;
}
F - Twin Polynomials
这题说实话只要想明白了就很简单,但想歪了……
我们发现,如果 \(a_i\) 有值但 \(a_{a_i}\) 没值时,\(a_{a_i}\) 必定等于 \(i\)。
如果 \(a_i\) 有值 \(a_{a_i}\) 也有值时,若 \(a_{a_i} \not = i\) 时,则这个方案无效,否则继续。
但剩下的怎么办呢?
不难想到,想让 \(a_i\) 合法,他只有可能等于 \(0\)、等于 \(i\)、连向其他一个点这三种情况。
就这样我们不难想到这是个 \(dp\)。
设 \(f_i\) 表示当有i个点剩余的合法方案数,等于 \(0\) 就是加上 \(f_{i-1}\),等于自己也是加上 \(f_{i-1}\),而选择另一个点就是加上 \((i-1)*f_{i-2}\)。
所以状态转移就是:
注意 \(a_n\) 不能为 \(0\),所以如果最后 \(a_n=-1\) 答案需要减掉 \(f_{cnt-1}\),其中 \(cnt\) 为剩余的 \(a_i=-1\) 的个数。
Code
#include <bits/stdc++.h>
#define pll pair<ll,ll>
#define pld pair<ld,ld>
typedef long long ll;
typedef long double ld;
typedef int praise_long_long;
namespace io {
using namespace std;
inline ll read() {
char x=getchar();
ll ans=0,f=1;
while(x<'0'||x>'9') {
if(x=='-') {
f*=(-1);
}
x=getchar();
}
while(x>='0'&&x<='9') {
ans*=10;
ans+=(x-'0');
x=getchar();
}
return ans*f;
}
inline void print(ll x) {
if(x<0) {
putchar('-');
x=-x;
}
if(x>=10) {
print(x/10);
putchar(x%10+'0');
}
else {
putchar(x+'0');
}
}
}
using namespace io;
const ll N=4e5+5,mod=1e9+7,inf=2e18;
const ld eps=1e-6;
ll n,a[N],cnt,f[N];
inline void solve() {
n=read();
cnt=0;
for(ll i=0;i<=n;i++) {
a[i]=read();
}
bool fl=0;
for(ll i=1;i<=n;i++) {
if(a[i]==-1||a[i]==0) {
continue;
}
if(a[i]>n||(i!=a[a[i]]&&a[a[i]]!=-1)) {
fl=1;
break;
}
if(a[a[i]]==-1) {
a[a[i]]=i;
}
}
if(fl) {
puts("0");
return ;
}
for(ll i=1;i<=n;i++) {
if(a[i]==-1) {
cnt++;
}
}
f[0]=1,f[1]=2;
for(ll i=2;i<=cnt;i++) {
f[i]=f[i-1]*2+(i-1)*f[i-2];
f[i]%=mod;
}
if(a[n]==-1) {
f[cnt]-=f[cnt-1];
f[cnt]+=mod;
f[cnt]%=mod;
}
print(f[cnt]);
puts("");
}
praise_long_long main() {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ll T=1;
T=read();
while(T--) {
solve();
}
return 0;
}
/**/

浙公网安备 33010602011771号