Preface
AGC好难啊,从C题开始就一点不会了,感觉以前OI时候的AGC没那么变态的啊,也许是我变菜好多了吧
A - i i's
考虑先放一个这样的序列:
这样就把\(n,n-1\)都用完了,同时还用了个\(n-2\),然后考虑从大到小地把剩下的数插入空隙中
每次暴枚找到所有合法的插入位置即可,复杂度\(O(n^3)\)但远远跑不满,可以通过
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=1005;
int n; vector <int> ans;
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j; for (scanf("%d",&n),i=1;i<=2*n-1;++i)
if (i&1) ans.push_back(n); else ans.push_back(n-1);
for (ans.push_back(n-2),i=n-2;i>=1;--i)
{
vector <int> tmp; int cnt=i-(i==n-2);
for (tmp.push_back(ans[0]),j=1;j<ans.size();++j)
{
if (cnt&&abs(ans[j-1]-i)<=2&&abs(ans[j]-i)<=2) --cnt,tmp.push_back(i);
tmp.push_back(ans[j]);
}
ans=tmp;
}
//for (i=1;i<ans.size();++i) assert(abs(ans[i]-ans[i-1])>=1&&abs(ans[i]-ans[i-1])<=2);
for (int x:ans) printf("%d ",x);
return 0;
}
B - Red and Blue Spanning Tree
XJB乱搞就过了
首先不难发现可以把边分成三类,根据这条边的颜色和它的端点同色的数量为\(2/1/0\)划分,不难发现第三类边对于颜色的限制是没用的,只能起到把图连通的作用
考虑如果没有第一类边的话我们需要至少\(n\)条第二类边才能满足每个点的颜色要求,而这是显然不合法的,因此第一类边必须要存在
同时稍微手玩一下会发现选第一类边一定比选第二类边来的优,因为在不成环的情况下第一类边的贡献至少也是\(1\),不会变劣
因此我们的策略就是先贪心地把第一类边全连了,并得到当前的已经满足颜色要求的点的集合,然后考虑从这个集合利用第二类边能扩展就尽量扩展即可
最后注意有时候找到的解其实是一个森林,需要遍历一遍剩下的所有边把它们连成一棵树
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=200005;
int n,m,x[N],y[N],c[N],col[N],fa[N],vis[N]; char s[N],ch[10];
vector <pi> v[N]; vector <int> ans;
inline int getfa(CI x)
{
return x!=fa[x]?fa[x]=getfa(fa[x]):x;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (scanf("%d%d",&n,&m),i=1;i<=m;++i)
scanf("%d%d%s",&x[i],&y[i],ch),c[i]=ch[0]=='R',v[x[i]].push_back(pi(y[i],i)),v[y[i]].push_back(pi(x[i],i));
for (scanf("%s",s+1),i=1;i<=n;++i) fa[i]=i,col[i]=s[i]=='R';
for (i=1;i<=m;++i) if (c[i]==col[x[i]]&&c[i]==col[y[i]]&&getfa(x[i])!=getfa(y[i]))
ans.push_back(i),fa[getfa(x[i])]=getfa(y[i]),vis[x[i]]=vis[y[i]]=1;
queue <int> q; for (i=1;i<=n;++i) if (vis[i]) q.push(i);
while (!q.empty())
{
int now=q.front(); q.pop();
for (auto [to,id]:v[now])
if (!vis[to]&&c[id]==col[to]&&getfa(now)!=getfa(to))
ans.push_back(id),vis[to]=1,q.push(to),fa[getfa(now)]=getfa(to);
}
for (i=1;i<=n;++i) if (!vis[i]) return puts("No"),0;
for (i=1;i<=m;++i) if (getfa(x[i])!=getfa(y[i]))
fa[getfa(x[i])]=getfa(y[i]),ans.push_back(i);
puts("Yes"); for (auto x:ans) printf("%d ",x);
return 0;
}
C - Erase and Divide Game
好有意思的题
首先考虑如果给出的数不是区间而是单独的值该怎么做,考虑这两种操作的本质
我们不妨把所有数扔进从低位到高位的0/1Trie中,不难发现每次操作就是往当前这个点的子树里走一步,先不能走的人就输了
而这道题的问题就在于给出的数是一段区间,而一段区间内的数在0/1Trie上的分布是很散的,我们很难显式地把0/1Trie建出来
考虑一种更本质的做法,我们求出每个点的SG函数,用\(0\)代表空点,\(1\)代表必胜态,\(2\)代表必败态,合并两个点时要注意空点的情况需要特别考虑
初始时我们所有给出的区间的SG函数都是\(1\),剩下的其它区间都是\(0\),我们考虑一步步地模拟合并的过程
对于从0/1Trie的第\(60\)层合并到第\(59\)层时,我们对于一个数\(x\),只要看\(x+2^{59}\)的SG函数即可,然后合并后我们得到的仍然是\(O(n)\)级别的区间,再处理向上的合并即可
这部分的实现可以用类似于归并排序的写法,具体实现看代码,复杂度\(O(n\log V)\)
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_map>
#define int long long
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int trs[3][3]={{0,1,1},{1,2,1},{1,1,1}};
struct ifo
{
int l,r,tp;
inline ifo(CI L=0,CI R=0,CI TP=0)
{
l=L; r=R; tp=TP;
}
}; vector <ifo> o; int t,n,l,r,lst;
signed main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
for (scanf("%lld",&t);t;--t)
{
RI i,j; for (scanf("%lld",&n),lst=-1,i=1;i<=n;++i)
{
scanf("%lld%lld",&l,&r);
if (lst+1<=l-1) o.push_back(ifo(lst+1,l-1,0));
o.push_back(ifo(l,r,1)); lst=r;
}
o.push_back(ifo(lst+1,(1LL<<60)-1,0));
for (j=60;j>=1;--j)
{
vector <ifo> L,R; int mid=1LL<<j-1;
for (auto [l,r,tp]:o)
{
if (r<mid) L.push_back(ifo(l,r,tp));
else if (l>=mid) R.push_back(ifo(l-mid,r-mid,tp));
else L.push_back(ifo(l,mid-1,tp)),R.push_back(ifo(0,r-mid,tp));
}
int p=0,q=0; o.clear(); while (p<L.size()&&q<R.size())
{
if (L[p].r<=R[q].r) o.push_back(L[p]); else o.push_back(R[q]);
o.back().tp=trs[L[p].tp][R[q].tp];
if (L[p].r==o.back().r) ++p; else L[p].l=o.back().r+1;
if (R[q].r==o.back().r) ++q; else R[q].l=o.back().r+1;
}
}
puts(o.back().tp==1?"Takahashi":"Aoki"); o.clear();
}
return 0;
}
D - Red and Blue Chips
妙妙计数题
考虑判定一个RB序列是否可以被得到,不妨从后往前考虑
如果当前位置是R
,则序列顶端一定得是一个R
,并且我们需要拿掉这个R
否则如果当前位置是B
,它顶端可以放的是任意东西,但下方一定是B
,此时我们可以把序列从某个B
的位置分裂
如果某次在遇到R
时拿不出R
了则该状态不合法,否则总存在一种方案可以构造出该序列
而每次遇到B
的分裂策略显然满足贪心性,我们把每个B
分裂的方案能带来的R
的数量从大到小排序,这样一定是最优的
不过要注意初始时上面的R
是不参与排序的,因为它们一开始就可以被取出来
因此不妨先统计出每个B
前面的R
的个数\(c_1,c_2,\cdots,c_k\),而初始序列相当于给定了序列\(d_1,d_2,\cdots,d_k\),我们需要计数满足以下限制的序列\(c\)的个数:
- \(k\)和原序列中
B
的数目相同 - \(\sum_{i=1}^k c_i\)和原序列中
R
的数目相同 - 讲\(c_2,c_3,\cdots,c_k\)排序后,\(\forall s\in[1,k],\sum_{i=1}^s c_i\ge \sum_{i=1}^s d_i\)
这个计数问题就很trivial了,我们从大到小把数加入,设\(f_{i,j,k}\)表示当前加入的最大数为\(i\),一共选了\(j\)个数,这些数的和为\(k\)的方案数
转移系数其实就是多重全排列的式子,总复杂度\(O(n^3\log n)\)
#include<cstdio>
#include<iostream>
#include<utility>
#include<vector>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<set>
#include<array>
#include<random>
#include<bitset>
#include<ctime>
#include<limits.h>
#include<assert.h>
#include<unordered_map>
#define RI register int
#define CI const int&
#define mp make_pair
#define fi first
#define se second
#define Tp template <typename T>
using namespace std;
typedef long long LL;
typedef long double LDB;
typedef unsigned long long u64;
typedef __int128 i128;
typedef pair <int,int> pi;
typedef vector <int> VI;
typedef array <int,3> tri;
const int N=305,mod=998244353;
int n,d[N],R,B,f[N][N][N],fact[N],ifac[N]; char s[N];
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) 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)
{
RI i; for (fact[0]=i=1;i<=n;++i) fact[i]=1LL*fact[i-1]*i%mod;
for (ifac[n]=quick_pow(fact[n]),i=n-1;i>=0;--i) ifac[i]=1LL*ifac[i+1]*(i+1)%mod;
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i,j,k,l; for (scanf("%d%s",&n,s+1),i=n;i>=1;--i)
if (s[i]=='B') ++B; else ++d[B],++R;
for (i=1;i<=B;++i) d[i]+=d[i-1];
for (init(n),i=d[1];i<=R;++i) f[R+1][1][i]=fact[B-1];
for (i=R;i>=0;--i) for (j=1;j<=B;++j) for (k=d[j];k<=R;++k)
for (l=0;j+l<=B&&k+i*l<=R;++l)
{
if (k+i*l<d[j+l]) break;
inc(f[i][j+l][k+i*l],1LL*f[i+1][j][k]*ifac[l]%mod);
}
return printf("%d",f[0][B][R]),0;
}
Postscript
啥都不会被虐哭了
- AtCoder Contest Grand 064atcoder contest grand 064 authentic atcoder contest grand negative atcoder contest grand histogram atcoder contest grand atcoder contest radius grand atcoder contest grand 022 atcoder contest grand 001 atcoder contest descent grand atcoder contest grand 019 atcoder contest grand 017