AtCoder Regular Contest 198 (Div. 2)
A - I hate 1
首先特判 \(n=1\) 这个 corner case。
然后有结论:选取 \(1\sim n\) 中所有偶数合法且最优。
看着就很对,考虑证明。
首先这个构造是合法的,因为所有数都是偶数所以余数不可能是奇数。
其次这个构造是最优的。因为如果选出相邻两个数就寄了,同时注意到 \(1\) 不能和别的数一起选,所以上界就是 \(\lfloor\frac{n}{2}\rfloor\)。
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
#define repll(i,l,r) for(ll i=(l);i<=(r);i++)
#define perll(i,r,l) for(ll i=(r);i>=(l);i--)
#define pb push_back
#define ins insert
#define clr clear
using namespace std;
namespace ax_by_c{
typedef long long ll;
void slv(){
int n;
scanf("%d",&n);
if(n==1){
puts("1");
puts("1");
return ;
}
printf("%d\n",n/2);
for(int i=2;i<=n;i+=2)printf("%d ",i);
}
void main(){
int T=1;
// int csid=0;scanf("%d",&csid);
// scanf("%d",&T);
while(T--)slv();
}
}
int main(){
string __name="";
if(__name!=""){
freopen((__name+".in").c_str(),"r",stdin);
freopen((__name+".out").c_str(),"w",stdout);
}
ax_by_c::main();
return 0;
}
B - Rivalry
相当于 \(1\) 两边有恰好 \(1\) 个 \(0\),\(2\) 两两不相邻。
相当于两个 \(0\) 之间可以插 \(0\sim 2\) 个 \(1\) 和 \(0\sim 1\) 个 \(2\),并且 \(1\) 个 \(1\) 时必须插 \(1\) 个 \(2\)。
首先如果 \(\lceil\frac{y}{2}\rceil>x\) 就无解了,因为 \(1\) 放不完。
如果 \(y\) 是偶数那么肯定是两两放一起,因为单独放还需要配 \(2\),因此 \(z\in[0,x]\) 就有解,否则无解。
如果 \(y\) 是奇数同理可知有一个 \(1\) 要配 \(2\),因此 \(z\in[1,x]\) 就有解,否则无解。
实际上可以写成 \(\lceil\frac{y}{2}\rceil\in[0,x]\) 且 \(z\in[y\bmod 2,x]\)。
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
#define repll(i,l,r) for(ll i=(l);i<=(r);i++)
#define perll(i,r,l) for(ll i=(r);i>=(l);i--)
#define pb push_back
#define ins insert
#define clr clear
using namespace std;
namespace ax_by_c{
typedef long long ll;
void slv(){
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
if((y+1)/2<=x&&y%2<=z&&z<=x)puts("Yes");
else puts("No");
}
void main(){
int T=1;
// int csid=0;scanf("%d",&csid);
scanf("%d",&T);
while(T--)slv();
}
}
int main(){
string __name="";
if(__name!=""){
freopen((__name+".in").c_str(),"r",stdin);
freopen((__name+".out").c_str(),"w",stdout);
}
ax_by_c::main();
return 0;
}
C - Error Swap
首先特判 \(n=2\) 这个 corner case。
序列和显然不变,所以 \(\sum a\neq\sum b\) 就无解。
这种题肯定先找基础操作,可以发现:
-
\((x,x+1),(x+1,y),(x,x+1),(x,y)\) 可以使 \(a_x\leftarrow a_x+1,a_y\leftarrow a_y-1\)。
-
\((x,y),(x,x+1),(x+1,y),(x,x+1)\) 可以使 \(a_x\leftarrow a_x-1,a_y\leftarrow a_y+1\)。
于是可以用 \(4\) 次操作使两个不相邻的元素转移 \(1\) 的量。
考虑从前往后扫,若当前位置需要增加就去后面找一个要减少的位置,反之同理,此处需要 \(2nV\) 次操作。
如果全部能找到就只需考虑 \(n-2,n-1,n\) 了,但是可能找不到。设当前位置为 \(i\),则 \(i+2\sim n\) 所需的变化和 \(i\) 是同类的,只有 \(i+1\) 不是。将 \(i+3\sim n\) 与 \(i+1\) 操作后就只需考虑 \(i,i+1,i+2\) 了。
那么只需要考虑三个数的情况,容易调整第 \(1\) 和 \(3\) 个数使得 \((1,2)\) 后第 \(2\) 个数复原,再次调整第 \(1\) 和 \(3\) 个数即可。此处需要 \(8V+1\) 次操作。
总操作数 \(2nV+8V+1\) 可以通过,以下实现是 \(O(n^2V)\) 的。
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
#define repll(i,l,r) for(ll i=(l);i<=(r);i++)
#define perll(i,r,l) for(ll i=(r);i>=(l);i--)
#define pb push_back
#define ins insert
#define clr clear
using namespace std;
namespace ax_by_c{
typedef long long ll;
typedef pair<int,int> pii;
const int N=105;
int n,a[N],b[N];
vector<pii>ans;
void ad1(int x,int y){
ans.pb({x,x+1});
ans.pb({x+1,y});
ans.pb({x,x+1});
ans.pb({x,y});
}
void rm1(int x,int y){
ans.pb({x,y});
ans.pb({x,x+1});
ans.pb({x+1,y});
ans.pb({x,x+1});
}
void FF(int pos){
int dlt=b[pos]+b[pos+2]-a[pos]-a[pos+2];
int to=a[pos+1]-1-dlt;
while(a[pos]<to){
ad1(pos,pos+2);
a[pos]++,a[pos+2]--;
}
while(a[pos]>to){
rm1(pos,pos+2);
a[pos]--,a[pos+2]++;
}
ans.pb({pos,pos+1});
int L=a[pos],R=a[pos+1];
a[pos]=R-1,a[pos+1]=L+1;
while(a[pos]<b[pos]){
ad1(pos,pos+2);
a[pos]++,a[pos+2]--;
}
while(a[pos]>b[pos]){
rm1(pos,pos+2);
a[pos]--,a[pos+2]++;
}
}
void slv(){
scanf("%d",&n);
rep(i,1,n)scanf("%d",&a[i]);
rep(i,1,n)scanf("%d",&b[i]);
if(n==2){
if(a[1]==b[1]&&a[2]==b[2]){
puts("Yes");
puts("0");
return ;
}
if(a[2]-1==b[1]&&a[1]+1==b[2]){
puts("Yes");
puts("1");
puts("1 2");
return ;
}
puts("No");
return ;
}
int sa=0,sb=0;
rep(i,1,n)sa+=a[i],sb+=b[i];
if(sa!=sb){
puts("No");
return ;
}
puts("Yes");
rep(i,1,n-2){
while(a[i]<b[i]){
int p=0;
rep(j,i+2,n)if(a[j]>b[j])p=j;
if(p){
ad1(i,p);
a[i]++,a[p]--;
}
else{
per(x,n,i+3){
while(a[x]<b[x]){
rm1(i+1,x);
a[i+1]--,a[x]++;
}
}
FF(i);
printf("%d\n",(int)ans.size());
for(auto it:ans)printf("%d %d\n",it.first,it.second);
return ;
}
}
while(a[i]>b[i]){
int p=0;
rep(j,i+2,n)if(a[j]<b[j])p=j;
if(p){
rm1(i,p);
a[i]--,a[p]++;
}
else{
per(x,n,i+3){
while(a[x]>b[x]){
ad1(i+1,x);
a[i+1]++,a[x]--;
}
}
FF(i);
printf("%d\n",(int)ans.size());
for(auto it:ans)printf("%d %d\n",it.first,it.second);
return ;
}
}
}
FF(n-2);
printf("%d\n",(int)ans.size());
for(auto it:ans)printf("%d %d\n",it.first,it.second);
}
void main(){
int T=1;
// int csid=0;scanf("%d",&csid);
// scanf("%d",&T);
while(T--)slv();
}
}
int main(){
string __name="";
if(__name!=""){
freopen((__name+".in").c_str(),"r",stdin);
freopen((__name+".out").c_str(),"w",stdout);
}
ax_by_c::main();
return 0;
}
D - Many Palindromes on Tree
相当于有一些相等的限制,用连边表示,那么一个连通块内必须相等。
为了最优,不同连通块的值肯定不等,于是两个位置相同等价于连通块相同。于是我们只需要求出 dsu 再求答案就好了。
直接做肯定是 \(O(n^3)\) 的,不难发现这两个东西都可以递推,预处理路径后继即可,时间复杂度 \(O(n^2)\)。
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
#define repll(i,l,r) for(ll i=(l);i<=(r);i++)
#define perll(i,r,l) for(ll i=(r);i>=(l);i--)
#define pb push_back
#define ins insert
#define clr clear
using namespace std;
namespace ax_by_c{
typedef long long ll;
const int N=3005;
int n,dis[N][N],nxt[N][N];
vector<int>g[N];
bool f[N][N];
void dfs(int u,int fa,int rt,int d){
dis[rt][u]=d;
for(auto v:g[u]){
if(v==fa)continue;
nxt[rt][v]=u;
dfs(v,u,rt,d+1);
}
}
void ff(int x,int y){
if(f[x][y])return ;
f[x][y]=1;
if(dis[x][y]>1)ff(nxt[y][x],nxt[x][y]);
}
struct DSU{
struct node{
int fa,sz;
}a[N];
void Init(int n){
rep(i,1,n)a[i]={i,1};
}
int find(int x){
if(a[x].fa==x)return x;
return a[x].fa=find(a[x].fa);
}
bool meg(int x,int y){
x=find(x),y=find(y);
if(x==y)return 0;
if(a[x].sz<a[y].sz)swap(x,y);
a[y].fa=x;
a[x].sz+=a[y].sz;
return 1;
}
}dsu;
bool mk[N][N],res[N][N];
bool G(int x,int y){
if(mk[x][y])return res[x][y];
mk[x][y]=1;
if(dsu.find(x)==dsu.find(y)){
if(dis[x][y]<=1)res[x][y]=1;
else res[x][y]=G(nxt[y][x],nxt[x][y]);
}
return res[x][y];
}
void slv(){
scanf("%d",&n);
rep(_,1,n-1){
int u,v;
scanf("%d %d",&u,&v);
g[u].pb(v);
g[v].pb(u);
}
rep(i,1,n)dfs(i,-1,i,0);
rep(x,1,n)rep(y,1,n){
char t;
scanf(" %c",&t);
if(t=='1')ff(x,y);
}
dsu.Init(n);
rep(x,1,n)rep(y,1,n)if(f[x][y])dsu.meg(x,y);
int ans=0;
rep(x,1,n)rep(y,1,n)if(G(x,y))ans++;
printf("%d\n",ans);
}
void main(){
int T=1;
// int csid=0;scanf("%d",&csid);
// scanf("%d",&T);
while(T--)slv();
}
}
int main(){
string __name="";
if(__name!=""){
freopen((__name+".in").c_str(),"r",stdin);
freopen((__name+".out").c_str(),"w",stdout);
}
ax_by_c::main();
return 0;
}
E - Monotone OR
设 \(f_S\) 为变成 \(S\) 的方案数,有 \(f_0=1,f_S=\sum_{x\mid a_i=(S-1)}f_x\)。
设 \(g_S=f_{S+1}\),则 \(f_S=g_{S-1},g_S=\sum_{x\mid a_i=S}f_x\)。
容斥一下就能变成 \(g_S=(\sum_{x\subseteq S}[a_i=x])(\sum_{x\subseteq S}f_x)-\sum_{x\subsetneq S}g_x\)。
这是半在线高维前缀和的形式,时间复杂度 \(O(n2^n)\)。
半在线高维前缀和:
考虑分治,每次先计算左半边,然后计算左半边对右半边的贡献,最后计算右半边。
但是这样会有问题,左半部分的贡献到了右边后可能还会产生多次贡献。
于是利用左半边前缀和确定右半边值后,在右半边内部计算,计算完再计算左半边对右半边前缀和的贡献即可。
结合代码可能更好理解。
#include<bits/stdc++.h>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
#define repll(i,l,r) for(ll i=(l);i<=(r);i++)
#define perll(i,r,l) for(ll i=(r);i>=(l);i--)
#define pb push_back
#define ins insert
#define clr clear
using namespace std;
namespace ax_by_c{
typedef long long ll;
const ll mod=998244353;
const int N=30;
const int S=(1<<24)+5;
int n,m,msk,cnt[S];
ll f[S],g[S],fs[S],gs[S];
void cdq(int l,int r){
if(l==r){
fs[l]=(fs[l]+f[l])%mod;
f[l+1]=g[l]=(fs[l]*cnt[l]%mod-gs[l]+mod)%mod;
fs[l]=f[l];
gs[l]=g[l];
return ;
}
int mid=l+((r-l)>>1);
cdq(l,mid);
for(int x=l,y=mid+1;x<=mid;x++,y++){
fs[y]=(fs[y]+fs[x])%mod;
gs[y]=(gs[y]+gs[x])%mod;
}
cdq(mid+1,r);
for(int x=l,y=mid+1;x<=mid;x++,y++){
fs[y]=(fs[y]+fs[x])%mod;
gs[y]=(gs[y]+gs[x])%mod;
}
}
void slv(int _csid,int _csi){
scanf("%d %d",&n,&m);msk=(1<<n)-1;
rep(i,1,m){
int x;
scanf("%d",&x);
cnt[x]++;
}
rep(j,0,n-1)rep(i,0,msk)if(i&(1<<j))cnt[i]+=cnt[i^(1<<j)];
f[0]=1;
cdq(0,msk);
printf("%lld\n",f[msk+1]);
}
void main(){
int T=1,csid=0;
// scanf("%d",&csid);
// scanf("%d",&T);
rep(i,1,T)slv(csid,i);
}
}
int main(){
string __name="";
if(__name!=""){
freopen((__name+".in").c_str(),"r",stdin);
freopen((__name+".out").c_str(),"w",stdout);
}
ax_by_c::main();
return 0;
}

浙公网安备 33010602011771号