2021-2022ICPC澳门VP

在这里插入图片描述
赛时出了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]i1则一定能够形成循环。但是考虑到可能会有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';
}

澳门的题有点难啊…赛场强手如云,愿昆明好运。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值