2020.11.4 Touhou 歡樂賽



寫在前面

題目及數據下載:鏈接:https://pan.baidu.com/s/1ZXLx8HGKgIicCCv7MwFukg
提取碼:koko

除了 B 題目都是搬的:

A CF1428C ABBB
C CF1444A Division
D P3786 萃香抱西瓜

在這里感謝原出題人,感謝驗題人 Kersen,yu__xuan,eee_hoho。

比較親民的一場。
無腦暴力分並不是很多,涉及的算法都很簡單,主要考察閱讀理解能力。


A 大空魔術

「哈啊,宇宙還真是充滿魅力啊……」
「怎么了?發出那種嘆息。你的意思是地上已經毫無魅力了嗎?」
「16時31分。看到長庚星了啊。因為地上幾乎已經沒有不可思議的事情了呢」
「如果像蓮子這樣能夠看到這個世界的全部構造,那么心里的虛無主義就會開始露頭了啊」

《大空魔術 ~ Magical Astronomy.》

有一個只包含 'A' 與 'B' 的字符串,每次可以消掉一個 "AB" 或一個 "BB",並把剩下的拼在一起。
求字符串最短的長度。

注意到可以消去 "AB","BB" 即可以消去 "?B"。
最后字符串的形態一定是 "BAA...A" 或者 "AA...AA"。

想到括號序列,考慮使用一個棧模擬該過程。
如果當前加入的是 "B" 且棧非空,則彈出棧頂元素,否則加入該字符。
因為能刪去的只有 "?B",所以能刪就刪最優。

總復雜度 \(O(t|s|)\)

這種情況:"ABB",顯然應先刪去 "AB",可以發現上面的模擬過程也符合這樣的刪除策略。

