Preface

Atcoder的题都好劲啊,都是我做不动的计数构造

就当锻炼自己的思维能力了(基本都是bzt教的)


A - XOR Circle

bzt说这题数据太水了只要判一下所有数异或值是否为\(0\)就能过,但我们要考虑正解(数据太弱我也不知道到底对不对)

首先我们发现放数的时候必然会出现\(a_1\to a_1\operatorname{xor} a_2\to a_2\to a_1\)的情况,即三个数一组的循环

那么我们可以得到一个普遍的结论:只有三种数且每种数字个数相同

然后你直接提交就会WA掉,原因是忽略了\(0\)的情况,因此我们要加上下面两个特判:

  1. 全为\(0\)时显然合法
  2. \(3|n\)且只有两种数(一种是\(0\)),\(0\)的个数为\(\frac{n}{3}\),非零的那个数个数为\(\frac{2n}{3}\)。假设非零数为\(x\),显然可以放置成\(x\to 0\to x\to x\to 0\to x\to \cdots x\to 0\to x\)
#include<cstdio>
#include<cctype>
#include<map>
#include<utility>
#define RI register int
#define CI const int&
#define Tp template <typename T>
#define fi first
#define se second
using namespace std;
typedef pair <int,int> pi;
const int N=100005;
int n,x,size; map <int,int> C;
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
char Fin[S],*A,*B;
public:
Tp inline void read(T& x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
#undef tc
}F;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (F.read(n),i=1;i<=n;++i)
F.read(x),(!C.count(x))&&(++size),++C[x];
if (size>3) return puts("No"),0; pi a,b,c;
a=*C.begin(); C.erase(C.begin());
if (size==1) return puts(a.fi?"No":"Yes"),0;
if (n%3) return puts("No"),0; b=*C.begin(),C.erase(C.begin());
if (size==2) return puts(!a.fi&&(a.se==n/3&&b.se==n/3*2)?"Yes":"No"),0;
c=*C.begin(),C.erase(C.begin());
if ((a.fi^b.fi)!=c.fi) return puts("No"),0;
if (a.se!=b.se||a.se!=c.se) puts("No"); else puts("Yes"); return 0;
}

B - Even Degrees

构造题,首先一眼发现\(m\)为奇数必然无解,考虑偶数的构造

我们考虑先找出一棵生成树,然后将所有非树边随便定向

然后考虑对于生成树上的边,如果我们以及处理完了其子树内的所有边,那么我们可以直接根据此时它的出度来决定它与父亲的边的方向

然后这样除了根节点所有节点出度都为偶数,然后总边数也是偶数,因此根节点出度必为偶数,于是得到了一种构造方案

#include<cstdio>
#include<cctype>
#define RI register int
#define CI const int&
#define Tp template <typename T>
using namespace std;
const int N=100005;
struct edge
{
int to,nxt,id;
}e[N<<1]; int head[N],n,cnt,m,x[N],y[N],out[N],ansx[N],ansy[N],anc[N]; bool vis[N],cs[N];
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch))
char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[15];
public:
FileInputOutput() { Ftop=Fout; Fend=Fout+S; }
Tp inline void read(T& x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
Tp inline void write(T x,const char& ch)
{
RI ptop=0; while (pt[++ptop]=x%10,x/=10);
while (ptop) pc(pt[ptop--]+48); pc(ch);
}
inline void flush(void)
{
fwrite(Fout,1,Ftop-Fout,stdout);
}
#undef tc
#undef pc
}F;
inline void addedge(CI x,CI y,CI z)
{
e[++cnt]=(edge){y,head[x],z}; head[x]=cnt;
e[++cnt]=(edge){x,head[y],z}; head[y]=cnt;
}
#define to e[i].to
inline void DFS1(CI now=1)
{
vis[now]=1; for (RI i=head[now];i;i=e[i].nxt)
if (!vis[to]) cs[e[i].id]=1,anc[to]=e[i].id,DFS1(to);
}
inline void DFS2(CI now=1,CI fa=0)
{
for (RI i=head[now];i;i=e[i].nxt) if (cs[e[i].id]&&to!=fa) DFS2(to,now);
if (out[now]&1) ansx[anc[now]]=now,ansy[anc[now]]=fa;
else ansx[anc[now]]=fa,ansy[anc[now]]=now,++out[fa];
}
#undef to
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (F.read(n),F.read(m),i=1;i<=m;++i)
F.read(x[i]),F.read(y[i]),addedge(x[i],y[i],i);
if (m&1) return puts("-1"),0;
for (DFS1(),i=1;i<=m;++i) if (!cs[i])
++out[x[i]],ansx[i]=x[i],ansy[i]=y[i];
for (DFS2(),i=1;i<=m;++i) F.write(ansx[i],' '),F.write(ansy[i],'\n');
return F.flush(),0;
}

