主頁 > 後端開發 > CSP-S-2020

CSP-S-2020

2020-11-10 02:20:25 後端開發

CSP-S-2020

T1 儒略日

向T1出題人致以最高的敬意(磕頭),

T2 動物園

簡化題意:給出一些數,保證\(x∈[1,2^k-1]\)且互不相同,給出一些條件,如果存在數\(x\)\(ai\)位為\(1\),那么就必須選物品\(bi\)\(bi\)互不相同,問有多少個數\(y\)滿足不存在\(x=y\),并且加入\(y\)后選取的\(b\)不變,

很容易發現如果一個條件不被滿足,即不存在數\(x\)\(ai\)位為1,那么選取的數第\(ai\)位也必須為0,對于給出的\(k\)個位置,假設必須為0的位數有\(c\)個,那么剩下的數就有\(2^{k-c}\)個數加入后不會改變\(b\)的選取,再減去給出的\(n\)個數就得到了答案,

\(n=0,k=64\)的情況要特判,記得開\(unsigned\) \(long\) \(long\)

#include<bits/stdc++.h>
using namespace std;
#define ll unsigned long long
const int N=1e6+5;
int n,m,c,k;
ll now,t[70];
int a[N];

int main()
{
	scanf("%d %d %d %d",&n,&m,&c,&k);
	t[0]=1;
	for(int i=1;i<=k;++i) t[i]=t[i-1]*2;
	
	now=0;ll x;
	for(int i=1;i<=n;++i) 
	{
		scanf("%llu",&x);
		now|=x;
	}
	for(int i=1,b;i<=m;++i) scanf("%d %d",&a[i],&b);
	sort(a+1,a+m+1);
	a[0]=unique(a+1,a+m+1)-a-1;
	
	int tmp=k;
	for(int i=1;i<=a[0];++i)
	{
		if(a[i]>=tmp) break;
		if((now&t[a[i]])==0) k--;		
	}
	if(k==64&&n==0) printf("18446744073709551616");
	else printf("%llu",(ll)t[k]-n);
	return 0;
}

T3 函式呼叫

個人感覺這道題應該壓軸啊,怎么會是T3,

30pts

線段樹+遞回的做法還是很好想到的,線段樹只維護乘積,每次1操作的時候就先從線段樹把乘積乘下去,再進行1操作,實作是\(O(logn)\),2操作直接在線段樹根節點乘,實作是O(1),

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5,M=1e6+5;
const ll mod=998244353;

int n,m,Q;
int tot,cz3[M];
struct node{int op,x,y;}cz[N];
ll val[N],mul[N<<2];

void Build(int l,int r,int p)
{
	mul[p]=1;
	if(l==r) return;
	
	int mid=(l+r)>>1;
	Build(l,mid,p<<1);
	Build(mid+1,r,p<<1|1);
	return;
}

void Spread(int p)
{
	if(mul[p]==1) return;
	
	int l=p<<1,r=p<<1|1;
	mul[l]=mul[l]*mul[p]%mod;
	mul[r]=mul[r]*mul[p]%mod;
	mul[p]=1;
	return;
}

void Change(int l,int r,int x,ll y,int p)
{
	if(l==r)
	{
		val[l]=(val[l]*mul[p]+y)%mod;
		mul[p]=1;
		return;
	}
	
	Spread(p);
	int mid=(l+r)>>1;
	if(x<=mid) Change(l,mid,x,y,p<<1);
	else Change(mid+1,r,x,y,p<<1|1);
	
	return;
}

void Update(int l,int r,int p)
{
	if(l==r) 
	{
		val[l]=val[l]*mul[p]%mod;
		return;
	}
	Spread(p);
	int mid=(l+r)>>1;
	Update(l,mid,p<<1);
	Update(mid+1,r,p<<1|1);
	return;
}

void Calc(int pos)
{
	if(cz[pos].op==1) Change(1,n,cz[pos].x,(ll)cz[pos].y,1);
	else if(cz[pos].op==2) mul[1]=mul[1]*cz[pos].x%mod;
	else 
	{
		for(int i=cz[pos].x;i<=cz[pos].y;++i)
		Calc(cz3[i]);
	}
	return;
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%lld",&val[i]);
	scanf("%d",&m);
	for(int i=1;i<=m;++i)
	{
		scanf("%d",&cz[i].op);
		if(cz[i].op==1) scanf("%d %d",&cz[i].x,&cz[i].y);
		else if(cz[i].op==2) scanf("%d",&cz[i].x);
		else
		{
			scanf("%d",&Q);
			cz[i].x=tot+1;
			for(int j=1;j<=Q;++j) scanf("%d",&cz3[tot+j]);
			cz[i].y=(tot+=Q);
		}
	}
	Build(1,n,1);
	scanf("%d",&Q);
	for(int i=1,x;i<=Q;++i)
	{
		scanf("%d",&x);
		Calc(x);
	}
	Update(1,n,1);
	for(int i=1;i<=n;++i) printf("%lld ",val[i]);
	return 0;
}

