2020JSCPC补题

过了4题,铜牌qwq

补题花了挺长时间

A Array

比赛的时候写了个加标记,乘标记,次方标记,麻烦死,最后写到比赛结束也没写完QAQ

太难顶了

比赛结束后才把这看成是一个映射

这题也反应出了我对懒标记的不熟悉

补这题前复习了线段树,并大改了自己的线段树模板。

补完这题后,又计时默写了几次线段树模板,现在能15min内默写出线段树加标记+乘标记的模板了

思路:关键的一点就是注意到模数非常小,我们记录每个节点上有多少个数取膜后为某一值,用一个数组来存这个个数

比如用zz[x]表示这个区间上取膜后为x的数有zz[x]个

区间加、乘、次方实际上都是对这个数组进行了映射操作

所以我们就用一个结构体为映射的懒标记

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
const int MAXN = 1e5 + 7;
int a[MAXN];
int n, p, q;
int k;
int qpow(int a, int b) {
    int res = 1;
    for (; b; a = a * a % p) {
        if (b & 1) res = res * a % p;
        b >>= 1;
    }
    return res;
}
struct F {
    int pt[30];
};
struct NODE {
    int l, r, mid, len;
    bool ff;//懒标记
    int zz[30];
    F f;//懒标记
    NODE* lson;
    NODE* rson;
};
NODE* head = new(NODE);
void upd(NODE* pp) {
    for (int i = 0; i < p; i++) {
        pp->zz[i] = pp->lson->zz[i] + pp->rson->zz[i];
    }
}
void build(NODE* pp, int l, int r) {
    pp->l = l; pp->r = r; pp->mid = l + r >> 1; pp->len = r - l + 1; pp->lson = new(NODE); pp->rson = new(NODE);
    for (int i = 0; i < p; i++) pp->f.pt[i] = i;
    pp->ff = false;
    if (l == r) {
        for (int i = 0; i < p; i++) pp->zz[i] = 0;
        int x;
        scanf("%d", &x);
        x %= p;
        pp->zz[x]++;
        return;
    }
    int mid = pp->mid;
    build(pp->lson, l, mid);
    build(pp->rson, mid + 1, r);
    upd(pp);
}
void pd(NODE* pp) {
    if (pp->l == pp->r) return;
    pp->lson->ff = true; pp->rson->ff = true;
    int tmp[30] = { 0 };
    for (int i = 0; i < p; i++) {
        pp->lson->f.pt[i] = pp->f.pt[pp->lson->f.pt[i]];
        pp->rson->f.pt[i] = pp->f.pt[pp->rson->f.pt[i]];
    }
    for (int i = 0; i < p; i++) {
        tmp[pp->f.pt[i]] += pp->lson->zz[i];
    }
    for (int i = 0; i < p; i++) {
        pp->lson->zz[i] = tmp[i];
        tmp[i] = 0;
    }
    for (int i = 0; i < p; i++) {
        tmp[pp->f.pt[i]] += pp->rson->zz[i];
    }
    for (int i = 0; i < p; i++) {
        pp->rson->zz[i] = tmp[i];
    }
    pp->ff = false;
    for (int i = 0; i < p; i++) pp->f.pt[i] = i;
}
void CHANGE(NODE* pp, int l, int r, F f) {
    if (pp->l == l && pp->r == r) {
        pp->ff = true;
        int tmp[30] = { 0 };
        for (int i = 0; i < p; i++) {
            pp->f.pt[i] = f.pt[pp->f.pt[i]];
            tmp[f.pt[i]] += pp->zz[i];
        }
        for (int i = 0; i < p; i++) pp->zz[i] = tmp[i];
        return;
    }
    if(pp->ff) pd(pp);
    int mid = pp->mid;
    if (r <= mid) CHANGE(pp->lson, l, r, f);
    else if (l > mid) CHANGE(pp->rson, l, r, f);
    else {
        CHANGE(pp->lson, l, mid, f);
        CHANGE(pp->rson, mid + 1, r, f);
    }
    upd(pp);
}
int sc[30];
void Q(NODE* pp, int l, int r) {
    if (pp->l == l && pp->r == r) {
        for (int i = 0; i < p; i++) {
            sc[i] += pp->zz[i];
        }
        return;
    }
    if (pp->ff) pd(pp);
    int mid = pp->mid;
    if (r <= mid)Q(pp->lson, l, r);
    else if (l > mid) Q(pp->rson, l, r);
    else {
        Q(pp->lson, l, mid);
        Q(pp->rson, mid + 1, r);
    }
}
int main()
{
    cin >> n >> p;
    build(head, 1, n);
    cin >> q;
    int op, l, r;
    for (int e = 1; e <= q; e++) {
        scanf("%d", &op);
        if (op == 1) {
            scanf("%d%d%d", &l, &r, &k);
            F f;
            for (int i = 0; i < p; i++) {
                f.pt[i] = (i + k) % p;
            }
            CHANGE(head, l, r, f);
        }
        else if (op == 2) {
            scanf("%d%d%d", &l, &r, &k);
            F f;
            k %= p;
            for (int i = 0; i < p; i++) {
                f.pt[i] = (i * k) % p;
            }
            CHANGE(head, l, r, f);
        }
        else if (op == 3) {
            scanf("%d%d%d", &l, &r, &k);
            F f;
            for (int i = 0; i < p; i++) {
                f.pt[i] = qpow(i, k);
            }
            CHANGE(head, l, r, f);
        }
        else if (op == 4) {
            scanf("%d%d%d", &l, &r, &k);
            for (int i = 0; i < p; i++) sc[i] = 0;
            Q(head, l, r);
            int ans = 0, res = 0;
            for (int i = 0; i < p; i++) {
                res = qpow(i, k);
                ans += res * sc[i];
                ans %= p;
            }
            printf("%d\n", ans);
        }
        else if (op == 5) {
            scanf("%d%d%d", &l, &r, &k);
            for (int i = 0; i < p; i++) sc[i] = 0;
            Q(head, l, r);
            int ans = 1, res = 0;
            for (int i = 0; i < p; i++) {
                res = qpow(i, sc[i]);
                ans = ans * res % p;
            }
            printf("%d\n", ans);
        }
    }
    return 0;
}

  

I Intersections

这题比A题简单,比赛时应该开这题的qwq

就是一个最短路   (趴)

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
const int MAXN = 3e5;
const long long INF = (long long)1<<60;
int n,m,xs,ys,xt,yt;
long long a[MAXN],b[MAXN],c[MAXN],w[MAXN],dis[MAXN];
void dijkstra(int st){
	priority_queue<pair<long long,int>,vector<pair<long long ,int> >,greater<pair<long long,int> > > que;
	dis[st] = 0;
	que.push(make_pair(dis[st],st));
	while(!que.empty()){
		int node = que.top().second;
		long long dt = que.top().first;
		if(dis[node]<dt){
			que.pop();
			continue;
		}
		que.pop();
		long long k = dis[node] / (a[node] + b[node]);
		long long re = dis[node] - k * (a[node] + b[node]);
		if(re<a[node]){
			long long ad = a[node] - re;
			int po = node - m;
			if(po > 0 && dis[po] > dis[node] + w[po]){
				dis[po] = dis[node] + w[po];
				que.push(make_pair(dis[po],po));
			}
			po = node + m;
			if(po <= n*m && dis[po] > dis[node] + w[node]){
				dis[po] = dis[node] + w[node];
				que.push(make_pair(dis[po],po));
			}
			if(node % m){
				po = node + 1;
				if(dis[po] > dis[node] + ad + c[node]){
					dis[po] = dis[node] + ad + c[node];
					que.push(make_pair(dis[po],po));
				}
			}
			if(node % m != 1){
				po = node - 1;
				if(dis[po] > dis[node] + ad + c[po]){
					dis[po] = dis[node] + ad + c[po];
					que.push(make_pair(dis[po],po));
				}
			}
		}
		else{
			int po = node - m;
			if(po > 0 && dis[po] > (k+1) * (a[node] + b[node]) + w[po] ){
				dis[po] = (k+1) * (a[node] + b[node]) + w[po];
				que.push(make_pair(dis[po],po)); 
			}
			po = node + m;
			if(po <= n*m && dis[po] > (k+1) * (a[node] + b[node]) + w[node]){
				dis[po] = (k+1) * (a[node] + b[node]) + w[node];
				que.push(make_pair(dis[po],po));
			}
			if(node % m){
				po = node + 1;
				if(dis[po] > dis[node] + c[node]){
					dis[po] = dis[node] + c[node];
					que.push(make_pair(dis[po],po));
				}
			}
			if(node % m != 1){
				po = node - 1;
				if(dis[po] > dis[node] + c[po]){
					dis[po] = dis[node] + c[po];
					que.push(make_pair(dis[po],po));
				}
			}
		}
	}
}
int main()
{
	cin>>n>>m>>xs>>ys>>xt>>yt;
	int id = 0;
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= m;j++){
			id++;
			scanf("%lld",&a[id]);
			dis[id] = INF;
		}
	}
	id = 0;
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= m;j++){
			id++;
			scanf("%lld",&b[id]);
		}
	}
	id = 0;
	for(int i = 1;i <= n;i++){
		for(int j = 1;j <= m - 1;j++){
			id++;
			scanf("%lld",&c[id]);
		}
		c[++id] = 0; 
	}
	id = 0;
	for(int i = 1;i <= n - 1;i++){
		for(int j = 1;j <= m;j++){
			id++;
			scanf("%lld",&w[id]);
		}
	}
	for(int j = 1;j <= m;j++) w[++id] = 0;
	dijkstra((xs - 1) * m + ys);
	cout<<dis[(xt - 1) * m + yt]<<endl;
	return 0;
}

  