C - Skolem XOR Tree

又是构造题,先把无解判了,发现若\(n\)是\(2\)的幂次必然无解(小于它的数这一位上不可能为\(1\))

否则我们可以发现现在的\(n\ge 3\),而\(n=3\)的构造方案样例已经告诉我们了,我们考虑在上面扩展

每次我们按序添加两个点\(i,i+1\),其中\(i\)为偶数,那么我们显然可以构造一条\((i,i+1)-(1,i)-(1,n+i+1)-(n+i+1,n+i)\)的链,显然这样链上就满足了条件

因此若\(n\)为奇数就做完了,然后若\(n\)为偶数只可能剩下最后一个点没选,相当于找一条树上路径异或和为\(n\),直接前缀和处理一下即可

#include<cstdio>
#include<vector>
#include<cstdlib>
#include<map>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,cur,pfx[N<<1]; vector <int> v[N<<1]; map <int,int> Hash;
inline void addedge(CI x,CI y)
{
printf("%d %d\n",x,y); v[x].push_back(y); v[y].push_back(x);
}
inline void DFS(CI now=1,CI fa=0)
{
pfx[now]=pfx[fa]^(now<=n?now:now-n);
if (pfx[now]==n) addedge(1,n),addedge(now,n<<1),exit(0);
if (Hash.count(pfx[now]^n^1))
addedge(Hash[pfx[now]^n^1],n),addedge(now,n<<1),exit(0);
Hash[pfx[now]]=now; for (vector <int>::iterator it=v[now].begin();it!=v[now].end();++it)
if (*it!=fa) DFS(*it,now);
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
scanf("%d",&n); for (cur=1;cur<n;cur<<=1);
if (cur==n) return puts("No"),0; puts("Yes");
addedge(1,2); addedge(2,3); addedge(3,n+1); addedge(n+1,n+2); addedge(n+2,n+3);
for (RI i=5;i<=n;i+=2) addedge(1,i-1),addedge(i,i-1),addedge(1,n+i),addedge(n+i,n+i-1);
if (n&1) return 0; return DFS(),0;
}

D - Add and Remove

思路很妙,代码很短的一题

首先看到数据范围一眼DP,但是发现状态根本不好表示

因此考虑倒序处理这个过程,每次相当于从两边向中间吐出一个数,这样我们就可以求出每个数对答案的贡献\(c_i\)然后乘上位置上的值即可

考虑如果有很多数,它们的贡献\(c_i\)已知,那么如果我们将\(i\)与\(i+1\)间的数吐出来,那么这个数对答案的贡献就是\(c_i+c_{i+1}\)

因此我们可以直接搞一个DP,定义\(f_{l,r,cl,cr}\)表示区间\((l,r)\),左端点的贡献是\(cl\),右端点的贡献是\(cr\),考虑转移:

\[f_{l,r,cl,cr}=\min_{m=l+1}^{r-1} f_{l,m,cl,cl+cr}+f_{m,r,cl+cr,cr}+(cl+cr)*a[m]
\]