100pts

線段樹做法敗在了3操作可以呼叫3操作,會進行很多重復計算,比如5呼叫2,8呼叫5(2,5,8為編號),最后給出的操作序列:2 5 8,那么操作順序就是:2 5 2 8 5 2,重復呼叫將時間疊上去了,

滿分思路是拓補排序,將每次操作都疊加在呼叫它的操作上,時間復雜度是\(O(n)\),不太好理解,

這種做法是將加法和乘法分開的,

舉個例子:假設只有一個數\(2\),先進行\(*3\)操作,再\(+1\),再\(*2\),按照之前的思路,計算程序是\((2*3+1)*2\),加法乘法拆開后計算程序變成\(2*3*2+1*2\)

因為每個乘法都是作用于整個陣列的,對于一開始給出的陣列,每個數擴大的倍數是一樣的,所以把所有的操作跑一遍之后:\(ai=k*ai\)

那么先講乘法部分,即如何快速求出這個\(k\)

乘法部分

首先要明白,最后給出的Q然后又進行一堆操作,可以看成一個3操作,即呼叫別的函式,假設編號為\(m+1\)

于是把所有操作都向呼叫它的操作連邊,就可以得到一張一定無環的有向圖,(如果存在環,即\(a\)呼叫\(b\)\(b\)呼叫\(a\),會陷入死回圈)

給出例圖(為了方便理解,呼叫順序假設都是從左到右呼叫):

因為每個操作都會被比它后呼叫的操作影響,所以倒著操作的時候可以保證這之后不存在有操作可以影響這個操作,

比如\(*3\)操作,如果后面藍色框框里得到的乘積是\(*5\),那么現在整張圖的乘積應該是\(*3*15\),(對應黃路和紫路)

可以看出來,\(*3\)\(*5\)操作,先算哪一個得到的整張圖的乘積都是一樣的,所以在乘積上,對于同一個操作,左右順序不重要,只需要保證這個操作進行的時候子操作都進行完了就可以了,

(比如給出的例子操作里,藍色框框里要先算最下面的三個點,得到上面的兩個點的值,才能用這兩個點向上面的一個點貢獻\(*5\)

所有可以用拓補排序按入度為0加入佇列的規則得到操作順序,

然后按這個操作順序每個點向連向的點貢獻乘積即可,

對于沒有被用到的操作,是不存在路徑節點到\(m+1\)的,所有它的貢獻是不會被累積到\(k\)里面的,

代碼實作(寫代碼的時候我的邊是從副操作到子操作,所有統計的是\(out\)):

inline void add(int u,int v)
{
	out[u]++;G[u].push_back(v);F[v].push_back(u);
}

void Init()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
	scanf("%d",&m);
	for(int i=1;i<=m;++i)
	{
		scanf("%d %d",&cz[i].op,&cz[i].x);
		if(cz[i].op==1) scanf("%d",&cz[i].y);
		else if(cz[i].op==3) for(int j=1,v;j<=cz[i].x;++j) scanf("%d",&v),add(i,v);
	}
	m++;
	scanf("%d",&Q);
	for(int i=1,v;i<=Q;++i) scanf("%d",&v),add(m,v);
}
void Topo()
{
	int l=1;
	for(int i=1;i<=m;++i)
	if(!out[i]) tq[++tq[0]]=i;
	
	while(l<=tq[0])
	{
		int u=tq[l++];
		
		int S=F[u].size();
		for(int i=0;i<S;++i)
		if(!--out[F[u][i]]) tq[++tq[0]]=F[u][i];
	}
}

void Update()
{
	sum[m]=mul[m]=1;
	for(int i=1;i<m;++i) sum[i]=(cz[i].op==2?cz[i].x:1);
	
	for(int i=1;i<=tq[0];++i)
	for(int j=0,S=F[tq[i]].size();j<S;++j)
	sum[F[tq[i]][j]]=sum[F[tq[i]][j]]*sum[tq[i]]%mod;
    	for(int i=1;i<=n;++i) a[i]=a[i]*sum[m]%mod;
}

加法部分

如圖,藍色框框代表許多許多操作(2節點在1節點之前還有操作,3在2之前也還有操作,畫上圖太亂了就沒畫),

假設2節點累積值是6,3節點累積值是10,

假設這里有個\(+5\)的操作,受到后面操作的影響,最后加上去的數會變成\(5*60+5*30+5*15\),(藍,綠,粉),

可以看出來,1直接連向4節點,這個時候23累積上去的乘積是60,所以1節點累加60,

1連向2節點,但2節點有兩種,

2節點直接連向4節點,這時候3累積上的乘積是10,所以2節點累加10,