E Eliminate the Virus

很有意思的一题

思路:n <= 16,要想到用状态来表示,算出每个状态(st1)模拟一遍感染后得到的新状态 (st2),f [st1] = st2,

注:f 是一个单值映射,但 f 的反函数不一定是单值函数,所以不要利用 f 的反函数

题目虽然没说要最小步数,但步数也是有限制的,所以最好求最小步数

这里状态不能从小到大遍历,因为有些状态可能是由比它大的状态转移过来

所以我们就用bfs,从最大状态bfs,求出到各个状态的距离,如果dis[0]!=INF,说明存在答案

接下来的这步就是求答案了

求答案不能慌,再写一段来求答案,不能急于求成

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int INF = 1e6;
const int MAXZT = 1 << 16;
int f[MAXZT + 10];
int cnt[MAXZT + 10];
int graph[18][18];
int ans[MAXZT + 10];
int dis[MAXZT + 10];
int pre[MAXZT + 10];
bool vis[MAXZT + 10] = {false};
int n, m, k;
void init() {
	bool b[18] = { false };
	for (int st = 0; st < 1 << n; st++) {
		dis[st] = INF;
		pre[st] = -1;
		vis[st] = false;
		for (int i = 0; i < n; i++) b[i] = false;
		for (int i = 0; i < n; i++) {
			if (st & (1 << i)) {
				for (int j = 1; j <= n; j++) {
					if (graph[i + 1][j]) b[j - 1] = true;
				}
			}
		}
		int res = 0;
		for (int i = 0; i < n; i++) {
			if (b[i]) res += 1 << i;
		}
		f[st] = res;
		int st2 = st;
		while (st2) {
			if (st2 & 1) cnt[st] ++;
			st2 >>= 1;
		}
	}
}
void bfs(int st) {
	queue<int>que;
	que.push(st);
	vis[st] = true;
	dis[st] = 0;
	while (!que.empty()) {
		st = que.front();
		que.pop();
		for (int st2 = st; ; st2--, st2 &= st) {//遍历子集
			if (cnt[st ^ st2] <= k) {
				if (!vis[f[st2]]) {
					dis[f[st2]] = dis[st] + 1;
					pre[f[st2]] = st;
					vis[f[st2]] = true;
					que.push(f[st2]);
				}
			}
			if (st2 == 0) break;
		}
	}
}
int main()
{
	cin >> n >> m >> k;
	int u, v;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			graph[i][j] = 0;
	for (int i = 1; i <= m; i++) {
		cin >> u >> v;
		graph[u][v] = graph[v][u] = 1;
	}
	init();
	int max_st = (1 << n) - 1;
	bfs(max_st);
	if (dis[0] == INF) {
		cout << -1 << endl;
		return 0;
	}
	cout << dis[0] << endl;
	for (int tt = 0; tt != max_st;) {//找答案
		int pp = pre[tt];
		for (int st2 = pp; ; st2--, st2 &= pp) {//遍历子集
			if (cnt[pp ^ st2] <= k) {
				if (f[st2] == tt) {
					ans[pp] = pp ^ st2;
					break;
				}
			}
			if (st2 == 0) break;
		}
		tt = pp;
	}
	int pos = 0;
	for (int e = 1; e <= dis[0];e++) {
		pos = pre[pos];
		for (int i = 0; i < n; i++) {
			if (ans[pos] & (1 << i)) {
				printf("%c", 'a' + i);
			}
		}
		printf("\n");
	}
	return 0;
} 

  

G Grid Coloring

一个星期才A这题,难顶

这题原来是结论题,难的一匹,看了题解才会这题,原来是有规律的

刚学矩阵加速,就来做这题,然后一直wa,我写了个暴力来对拍,然后发现前面几项对拍都一样,从某项开始就不一样了,

于是我一直在debug我的矩阵,还是不能一样

后来我又写了一个暴力+剪枝的对拍,发现原来是我之前的暴力答案错了。。我一直以为我的矩阵错了,调死调不出来

既然矩阵没错,哪里wa了呢,我又继续对拍,然后发现,原来是n >= 5写成了n == 5了草。。。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<vector> 
using namespace std;
const long long MOD = 1e9+7;
const int MAXN = 103;
int ST[100];
struct Martix {
    int n;
    long long mat[MAXN][MAXN];