那么最后的答案就是\(f_{1,n,1,1}+a_1+a_n\),然后由于一些奇怪的东西这个的状态数是\(O(2^n\times Poly(n))\)的,因此直接爆搜就好了

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=20;
int n,a[N];
inline long long DFS(CI l,CI r,CI cl,CI cr)
{
if (r-l+1<=2) return 0; long long ret=1e18;
for (RI i=l+1;i<r;++i) ret=min(ret,DFS(l,i,cl,cl+cr)+DFS(i,r,cl+cr,cr)+1LL*(cl+cr)*a[i]);
return ret;
}
signed main()
{
RI i; for (scanf("%d",&n),i=1;i<=n;++i)
scanf("%d",&a[i]); return printf("%lld",DFS(1,n,1,1)+a[1]+a[n]),0;
}

E - Develop

神仙计数题,被神仙bzt出在了模拟赛里

首先考虑将原问题转化,我们可以发现对于一个状态,可以将每一个数看做一个点,那么如果\(x\)在集合内就向\(x-2\)和\(x+k\)(如果存在)连一条有向边,如果最后得出的图无环即有解

那么我们分\(k\)的奇偶性讨论,首先如果\(k\)是偶数我们可以将原问题的奇偶数分别考虑最后乘起来

那么现在问题等价于选一个数\(x\)就不能选\(x-1\)和\(x+\frac{k}{2}\)

发现其实这就等价于不能有连续\(\frac{k}{2}+1\)个\(1\)被选,直接令\(f_{i,j}\)表示第\(i\)位结尾有\(j\)个连续的\(1\)再DP即可

考虑\(k\)为奇数的情况,我们发现一个环必然是\(a\to a-2\to a-4\to \cdots a-2d+k\to a-2(d+1)+k\to \cdots \to a-2(k-1)+2k\to a\)

即先减去一些\(2\)然后加上一个\(k\)再减去一些\(2\)再加上一个\(k\)

那么分析一下就是选出\(k\)个\(-2\)和\(2\)个\(k\),转化一下就变成选出\(k\)个\(2\)和\(2\)个\(-k\)

我们考虑一种神奇的建模,将奇偶两排点建在左右两边,然后将两排数对齐使得左边的奇数+\(k\)=右边的偶数

然后我们发现一条不合法的路径(即出现环)就是这张图上长度大于等于\(k+2\)的路径(多画画图理解下)

因此我们定义DP状态\(f_{i,j,k}\)表示做到第\(i\)行,一直向下走的路径长为\(j\),走成下-左-下的路径长为\(k\),那么所有\(k\le k+1\)的路径都是合法的,直接转移即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=155;
int n,k,mod;
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
namespace Case1 //Even number solver
{
int f[N][N];
inline int calc(CI n,CI k)
{
RI i,j; for (f[0][0]=i=1;i<=n;++i) for (f[i][0]=f[i-1][0],j=1;j<=k;++j)
inc(f[i][0],f[i-1][j]),f[i][j]=f[i-1][j-1];
int ret=0; for (i=0;i<=k;++i) inc(ret,f[n][i]); return ret;
}
inline void solve(void)
{
printf("%d",1LL*calc(n>>1,k>>1)*calc(n-(n>>1),k>>1)%mod);
}
};
namespace Case2 //Odd number solver
{
int f[N][N][N],ans;
inline void solve(void)
{
RI i,j,p; for (f[0][0][0]=i=1;i<=n;++i)
{
int r=i<<1,l=r-k,fl=1<=l&&l<=n,fr=1<=r&&r<=n;
for (j=0;j<=n;++j) for (p=0;p<=k+1;++p)
{
inc(f[i][0][0],f[i-1][j][p]); if (fr) inc(f[i][j+1][0],f[i-1][j][p]);
if (fl&&p+1<k+2) inc(f[i][0][p?p+1:p],f[i-1][j][p]);
if (fl&&fr&&max(j+2,p+1)<k+2) inc(f[i][j+1][max(j+2,p+1)],f[i-1][j][p]);
}
}
for (i=0;i<=n;++i) for (j=0;j<=k+1;++j)
inc(ans,f[n][i][j]); printf("%d",ans);
}
};
int main()
{
scanf("%d%d%d",&n,&k,&mod); if (k&1) Case2::solve();
else Case1::solve(); return 0;
}