2節點是連向3節點,3節點連向4節點,無后續影響3節點,此時根節點的累積值是1,所以3累加1,這時候3節點后續有個\(*5\)影響2節點,此時累積到4的積是1,所以在2累加5,

所以2的累加值為\(10+5\)

1在2這里會被\(*3\)影響,所以1的累加值是\(60+30+15\)

這樣會發現好像是一個遞回的操作,但其實倒著想可以看成一個從上到下的下放操作,實作極其簡單,代碼非常短,

	for(int i=tq[0];i;--i)
	for(int j=G[tq[i]].size()-1,ml=1;j>=0;--j)
	{
		mul[G[tq[i]][j]]=((ll)ml*mul[tq[i]]+mul[G[tq[i]][j]])%mod;
		ml=(ll)ml*sum[G[tq[i]][j]]%mod;
	}
		for(int i=1;i<=n;++i) a[i]=a[i]*sum[m]%mod;
	for(int i=1;i<m;++i)
	if(cz[i].op==1) a[cz[i].x]=(a[cz[i].x]+cz[i].y*mul[i])%mod;

放上完整代碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+5;
const ll mod=998244353;
int n,m,Q,out[N],tq[N];
ll a[N],mul[N],sum[N];
struct node{int op,x,y;}cz[N];
vector<int>G[N],F[N];

inline void add(int u,int v)
{
	out[u]++;G[u].push_back(v);F[v].push_back(u);
}

void Init()
{
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
	scanf("%d",&m);
	for(int i=1;i<=m;++i)
	{
		scanf("%d %d",&cz[i].op,&cz[i].x);
		if(cz[i].op==1) scanf("%d",&cz[i].y);
		else if(cz[i].op==3) for(int j=1,v;j<=cz[i].x;++j) scanf("%d",&v),add(i,v);
	}
	m++;
	scanf("%d",&Q);
	for(int i=1,v;i<=Q;++i) scanf("%d",&v),add(m,v);
}
void Topo()
{
	int l=1;
	for(int i=1;i<=m;++i)
	if(!out[i]) tq[++tq[0]]=i;
	
	while(l<=tq[0])
	{
		int u=tq[l++];
		
		int S=F[u].size();
		for(int i=0;i<S;++i)
		if(!--out[F[u][i]]) tq[++tq[0]]=F[u][i];
	}
}

void Update()
{
	sum[m]=mul[m]=1;
	for(int i=1;i<m;++i) sum[i]=(cz[i].op==2?cz[i].x:1);
	
	for(int i=1;i<=tq[0];++i)
	for(int j=0,S=F[tq[i]].size();j<S;++j)
	sum[F[tq[i]][j]]=sum[F[tq[i]][j]]*sum[tq[i]]%mod;
	
	for(int i=tq[0];i;--i)
	for(int j=G[tq[i]].size()-1,ml=1;j>=0;--j)
	{
		mul[G[tq[i]][j]]=((ll)ml*mul[tq[i]]+mul[G[tq[i]][j]])%mod;
		ml=(ll)ml*sum[G[tq[i]][j]]%mod;
	}
	
	for(int i=1;i<=n;++i) a[i]=a[i]*sum[m]%mod;
	for(int i=1;i<m;++i)
	if(cz[i].op==1) a[cz[i].x]=(a[cz[i].x]+cz[i].y*mul[i])%mod;
}
int main()
{	
	Init();
	Topo();
	Update();
	
	for(int i=1;i<=n;++i) printf("%lld ",a[i]);
}

T4 貪吃蛇

如果蛇A吃了蛇B后,會被別的蛇吃掉,那他就不會吃B,那么假設所有蛇都不聰明,那么當被吃掉的蛇,假設為A,A吃過別的蛇,假設被A吃的是B,那么A當初就不會吃B,因為它吃了B后會被別的蛇吃掉,所以每個蛇吃別的蛇的時候,都儲存一下如果這個蛇沒有吃的話現在還剩了幾條蛇,吃到第一條吃過別的蛇的蛇就回傳這個的儲存值,

可以用\(set\),平衡樹來快速插入洗掉,

但時間復雜度是A不了的,可以拿大部分分,

每次被吃掉的蛇一定是沒有吃過別的蛇的,就是在初始陣列的蛇,所以被吃的蛇一定是嚴格遞增,這個遞增是指按題上的大小排序,

另開一個陣列q來儲存A吃B之后的值,每次拿出來吃別的蛇的蛇一定嚴格遞減,所以吃完之后的得到的值也是嚴格遞減,保證了q的單調性,每次最大的要么是q的要么是原始陣列里的,每次最小的只能是原始陣列的,如果q的最小值比原始陣列小,本次計算就結束了,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/208600.html

標籤:其他

上一篇:微信小程式開發之云開發

下一篇:技術點5:XML語言

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more