2024“釘耙程式設計”中國大學生演算法設計超級聯賽(1)

Luckyblock發表於2024-07-20

目錄
  • 寫在前面
  • 7440
  • 7434
  • 7433
  • 7435
  • 7444
  • 7436
  • 7438
  • 寫在最後

寫在前面

補題地址:https://acm.hdu.edu.cn/listproblem.php?vol=65 7433 ~ 7444

以下按個人難度向排序。

我草我線段樹分治為啥過不去!!!

7440

dztlb 大神秒了,我看都沒看。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=40005;
int n,k,t,ans,sum; 
void solve(int x){
	int d=1;
	for(int i=0;i<k;++i){
		if(x&(1ll<<i)){
			d*=2;	
		}
	}
	ans+=d*sum;
}
signed main(){
	cin>>t;
	while(t--){
		ans=0,sum=0;
		cin>>n>>k;
		for(int i=(1ll<<k)-1;i>=0;--i){
			int tmp=1;
			for(int j=0;j<k;++j){
				if(((i>>j)&1)==0){
					tmp*=3;
				}
			}
			sum+=tmp;
		}
//		cout<<sum<<endl;
		for(int i=n;i;i=(i-1)&n){
			solve(i);
		}
		solve(0);
		cout<<ans<<'\n';
	}
	return 0;
}
/*
1
5
10 2 31 44 73

*/

7434

DP。

發現 \(nk\) 可過,則有顯然的二維 DP,設 \(f_{i, j}\) 表示使用前 \(i\) 次操作獲得 \(j\) 顆星星的最小代價之和,初始化 \(f_{i, j} = +\infin\)\(f_{0, 0} = 0\),則有轉移:

\[f_{i, j} = \min\left( f_{i - 1, j}, f_{i - 1, j - 1} + a, f_{i - 1, j - 2} + b, f_{i - 1, j - 3} + c, f_{i - 1, j - 4} + d\right) \]

答案即為 \(f_{n, k}\)。總時間複雜度 \(O(nk)\) 級別。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const LL kInf = 1e18 + 2077;
const int kN = 1010;
//=============================================================
int n, k;
LL ans, cost[kN][5], f[kN][4 * kN];
//=============================================================
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;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  int T; std::cin >> T;
  while (T --) {
    n = read(), k = read();

    for (int i = 0; i <= n; ++ i) {
      for (int j = 0; j <= k; ++ j) {
        f[i][j] = kInf;
      }
    }
    f[0][0] = 0;
    
    for (int i = 1; i <= n; ++ i) {
      for (int j = 1; j <= 4; ++ j) cost[i][j] = read();
      for (int j = 0; j <= k; ++ j) {
        f[i][j] = std::min(f[i][j], f[i - 1][j]);
        for (int l = 1; l <= std::min(j, 4); ++ l) {
          f[i][j] = std::min(f[i][j], f[i - 1][j - l] + cost[i][l]);
        }
      }
    }

    printf("%lld\n", f[n][k]);
  }
  return 0;
}

7433

雜湊。

發現 \([A]\) 即為 \(A\)\(|A|\) 種迴圈移位,即字串 \(A+A\)\(n\) 種長度為 \(|A|\) 的連續子串。於是考慮對 \(A + A, B\) 分別進行字串雜湊,記錄上述子串的雜湊值,然後列舉 \(B\) 中長度為 \(|A|\) 的子串檢查是否出現即可。

總時間複雜 \(O(\sum |A| + |B|)\) 級別。