    void init() {
        memset(mat, 0, sizeof(mat));
    }

    void E_init() {
        memset(mat, 0, sizeof(mat));
        for (int i = 1; i <= n; i++) mat[i][i] = (long long)1;
    }

    void scan() {
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                scanf("%lld", &mat[i][j]);
    }

    void print() {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) cout << mat[i][j] << " ";
            cout << endl;
        }
    }

    Martix operator *(Martix b) {
        Martix tmp;
        tmp.init();
        tmp.n = n;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                for (int k = 1; k <= n; k++) {
                    tmp.mat[i][j] += (mat[i][k] * b.mat[k][j]);
                    tmp.mat[i][j] = (MOD + tmp.mat[i][j] % MOD) % MOD;
                }
        return tmp;
    }

};
Martix Mat_qpow(Martix A, long long k) {
    Martix ret;
    ret.n = A.n;
    ret.E_init();
    for (; k; k >>= 1, A = A * A){
        if (k & 1) ret = ret * A;
    }
    return ret;
}
int n,m;
bool check(int st,int n){
	int a[4],b[4];
	for(int i = 1;i <= n ;i++){
		if(st&1) b[n-i+1] = 1;
		else b[n-i+1] = 0;
		st>>=1;
	} 
	if(n == 3 && b[1] == b[2] && b[2] == b[3]) return false;
	for(int i = 1;i <= n;i++){
		if(st&1) a[n-i+1] = 1;
		else a[n-i+1] = 0;
		st>>=1;
	}
	if(n == 3 && a[1] == a[2] && a[2] == a[3]) return false;
	return true;
}
bool ok(int st1,int st2,int n){
	int a[4],b[4],c[4],d[4];
	for(int i = 1;i <= n ;i++){
		if(st1&1) b[n-i+1] = 1;
		else b[n-i+1] = 0;
		st1>>=1;
	} 
	if(n == 3 && b[1] == b[2] && b[2] == b[3]) return false;
	for(int i = 1;i <= n;i++){
		if(st1&1) a[n-i+1] = 1;
		else a[n-i+1] = 0;
		st1>>=1;
	}
	if(n == 3 && a[1] == a[2] && a[2] == a[3]) return false;
	for(int i = 1;i <= n;i++){
		if(st2&1) d[n-i+1] = 1;
		else d[n-i+1] = 0;
		st2>>=1;
	} 
	if(n == 3 && d[1] == d[2] && d[2] == d[3]) return false;
	for(int i = 1;i <= n;i++){
		if(st2&1) c[n-i+1] = 1;
		else c[n-i+1] = 0;
		st2>>=1;
	}
	if(n == 3 && c[1] == c[2] && c[2] == c[3]) return false;
	for(int i = 1;i <= n;i++) 
		if(b[i]!=c[i]) return false;
	for(int i = 1;i <= n;i++){
		if(a[i] == b[i] && b[i] == d[i]) return false;
	}
	if(n == 3 && a[1] == b[2] && c[2] == d[3]) return false;
	if(n == 3 && a[3] == b[2] && c[2] == d[1]) return false;
	return true;
}
int main()
{
	int T;
	cin>>T;
	while(T--){
		cin>>n>>m;
		if(n>m) swap(n,m);
		if(n==1&&m==1){
			cout<<2<<endl;
			continue;
		}
		if(n==4&&m==4){
			cout<<18<<endl;
			continue;
		}
		if(n==4) {
			cout<<14<<endl;
			continue;
		}
		if(n>=5) {//写成n==5了啊啊啊啊啊啊啊啊 
			cout<<8<<endl;
			continue;
		}
		if(n<=3){
			Martix A,B;
			int tot = 0;
			for(int st = 0;st < 1<<n*2;st++){
					if(check(st,n)) ST[++tot] = st;
			}
			A.n = B.n = tot + 1;
			A.init();B.init();
			for(int i = 1;i <= tot;i++){
				for(int j = 1;j <= tot;j++){
					if(ok(ST[j],ST[i],n)){
						A.mat[i][j] = (long long)1;
					}
				}
			}
			B = Mat_qpow(A,(long long)m-(long long)2);
			Martix C;
			C.n = tot + 1;
			C.init();
			for(int i = 1;i<=tot;i++) C.mat[i][1] = (long long)1;
			B = B * C;
			long long ans = 0;
			for(int i = 1;i<=tot;i++) {
				ans += B.mat[i][1];
				ans %= MOD;
			}
			cout << ans << endl;
		}		
	}
	return 0;
}

  

 

posted @ 2020-12-11 15:13  beta_dust  阅读(378)  评论(0编辑  收藏  举报