F - Two Histograms

妙不可言的一题,首先我们发现一些不同的操作序列\(k,l\)可能会构造出相同的矩阵,那么我们考虑将一些能构造出相同矩阵的序列用一种方法表示出来就不会计重了

考虑我们可以尽量把一种方案中的\(l\)中的一个数从\(l\)移动到\(k\)中,推导一下发现就是要满足\(k_i+1=j\)且\(l_j=i\)的点对\((i,j)\)

因此我们发现这样每个\(i\)只会和一个\(j\)唯一配对,因此我们可以直接计算出大于等于\(i\)对配对的方案数

那么我们枚举点对数目\(i\),容斥计算即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=500005,mod=998244353;
int n,m,d,ans,fact[N],inv[N],pwn[N],pwm[N];
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
inline void dec(int& x,CI y)
{
if ((x-=y)<0) x+=mod;
}
inline int quick_pow(int x,int p=mod-2,int mul=1)
{
for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void init(CI n,CI m)
{
RI i; for (fact[0]=i=1;i<=m;++i) fact[i]=1LL*fact[i-1]*i%mod;
for (inv[m]=quick_pow(fact[m]),i=m-1;~i;--i) inv[i]=1LL*inv[i+1]*(i+1)%mod;
for (pwn[0]=i=1;i<=m;++i) pwn[i]=1LL*pwn[i-1]*(n+1)%mod;
for (pwm[0]=i=1;i<=m;++i) pwm[i]=1LL*pwm[i-1]*(m+1)%mod;
}
inline int C(CI n,CI m)
{
return 1LL*fact[n]*inv[m]%mod*inv[n-m]%mod;
}
inline int A(CI n,CI m)
{
return 1LL*fact[n]*inv[n-m]%mod;
}
int main()
{
scanf("%d%d",&n,&m); if (n>m) swap(n,m);
init(n,m); for (RI i=0;i<=n;++i)
{
int d=1LL*C(n,i)*A(m,i)%mod*pwm[n-i]%mod*pwn[m-i]%mod;
if (i&1) dec(ans,d); else inc(ans,d);
}
return printf("%d",ans),0;
}

Postscript

感觉AGC好考验思维啊,我可能需要多做一点这样的题目QAQ

AtCoder Grand Contest 035的更多相关文章

  1. 【AtCoder】AtCoder Grand Contest 035 解题报告

    点此进入比赛 \(A\):XOR Circle(点此看题面) 大致题意: 给你\(n\)个数,问是否能将它们摆成一个环,使得环上每个位置都是其相邻两个位置上值的异或值. 先不考虑\(0\),我们假设环 ...

  2. AtCoder Grand Contest 035 简要题解

    从这里开始 题目目录 Problem A XOR Circle 你发现,权值的循环节为 $a_0, a_1, a_0\oplus a_1$,然后暴力即可. Code #include <bits ...

  3. AtCoder Grand Contest 012

    AtCoder Grand Contest 012 A - AtCoder Group Contest 翻译 有\(3n\)个人,每一个人有一个强大值(看我的假翻译),每三个人可以分成一组,一组的强大 ...

  4. AtCoder Grand Contest 011

    AtCoder Grand Contest 011 upd:这篇咕了好久,前面几题是三周以前写的... AtCoder Grand Contest 011 A - Airport Bus 翻译 有\( ...

  5. AtCoder Grand Contest 031 简要题解

    AtCoder Grand Contest 031 Atcoder A - Colorful Subsequence description 求\(s\)中本质不同子序列的个数模\(10^9+7\). ...

  6. AtCoder Grand Contest 010

    AtCoder Grand Contest 010 A - Addition 翻译 黑板上写了\(n\)个正整数,每次会擦去两个奇偶性相同的数,然后把他们的和写会到黑板上,问最终能否只剩下一个数. 题 ...

  7. AtCoder Grand Contest 009

    AtCoder Grand Contest 009 A - Multiple Array 翻译 见洛谷 题解 从后往前考虑. #include<iostream> #include< ...

  8. AtCoder Grand Contest 008

    AtCoder Grand Contest 008 A - Simple Calculator 翻译 有一个计算器,上面有一个显示按钮和两个其他的按钮.初始时,计算器上显示的数字是\(x\),现在想把 ...

  9. AtCoder Grand Contest 007

    AtCoder Grand Contest 007 A - Shik and Stone 翻译 见洛谷 题解 傻逼玩意 #include<cstdio> int n,m,tot;char ...

随机推荐

  1. Azure EA (2) 使用Postman访问国内Azure Billing API

    <Windows Azure Platform 系列文章目录> 本文介绍的是国内由世纪互联运维的Azure China 请读者先看一下之前的文档内容:Azure EA (1) 查看国内Az ...

  2. Ubuntu 16.04 + OpenCV 自定义环境变量 pkg-config / PKG_CONFIG_PATH

    0. 前言 今天在执行一段脚本的时候,爆出错误: Package opencv was not found in the pkg-config search path. Perhaps you sho ...

  3. Appium移动自动化测试-----(三)Intellij IDEA + Android SDK + Genymotion Emulator

    下载安装Intellij IDEA 略 下载Android SDK http://tools.android-studio.org/index.php/sdk    下载后解压 http://www. ...

  4. 【模板】gcd和exgcd

    1. gcd: int gcd(int a,int b) { return !b?a:gcd(b,a%b); } exgcd: int exgcd(int a,int b,int& x,int ...

  5. python threading ThreadPoolExecutor

    线程池,为什么要使用线程池:1. 线程中可以获取某一个线程的状态或者某一个任务的状态,以及返回值2. 当一个线程完成的时候我们主线程能立即知道3. futures可以让多线程和多进程编码接口一致 获取 ...

  6. 三维网格补洞算法(Poisson Method)(转载)

    转载:https://www.cnblogs.com/shushen/p/5864042.html 下面介绍一种基于Poisson方程的三角网格补洞方法.该算法首先需要根据孔洞边界生成一个初始化补洞网 ...

  7. 排序算法Java代码实现(二)—— 冒泡排序

    本篇内容: 冒泡排序 冒泡排序 算法思想: 冒泡排序的原理是:从左到右,相邻元素进行比较. 每次比较一轮,就会找到序列中最大的一个或最小的一个.这个数就会从序列的最右边冒出来. 代码实现: /** * ...

  8. 安装使用 superset

    安装 superset 创建虚拟环境: python -m venv msuperset 激活虚拟环境: cd msuperset source bin/activate 安装 superset pi ...

  9. 最全面的PS快捷键使用指南(图文演示)

    每次做图的时候都会记错快捷键,很苦恼有木有!!!只能各处搜寻PS快捷键汇总起来,老板再也不会说我作图慢了....... 1.Ctrl+T:自由变形 该快捷键,主要对图层进行旋转.缩放等变形调整,同时可 ...

  10. jenkins19年最新最管用的汉化

    今天准备学学jenkins ,官方下载了一个最新版本,发现是英文版,网上找个许多汉化方式,几乎都是一种,下载插件 :Locale plugin ....很尴尬,下载完了还是没有汉化 ,是不是jenki ...