//知識點:二分答案,Hash
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kMaxn = 3e6 + 10;
const LL kMod1 = 998244353;
const LL kMod2 = 1e9 + 9;
const LL kBase = 1145141;
//=============================================================
int n1, n2, ans;
char s1[kMaxn], s2[kMaxn];
LL pow1[kMaxn], pow2[kMaxn];
LL has11[kMaxn], has12[kMaxn], has21[kMaxn], has22[kMaxn];
std::set <std::pair<LL,LL> > yes;
//=============================================================
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 Prepare() {
  scanf("%s", s1 + 1);
  scanf("%s", s2 + 1);
  n1 = strlen(s1 + 1);
  n2 = strlen(s2 + 1);
  
  for (int i = 1; i <= n1; ++ i) s1[n1 + i] = s1[i];
  n1 = 2 * n1;
  s1[n1 + 1] = '\0';
  
  pow1[0] = pow2[0] = 1;
  for (int i = 1; i < std::max(n1, n2); ++ i) {
    pow1[i] = pow1[i - 1] * kBase % kMod1;
    pow2[i] = pow2[i - 1] * kBase % kMod2;
  }
  for (int i = 1; i <= n1; ++ i) {
    has11[i] = (has11[i - 1] * kBase + s1[i]) % kMod1;
    has12[i] = (has12[i - 1] * kBase + s1[i]) % kMod2;
  }
  for (int i = 1; i <= n2; ++ i) {
    has21[i] = (has21[i - 1] * kBase + s2[i]) % kMod1;
    has22[i] = (has22[i - 1] * kBase + s2[i]) % kMod2;
  }
}
void solve() {
  ans = 0;

  yes.clear();
  for (int l = 1, r = n1 / 2; r <= n1; ++ l, ++ r) {
    LL now_has11 = ((has11[r] - has11[l - 1] * pow1[r - l + 1] % kMod1) + kMod1) % kMod1;
    LL now_has12 = ((has12[r] - has12[l - 1] * pow2[r - l + 1] % kMod2) + kMod2) % kMod2;
    yes.insert(std::make_pair(now_has11, now_has12));
  }
  for (int l = 1, r = n1 / 2; r <= n2; ++ l, ++ r) {
    LL now_has21 = ((has21[r] - has21[l - 1] * pow1[r - l + 1] % kMod1) + kMod1) % kMod1;
    LL now_has22 = ((has22[r] - has22[l - 1] * pow2[r - l + 1] % kMod2) + kMod2) % kMod2;
    if (yes.count(std::make_pair(now_has21, now_has22))) ++ ans;
  }
}
//=============================================================
int main() {
  // freopen("A.txt", "r", stdin);
  int T = read();
  while (T --) {
    Prepare();
    solve();
    printf("%d\n", ans);
  }
  return 0;
}
/*
opawmfawklmiosjcas1145141919810asopdfjawmfwaiofhauifhnawf
opawmdawlmioaszhcsan1145141919810bopdjawmdaw
*/

7435

dsu on tree。

挺板的。

發現計算 \(f(u, v)\) 時僅需考慮兩個權值間的大小關係即可,和樹的形態並無關係。則對於子樹的答案 \(ans_i\),僅需考慮子樹中節點的權值構成的多重集 \(S\) 即可。先處理對於某個子樹的單次詢問。考慮不斷向上述多重集 \(S\) 中加入權值 \(x\),並求得新增的 \(\sum_{y\in S} f(x, y)\) 的貢獻,發現僅需討論 \(x\)\(y\) 的相對關係大小:

  • \(x = y\)\(f(x, y) = 0\)
  • \(x < y\)\(f(x, y) = y(y - x) = y^2 - xy\),則所有 \(y\) 的貢獻之和為 \(\left(\sum_y y^2\right) - x\left(\sum_y y\right)\)
  • \(x > y\)\(f(x, y) = x(x - y) = x^2 - xy\),則所有 \(y\) 的貢獻之和為 \(x^2\left(\sum_y 1\right) + x\left(\sum_y y\right)\)

發現上述 \(\sum_y 1\)\(\sum_y y\)\(\sum_y y^2\) 即為某段權值區間內某個權值貢獻分別為 1、\(y\)\(y^2\) 時,該權值區間的貢獻之和。很容易使用權值樹狀陣列維護。則單次子樹詢問操作僅需按任意順序列舉所有權值,並不斷把他們加入多重集,在此過程中使用樹狀陣列維護新增貢獻即可。

又本題要求所有子樹的貢獻,發現將每個節點加入多重集的操作是獨立的,於是直接套個 dsu on tree 就完了。