//知識點:貪心,模擬 
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#include <stack>
#define LL long long
const int kMaxn = 2e6 + 10;
//=============================================================
int T, n, ans;
char s[kMaxn];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void Chkmax(int &fir_, int sec_) {
  if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(int &fir_, int sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
//=============================================================
int main() {
  T = read();
  while (T --) {
    std::stack <char> st;
    scanf("%s", s + 1);
    n = strlen(s + 1), ans = 0;
    for (int i = 1; i <= n; ++ i) {
      if (s[i] == 'A') {
        st.push('A'); 
      } else if (! st.empty()) {
        st.pop();
      } else {
        st.push('B');
      }
    }
    for (; ! st.empty(); st.pop()) {
      ans ++;
    }
    printf("%d\n", ans);
  }
  return 0;
}

B 夜桜街道

\[死してなお楚々と芽吹く \]

\[雖知終將迎來凋零\ 花苞仍在楚楚成長 \]

\[散ればこそ美しき春の來ない桜よ \]

\[散去之時最為凄美\ 未曾沐浴春風的夜櫻啊 \]

\[今、幽雅に咲かせ \]

\[現在、請幽雅地綻放吧 \]

給定一長度為 \(n\) 的數列 \(a\),定義 \(f(l,r)\),有:

\[f(l,r) = \sum_{i=l}^{r}\sum_{j=i+1}^{n}[a_i< a_j] \]

其中 \([\ ]\) 表示 艾佛森括號
求:

\[\sum_{i=1}^{n}\dfrac{f(1,i)}{i} \pmod {998244353} \]

發現給定的 \(f(l,r)\) 即為區間 \([l,r]\) 內的順序對個數。

考慮如何通過 \(\sum_{i=1}^{n-1} f(1,i)\) 得到 \(\sum_{i=1}^{n} f(1,i)\)
發現新增的貢獻即為較大的數為 \(a_n\) 的順序對。
離散化后順序枚舉數列,樹狀數組維護,統計答案時乘上一個逆元即可。
模數是質數,逆元怎么搞都行,總復雜度均為 \(O(n\log n)\)

//鐭ヨ瘑鐐癸細鏍戠姸鏁扮粍錛岄€嗗厓 
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define LL long long
const int kMaxn = 1e6 + 10;
const LL mod = 998244353;
//=============================================================
int n, d_num, a[kMaxn], data[kMaxn];
LL f, ans, inv[kMaxn];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void Chkmax(int &fir_, int sec_) {
  if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(int &fir_, int sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
LL QuickPow(LL x_, LL y_) {
  LL ret = 1;
  for (; y_; y_ >>= 1ll) {
    if (y_ & 1ll) ret = ret * x_ % mod;
    x_ = x_ * x_ % mod;
  }
  return ret;
}
namespace Bit {
  #define lowbit(x) (x&-x)
  int Lim, t[kMaxn];
  void Init(int lim_) {
    Lim = lim_;
  }
  void Insert(int pos_) {
    for (int i = pos_; i <= Lim; i += lowbit(i)) {
      t[i] ++;
    }
  }
  int Sum(int pos_) {
    int ret = 0;
    for (int i = pos_; i; i -= lowbit(i)) {
      ret += t[i];
    }
    return ret;
  }
}
void Prepare() {
  n = read();
  inv[1] = 1;
  for (int i = 1; i <= n; ++ i) {
    data[i] = a[i] = read(); 
    if (i > 1) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
  }
  
  std::sort(data + 1, data + n + 1);
  d_num = 1;
  for (int i = 2; i <= n; ++ i) {
    if (data[i] != data[i - 1]) ++ d_num;
    data[d_num] = data[i];
  }
  for (int i = 1; i <= n; ++ i) {
    a[i] = std::lower_bound(data + 1, data + d_num + 1, a[i]) - data;
  }
  Bit::Init(d_num);
}
//=============================================================
int main() {
  Prepare();
  for (int i = 1; i <= n; ++ i) {
    f = (f + 1ll * Bit::Sum(a[i] - 1)) % mod;
    ans = (ans + f * inv[i] % mod) % mod;
    Bit::Insert(a[i]);
  }
  printf("%lld\n", ans);
  return 0;
}

C 科學世紀

夢違,幻之紅屋里閃爍的異彩
現世,構築於毫無血色的石塊之上
空想的夢,描繪了古老的美麗都市的童話
白日,照在越發骯臟的街市里

《夢違科學世紀》

\(t\) 組數據。
給定參數 \(p,q\),求一個最大的 \(x\),滿足 \((x|p) \land (q\nmid {x})\)
\(1\le t\le 500\)\(1\le p\le 10^{18}\)\(2\le q\le 10^9\)
1S,512MB。

顯然 \(p \bmod q \not= 0\),輸出 \(p\)

\(p \bmod q = 0\) 時,有個想法。
想到讓 \(p\) 除以某些 \(q\) 的質因子,直到 \(p\) 不能被 \(q\) 整除。

\(p\) 中比 \(q\) 大的質因子沒有影響,可以僅考慮 \(q\) 的質因子。
並且僅削除一種質因子,肯定比削除多種質因子優。

於是枚舉 \(q\) 的每一種質因子,令 \(p\) 除以該質因子,直到 \(p\) 中該質因子的次數比 \(q\) 中的次數
統計削除不同質因子時,答案的最大值即可。

總復雜度 \(O(t\sqrt{q})\)


先對 \(p,q\) 質因數分解,設 \(\{a_i\}\) 為質數集,\(\{b_i\}\) 為對應質數的次數:

\[p=\prod a_{i}^{b1_i} \]

\[q=\prod a_{i}^{b2_i} \]

\((x|p) \land (q\nmid {x})\),則 \(x\) 質因數分解后有:

\[x=\prod a_{i}^{b3_i} \]

\[p=k\times x =k\times \prod a_{i}^{b3_i}(k\in \mathbf{N}^*) \]

\[\exist a_j|q,\ b3_j < b2_j \]

第二個條件表示 \(x|p\),第三個條件表示存在一個整除 \(q\) 的質數 \(a_j\),它在 \(x\) 中的次數比在 \(q\) 中的次數要小,從而使 \(q\nmid x\)

顯然,最優的 \(x\) 一定是 \(p\) 摳去一些質因子 \(a_j\),使得該質因子在 \(p\) 中的次數小於 \(q\) 中的次數后剩下的值。

顯然摳去的質因子最多有一個。

所以可以枚舉 \(q\) 的所有質因子並進行計算。


感謝 @caq 指出錯誤並提供了一份好看的代碼。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#define int long long
using namespace std;
int T;

signed main()
{
	cin>>T;
	while(T--)
	{
		int ans=1;
		int p,q;
		cin>>p>>q;
		int kap=p;
		if(p<q) 
		{
			cout<<p<<endl;
			continue;
		}
		if(p%q!=0)
		{
			cout<<p<<endl;
			continue;
		}
		int cun=q;
		for(int i=2;i*i<=cun;i++)
		{
			if(q%i==0ll)
			{
				int cntq=0;
				int cntp=0;
				while(q%i==0ll)
				{
					q/=i;
					cntq++;
				}
				while(p%i==0ll)
				{
					p/=i;
					cntp++;
				}
				if(cntp<cntq) continue;
				int maxn=kap;
				for(int k=cntp-cntq+1;k;k--)
				{
					maxn/=i;
				}
				if(maxn>ans)
				{
					ans=maxn;
				}
			}
		}
		if(q!=1)
		{
			int cntp_=0;
			while(p%q==0ll)
			{
				p/=q;
				cntp_++;
			}
			int pd=kap;
			for(int k=cntp_;k;k--)
			{
				pd/=q;
			}
			if(pd>ans) ans=pd;
		}
		cout<<ans<<endl;
	}
} 

D 百鬼夜行

伊人不見隱何方,
吹散迷霧落月光,
萃取盛宴集佳釀,
香氣溢滿堂。

無法簡述的題面。

其實根本沒有 -1 的點/cy

稍有點細節的狀壓 DP。

讀入時對所有時刻所有位置進行標記,記錄是否有大小西瓜。

發現小西瓜個數較小,考慮狀壓一下獲得的小西瓜狀態。
\(s_{t,x,y}\) 表示 \(t\) 時刻,位置 \((x,y)\) 的小西瓜的存在狀態。
\(f(t, x, y, S)\) 表示,在時間 \(t\),萃香的位置在 \((x,y)\),獲得的小西瓜狀態為 \(S\) 時,需要移動的最小步數。

初始化 \(f(1, sx, sy, s_{1,sx,sy}) = 0\),所有不可到達狀態的 \(f = \operatorname{Inf}\)

轉移時枚舉每個時刻每個位置,從能到達該位置的上個時刻的位置 \((x',y')\) 轉移過來,還需要枚舉上個位置的小西瓜狀態,有:

\[f(t,x,y,S|s_{t,x,y}) = \min\{f(t-1,x',y', S)\} + 1 \]

統計答案時枚舉最后一個時刻的所有取完所有小西瓜的狀態,取最小值即可。

復雜度 \(O(Thw2^m)\),數據范圍小可以隨便過。

//知識點:分層圖,狀態壓縮,最短路,DP 
/*
By:Luckyblock
注意數組大小 
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define LL long long
const int kMaxH = 5 + 1;
const int kMaxn = 20 + 1;
const int kMaxT = 100 + 1;
const int kMaxAll = (1 << 10) + 1;
const int kInf = 0x3f3f3f3f;
const int ex[5] = {0, 0, 0, 1, -1};
const int ey[5] = {0, 1, -1, 0, 0};
//=============================================================
int h, w, T, sx, sy;
int n, m, all, t1[kMaxn], t2[kMaxn];

int f[kMaxT][kMaxH][kMaxH][kMaxAll];
int s_num, s[kMaxT][kMaxH][kMaxH];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
void Chkmax(int &fir_, int sec_) {
  if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(int &fir_, int sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
void Prepare() {
  h = read(), w = read(), T = read();
  sx = read(), sy = read();
  n = read(), m = read();
  for (int i = 1; i <= n; ++ i) {
    t1[i] = read(), t2[i] = read();
    int type = read();
    if (type) s_num ++;
    for (int j = t1[i]; j < t2[i]; ++ j) {
      int x = read(), y = read();
      s[j][x][y] = type ? (1 << (s_num - 1)) : -1;
    }
  }
}
//=============================================================
int main() {
  Prepare();
  if (s[1][sx][sy] == -1) {
    printf("-1");
    return 0;
  }
  
  all = (1 << m) - 1;
  memset(f, 0x3f, sizeof (f));
  f[1][sx][sy][s[1][sx][sy]] = 0;
  for (int i = 2; i <= T; ++ i) {
    for (int x = 1; x <= w; ++ x) {
      for (int y = 1; y <= h; ++ y) {
        if (s[i][x][y] == -1) continue ;
        for (int j = 0; j <= 4; ++ j) {
          int fx = x + ex[j], fy = y + ey[j]; //from
          if (fx < 1 || fx > w || fy < 1 || fy > h) continue ;
          if (s[i - 1][fx][fy] == -1) continue ;
          for (int k = 0; k <= all; ++ k) {
            Chkmin(f[i][x][y][k | s[i][x][y]],
                   f[i - 1][fx][fy][k] + (j > 0));
          }
        }
      }
    }
  }
  
  int ans = f[0][0][0][0];
  for (int x = 1; x <= w; ++ x) {
    for (int y = 1; y <= h; ++ y) {
      Chkmin(ans, f[T][x][y][all]);
    }
  }
  printf("%d\n", ans < kInf ? ans : -1);
  return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM