过了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
", 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
", 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("
");
}
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;
}