模數為 \(2^{64}\) 直接自然溢位啥事兒沒有!

總時間複雜度 \(O(n\log^2 n)\) 級別。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 5e5 + 10;
const int kM = kN << 1;
const int kLim = 1e6;
//=============================================================
int n, a[kN], edgenum, head[kN], v[kM], ne[kM];
int dfnnum, node[kN], L[kN], R[kN], sz[kN], son[kN];
unsigned LL nowans, ans[kN];
//=============================================================
const int kNode = kM;
#define lowbit(x) ((x)&(-x))
struct Bit {
  int lim; 
  unsigned LL t[kNode];
  void init(int lim_) {
    lim = lim_;
  }
  void insert(int pos_, unsigned LL val_) {
    for (int i = pos_; i <= lim; i += lowbit(i)) {
      t[i] += val_;
    }
  }
  LL sum(int pos_) {
    LL ret = 0;
    for (int i = pos_; i; i -= lowbit(i)) ret += t[i];
    return ret;
  }
  LL query(int l_, int r_) {
    if (l_ > r_) return 0;
    return sum(r_) - sum(l_ - 1);
  }
} bit[3];
void Add(int u_, int v_) {
  v[++ edgenum] = v_;
  ne[edgenum] = head[u_];
  head[u_] = edgenum;
}
void add(int u_) {
  unsigned LL v = a[u_];
  nowans += v * (bit[0].sum(v - 1) * v - bit[1].sum(v - 1));
  nowans += bit[2].query(v + 1, kLim) - v * bit[1].query(v + 1, kLim);

  bit[0].insert(v, 1);
  bit[1].insert(v, v);
  bit[2].insert(v, v * v);
}
void del(int u_) {
  unsigned LL v = a[u_];
  nowans -= v * (bit[0].sum(v - 1) * v - bit[1].sum(v - 1));
  nowans -= bit[2].query(v + 1, kLim) - v * bit[1].query(v + 1, kLim);

  bit[0].insert(v, -1);
  bit[1].insert(v, -v);
  bit[2].insert(v, -v * v);
}
void Dfs1(int u_, int fa_) {
  L[u_] = ++ dfnnum;
  node[dfnnum] = u_;
  sz[u_] = 1;
  for (int i = head[u_]; i; i = ne[i]) {
    int v_ = v[i];
    if (v_ == fa_) continue;
    Dfs1(v_, u_);
    sz[u_] += sz[v_];
    if (sz[v_] > sz[son[u_]]) son[u_] = v_;
  }
  R[u_] = dfnnum;
}
void Dfs2(int u_, int fa_, bool son_) {
  for (int i = head[u_]; i; i = ne[i]) {
    int v_ = v[i];
    if (v_ == fa_ || v_ == son[u_]) continue;
    Dfs2(v_, u_, 0);
  }
  if (son[u_]) Dfs2(son[u_], u_, 1);
  for (int i = head[u_]; i; i = ne[i]) {
    int v_ = v[i];
    if (v_ == fa_ || v_ == son[u_]) continue;
    for (int j = L[v_]; j <= R[v_]; ++ j) add(node[j]);
  }
  add(u_);
  ans[u_] = nowans;
  if (!son_) {
    for (int i = L[u_]; i <= R[u_]; ++ i) {
      del(node[i]);
    }
  }
}
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  std::cin >> n;
  for (int i = 1; i < n; ++ i) {
    int u_, v_; std::cin >> u_ >> v_;
    Add(u_, v_), Add(v_, u_);
  }
  for (int i = 1; i <= n; ++ i) std::cin >> a[i];

  for (int i = 0; i < 3; ++ i) bit[i].init(kLim);
  Dfs1(1, 1), Dfs2(1, 0, 0);

  unsigned LL output = 0;
  for (int i = 1; i <= n; ++ i) output ^= 2ll * ans[i];
  std::cout << output << "\n";
  return 0;
}

7444

數學,亂搞。

dztlb 大神寫的,場上我看都沒看。

