赛时出了A,C,F,K
赛后队友补了E,我补了G
EASY A,F,K
A.So I’ll Max Out My Constructive Algor…
队友出的,思路就是随便找一条路径,如果不满足就反着输出
#include <bits/stdc++.h>
using namespace std;
int T, n, a[105][105];
vector<int> v;
void solve()
{
v.clear();
cin >> n;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
{
cin >> a[i][j];
}
}
for (int i = 1; i <= n; ++i)
{
if (i & 1)
{
for (int j = 1; j <= n; ++j)
{
v.push_back(a[i][j]);
}
}
else
{
for (int j = n; j >= 1; --j)
{
v.push_back(a[i][j]);
}
}
}
int tot = 0;
for (int i = 0; i < v.size() - 1; ++i)
{
if (v[i] < v[i + 1])
tot--;
else
tot++;
}
if (tot < 0)
{
reverse(v.begin(), v.end());
}
for (int i = 0; i < v.size() - 1; ++i)
{
cout << v[i] << " ";
}
cout << v[v.size() - 1]<<"\n";
}
F. Sandpile on Clique
这道题首先不难发现一个结论,如果对于序列a[n]进行从小到大的排序后,对于 i ∈ { 1 , . . . n } i \in \{1,...n\} i∈{1,...n}有 a [ i ] ≥ i − 1 a[i]\ge i-1 a[i]≥i−1则一定能够形成循环。但是考虑到可能会有0 0 0 0 0 kn kn kn…这样的极端数据出现,因此处理的第一步就是把所有数超过n-1的部分全部砍掉,再一个一个加上去之后排序,如果满足上述公式则会出现循环。否则我们用优先队列维护每次操作即可。
const int N = 500010;
int n,a[N],b[N],delta,ans[N];
priority_queue<pii> heap;
void bfs(){
for(int i=1;i<=n;++i) heap.push({a[i], i});
while(heap.top().x+delta>=n-1){
auto u=heap.top(); heap.pop();
int x=u.x, y=u.y;
x+=delta;
x-=n-1,delta++;
heap.push({x-delta, y});
}
while(heap.size()){
auto u=heap.top();heap.pop();
ans[u.y]=u.x+delta;
}
}
void solve(){
scanf("%lld",&n);
rep(i,1,n) scanf("%lld",&a[i]),b[i]=a[i];
int v=0;
for(int i=1;i<=n;++i){
int tmp=b[i]/(n-1);
v+=tmp,b[i]%=(n-1);
b[i]-=tmp;
}
for(int i=1;i<=n;++i) b[i]+=v;
sort(b+1, b+1+n);
bool ok=true;
for(int i=1;i<=n;++i){
if(b[i]<i-1){ok=false; break;}
}
for(int i=1;i<=n;++)
if(ok) {puts("Recurrent");return ;}
bfs();
for(int i=1;i<n;++i) printf("%lld ",ans[i]);
printf("%lld\n",ans[n]);
}
K. Link-Cut Tree
很显然的贪心,按照顺序依次加边直到出现环,然后把环找出来即可。
const int N = 100010,M=N*2;
struct Edge{
int a,b,w;
}edges[N];
vector<pii> edge[M];
vector<int> ans;
int d[N],n,m,k,fa[N],st[N];
void init(){
for(int i=1;i<=n;++i) edge[i].clear(),d[i]=0,st[i]=0;
rep(i,1,n) fa[i]=i;
ans.clear();
}
int find(int x){return x==fa[x]?fa[x]:fa[x]=find(fa[x]);}
bool join(int a,int b,int id){
int pa=find(a), pb=find(b);
if(pa!=pb){
fa[pa]=pb;
edge[a].push_back({b,id});
edge[b].push_back({a,id});
d[a]++,d[b]++;
return true;
}
d[a]++,d[b]++;
edge[a].push_back({b,id});
edge[b].push_back({a,id});
return false;
}
void dfs(int u,int pre){
for(auto v:edge[u]){
int ver=v.x, w=v.y;
if(ver==pre) continue;
if(d[ver]==2&&!st[ver]){
st[ver]=true;
ans.push_back(w);
dfs(ver, u);
}
}
}
int hh,tt,q[N];
void bfs(){
hh=0,tt=-1;
for(int i=1;i<=n;++i){
if(d[i]==1) q[++tt]=i;
}
while(hh<=tt){
int u=q[hh++];
for(auto j:edge[u]){
// int j=e[i];
d[j.x]--;
if(d[j.x]==1){
q[++tt]=j.x;
}
}
}
for(int i=1;i<=n;++i) if(d[i]==2) {
dfs(i,-1);
break;
}
sort(ans.begin(),ans.end());
}
void solve(){
n=read(),m=read();
init();
for(int i=1;i<=m;++i){
int a=read(),b=read();
edges[i]={a,b,i};
}
bool flag=false;
for(int i=1;i<=m;++i){
int a=edges[i].a, b=edges[i].b;
bool ok=join(a,b,i);
if(!ok){
bfs();
for(int i=0;i<ans.size()-1;++i) printf("%d ",ans[i]); printf("%d\n",ans.back());
flag=true;
break;
}
}
if(!flag) print(-1);
}
Mid C,E,G
E Pass the Ball!
队友赛后补的,我现在还不怎么会多项式,以后()补…
至于是怎么往多项式上面凑得,我可能听懂了也可能没有听懂.
namespace NTT {
const int max_len = (1 << 18) + 10;
const Lint mod = 1945555039024054273;
const Lint g = 5;
int r[max_len];
Lint fpow(Lint a, Lint b, Lint mod) {
Lint res = 1;
for (; b; b >>= 1) {
if (b & 1)
res = (LLint)res * a % mod;
a = (LLint)a * a % mod;
}
return res;
}
inline Lint add(Lint a, Lint b) {
a += b;
return a >= mod ? a - mod : a;
}
inline Lint mul(Lint a, Lint b) {
return (LLint)a * b % mod;
}
void cal_r(int n) {
for (int i = 0; i < n; i++)
r[i] = 0;
for (int i = 0; i < n; i++)
r[i] = (i & 1) * (n >> 1) + (r[i >> 1] >> 1);
}
void dft(Lint* a, int n, int type) {
for (int i = 0; i < n; i++)
if (i < r[i]) {
swap(a[i], a[r[i]]);
}
for (int i = 1; i < n; i <<= 1) {
Lint w = fpow(g, (mod - 1) / (i << 1), mod);
if (type == -1)
w = fpow(w, mod - 2, mod);
for (int j = 0, p = i << 1; j < n; j += p) {
Lint t = 1;
for (int k = 0; k < i; k++, t = mul(t, w)) {
Lint tmp = mul(a[j + k + i], t);
a[j + k + i] = add(a[j + k], mod - tmp);
a[j + k] = add(a[j + k], tmp);
}
}
}
}
Lint p[max_len], q[max_len];
vector<Lint> poly_mul(const vector<Lint>& a, const vector<Lint>& b) {
vector<Lint> res;
int n = a.size(), m = b.size();
res.resize(n + m - 1);
int len = n + m - 1;
int lim = 1;
while (lim < len)
lim <<= 1;
for (int i = 0; i < n; i++)
p[i] = a[i];
for (int i = n; i < lim; i++)
p[i] = 0;
for (int i = 0; i < m; i++)
q[i] = b[i];
for (int i = m; i < lim; i++)
q[i] = 0;
cal_r(lim);
dft(p, lim, 1), dft(q, lim, 1);
for (int i = 0; i < lim; i++)
p[i] = mul(p[i], q[i]);
dft(p, lim, -1);
Lint inv = fpow(lim, mod - 2, mod);
for (int i = 0; i < lim; i++)
p[i] = mul(p[i], inv);
for (int i = 0; i < n + m - 1; i++)
res[i] = p[i];
return res;
}
}; // namespace NTT
int n, q;
int b[maxn];
bool vis[maxn];
int tot;
vector<Lint> ans[maxn];
vector<Lint> ring[maxn];
vector<int> large_ring;
Lint res[maxsqrtn][maxsqrtn];
void find_ring(int x, int id) {
for (int i = b[x]; i != x; i = b[i]) {
vis[i] = 1;
ring[id].push_back(i);
}
ring[id].push_back(x);
}
void cal(int id) {
vector<Lint> tmp;
int n = ring[id].size();
tmp.resize(n);
for (int i = 0; i < n - 1; i++) {
tmp[n - i - 2] = ring[id][i];
}
tmp[n - 1] = ring[id][n - 1];
vector<Lint> res = NTT::poly_mul(ring[id], tmp);
for (int i = 0; i < n - 1; i++) {
res[n + i] += res[i];
}
for (int i = n - 1; i < 2 * n - 1; i++) {
ans[id].push_back(res[i]);
}
}
int main() {
cin >> n >> q;
int sqrtn = sqrt(n);
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
for (int i = 1; i <= n; i++) {
if (vis[i])
continue;
find_ring(i, ++tot);
}
for (int i = 1; i <= tot; i++) {
cal(i);
int len = ring[i].size();
if (len > sqrtn) {
large_ring.push_back(i);
continue;
}
for (int j = 1; j <= len; j++) {
res[len][j] += ans[i][j - 1];
}
}
while (q--) {
int k;
cin >> k;
Lint sum = 0;
for (int i = 2; i <= sqrtn; i++)
sum += res[i][(k - 1) % i + 1];
for (int i : large_ring) {
int len = ring[i].size();
sum += ans[i][(k - 1) % len];
}
cout << sum << '\n';
}
return 0;
}
G Cyclic Buffer
其实这题真的不难,但是我们两个半小时开完四道题之后我想这道题,另外两个队友想E,都是有思路的,然后就是我写一会儿G,队友写一会儿E,不懂为什么我写代码的时候犯了n多小错误,线段树modify写错,query又写假了导致最后也没码出来。
但是这道题首先一点,应该想到每个数要么出现在1位置,要么出现在k位置,然后就顺序扫描用线段树维护出trans[i][0/1]
表示i位于1/k 位置时没能覆盖的大于等于i的最小值,且对于小于此值的所有值都已经被覆盖了。
有了这个之后,就是一个决策问题,线性DP转移即可。
int n,m,k,a[N],trans[N][2],pos[N],st[N];
LL f[N][2];
#define Mod(x,n) (((x)%(n)+(n))%(n))
#define dist(a,b) min(abs(a-b), n-abs(a-b))
struct Segment_Tree{
struct Node{
int l,r;
int sum;
}tr[N<<2];
#define push_up(u) tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum
void build(int u,int l,int r){
tr[u]={l,r,0};
if(l==r) return ;
int mid=l+r>>1;
build(u<<1,l,mid), build(u<<1|1,mid+1,r);
push_up(u);
}
void modify(int u,int pos,int d){
if(tr[u].l==tr[u].r){
tr[u].sum+=d;
return ;
}
int mid=tr[u].l+tr[u].r>>1;
if(pos<=mid) modify(u<<1,pos,d);
else modify(u<<1|1,pos,d);
push_up(u);
}
int query(int u,int pos){
if(!tr[u].sum&&pos<tr[u].l) return tr[u].l;
if(tr[u].l==tr[u].r) return -1;
int mid=tr[u].l+tr[u].r>>1;
if(mid<=pos) return query(u<<1|1,pos);
int res=query(u<<1,pos);
if(res==-1) return query(u<<1|1,pos);
return res;
}
}T; //权值线段树,query是在线段树类似于二分的操作,找到第一个>=pos且不连续的值
void solve(){
n=read(),k=read();
rep(i,1,n) a[i]=read(),pos[a[i]]=i,f[i][0]=f[i][1]=INF;
for(int i=1;i<=n;++i) a[i+n]=a[i];
T.build(1,1,n+1);
LL ans=INF;
int s;
for(int i=1;i<=n+k-1;++i){ //预处理出每个点转到开头和k的末尾,没能覆盖的大于当前值的第一个数
T.modify(1,a[i],1);
if(i>k) T.modify(1,a[i-k],-1);
if(i==k) s=T.query(1,0);
if(i>=k){
trans[a[i-k+1]][0]=T.query(1,a[i-k+1]);
trans[a[i]][1]=T.query(1,a[i]);
}
}
if(s==n+1){puts("0");return ;}
f[s][0]=dist(1,pos[s]);
f[s][1]=dist(k,pos[s]);
for(int i=1;i<=n;++i){ //DP
if(f[i][0]<INF){
int nxt=trans[i][0];
if(nxt==n+1) ans=min(ans, f[i][0]);
else{
int pos1=pos[i],pos2=pos[nxt];
f[nxt][0]=min(f[nxt][0], f[i][0]+dist(pos1,pos2));
f[nxt][1]=min(f[nxt][1], f[i][0]+dist(Mod(pos1+k-1,n),pos2));
}
}
if(f[i][1]<INF){
int nxt=trans[i][1];
if(nxt==n+1) ans=min(ans, f[i][1]);
else{
int pos1=pos[i],pos2=pos[nxt];
f[nxt][0]=min(f[nxt][0], f[i][1]+dist(Mod(pos1-k+1,n),pos2));
f[nxt][1]=min(f[nxt][1], f[i][1]+dist(pos1,pos2));
}
}
}
print(ans);
}
C.Laser Trap
队友出的,据说这道题卡精度,而且极角序需要去重,然后在环上双指针找到最小要删除的数量
struct Point {
Lint x, y;
int area() const {
if (y > 0 || y == 0 && x > 0)
return 0;
return 1;
}
Lint cross(const Point& rhs) const { return x * rhs.y - rhs.x * y; }
bool operator<(const Point& rhs) const { return area() < rhs.area() || area() == rhs.area() && cross(rhs) > 0; }
bool operator==(const Point& rhs) const { return area() == rhs.area() && cross(rhs) == 0; }
} a[maxn];
int nxt[maxn];
bool check(const Point& i, const Point& j) {
return i.cross(j) > 0;
}
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].x >> a[i].y;
}
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n; i++) {
a[i + n] = a[i];
}
for (int i = 1, j; i <= n;) {
for (j = i + 1; j <= n; j++) {
if (!(a[i] == a[j]))
break;
}
nxt[i] = j;
nxt[n + i] = n + j;
i = j;
}
int res = n;
for (int i = 1, j = 1; i <= n; i = nxt[i]) {
while (j < n + i && (i == j || check(a[i], a[j]))) {
j = nxt[j];
}
if (j == n + i)
res = 0;
else
res = min(res, j - nxt[i]);
}
cout << res << '\n';
}
澳门的题有点难啊…赛场强手如云,愿昆明好运。