考慮到給定矩形數量不太多,

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=4105;
const int mod=998244353;
int n;
short ma[8008][8008];
int C[2005][2005];
struct node{
	int a,b,c,d;
}p[N];
int a[N*8],b[N*8],aid[N*8],bid[N*8];
int t1,t2;
int ans[N],sum[N];
int qpow(int x,int y){
	int ans=1;
	while(y){
		if(y&1) ans=ans*x%mod;
		x=x*x%mod;
		y>>=1;
	} return ans;
}
signed main(){
	cin>>n;
	C[0][0]=1;
	for (int i = 1; i <= n; ++ i) {
		C[i][0]=1;
		C[i][i]=1;
		for (int j=1;j<=i-1;++j) C[i][j] = (C[i-1][j] + C[i-1][j-1]) % mod;
	}
	for(int i=1;i<=n;++i){
		cin>>p[i].a>>p[i].b>>p[i].c>>p[i].d;
		p[i].a+=5;
		p[i].b+=5;
		p[i].c+=5;
		p[i].d+=5;
		p[i].c--,p[i].d--;
		a[++t1]=p[i].a,a[++t1]=p[i].c;
		a[++t1]=p[i].a-1,a[++t1]=p[i].c+1;
		b[++t2]=p[i].b,b[++t2]=p[i].d;	
		b[++t2]=p[i].b-1,b[++t2]=p[i].d+1;	
	}
	sort(a+1,a+1+t1);
	sort(b+1,b+1+t2);
	int l1=unique(a+1,a+1+t1)-(a+1);
	int l2=unique(b+1,b+1+t2)-(b+1);
	for(int i=1;i<=n;++i){
		p[i].a=lower_bound(a+1,a+l1+1,p[i].a)-a;
		p[i].c=lower_bound(a+1,a+l1+1,p[i].c)-a;
		p[i].b=lower_bound(b+1,b+l2+1,p[i].b)-b;
		p[i].d=lower_bound(b+1,b+l2+1,p[i].d)-b;
//		cout<<p[i].a<<' '<<p[i].b<<' '<<p[i].c<<' '<<p[i].d<<endl;
		ma[p[i].a][p[i].b]++;
		ma[p[i].a][p[i].d+1]--;
		ma[p[i].c+1][p[i].b]--;
		ma[p[i].c+1][p[i].d+1]++;
	}
//	cout<<l1<<' '<<l2<<endl;
//	for(int i=1;i<=l1;++i){
//		for(int j=1;j<=l2;++j){
//			cout<<ma[i][j]<<' ';
//		}cout<<endl; 
//	}
	for(int i=1;i<=l1;++i){
		for(int j=1;j<=l2;++j){
			ma[i][j]+=ma[i-1][j]+ma[i][j-1]-ma[i-1][j-1];
//			cout<<ma[i][j]<<' ';
			if(ma[i][j]>=1) {
				
				sum[ma[i][j]]+=(a[i+1]-a[i])*(b[j+1]-b[j])%mod,sum[ma[i][j]]%=mod;
				
				}
			
		}
//		cout<<endl; 
	}
//	for(int i=1;i<=n;++i){
//		cout<<sum[i]<<endl;
//	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			if(n-j>=i) ans[i]+=sum[j]*((C[n][i]-C[n-j][i]+mod)%mod)%mod;
			else ans[i]+=sum[j]*(C[n][i])%mod;
			ans[i]%=mod;
		}
//		cout<<ans[i]<<"!!!!\n";
		cout<<ans[i]*qpow(C[n][i],mod-2)%mod<<'\n';
	}
	return 0;
}
/*
3
1 1 2 2
3 3 4 4
1 1 4 4
*/

7436

線段樹分治,並查集。

感覺是本場最智慧的一集,可惜沒調出來輸輸輸。


7438

DP,結論

我草這題好玩啊

寫在最後

學到了什麼:

  • 7436:
  • 7438:

mad 小梓立牌怎麼賣那麼快大早上到就沒貨了付款之前搶完了加代子也沒拿到只能拿了個紗織輸輸輸千年是明日奈太幾把傻逼了就算換個尼祿上來也不至於這個人氣、、、

相關文章