1. 程式人生 > >集訓模擬賽3【啥也不會的一天】

集訓模擬賽3【啥也不會的一天】

# 前言 今天$T1$快速冪(可惜我假期摸魚……)$T2$是字典樹(正解其實應該是AC自動機,但是沒人會……)$T3$竟然整了一個雜湊dp……當場自閉。$T4$多種方法,最短路,暴力dp,線段樹均可。總的來說就是自閉的一天……~~(說實話,今天老師選的題真的是開玩笑一樣)~~ ## NO.1 李時珍的面板衣 ### 題目描述 $LSZ$很皮!$LSZ$的面板衣更皮! $LSZ$有很多件神奇的面板衣,而且$LSZ$總是喜歡一次穿上多件面板衣(一件套一件,而且一直穿好多天),這些面板衣有透明或不透明兩種狀態,當不透明的面板衣吸收了一天的陽光直射後,就會變成透明的面板衣,透明的面板衣能使陽光照射到裡層面板衣,而透明的面板衣再吸收陽光,會在第二天會變成不透明的面板衣,不透明的面板衣會阻止陽光照射到裡層面板衣。 $LSZ$從某天起(該天算作第$1$天)穿上$N$件面板衣(剛開始所有面板衣都是不透明的),問你最少要經過多少天,$LSZ$身上的面板衣都經歷過透明變化? 例如今天(公元$2018$年$6$月$17$日)$LSZ$穿了$3$件面板衣服,會在公元$2018$年$6$月$21$日$3$件面板衣都會經歷過透明變化。 ### 輸入格式 一行,只有一個整數$N$。 ### 輸出格式 最少的天數(對$N$取餘數) ### 樣例 #### 樣例輸入1 >3 #### 樣例輸出1 >2 #### 樣例1解釋 使用$0$代表面板衣透明狀態。 使用$1$代表面板衣不透明的狀態。 ![](https://img2020.cnblogs.com/blog/1996530/202006/1996530-20200629191838101-1389354959.png) $5$對$3$取餘數為$2$。 #### 樣例輸入2 >5 #### 樣例輸出2 >2 ### 資料範圍與提示 $20\%$的資料:$N \le 15$ $60\%$的資料:$N \le 10^7$ $100\%$的資料:$N \le 10^{10}$ ### 分析 看到這個題目一開始是沒有思路的,但是可以手模一到兩個小資料,然後就會發現,其實這個答案是有規律的。假設$i$件,$g[i]$為答案,那麼手模幾個可以發現這個題的規律跟漢諾塔差不多,即$g[i] = 2\times g[i-1]+1$,相信能做這種題的數列都學過,推個通項公式嚶該沒問題~~(好懶……)~~,最後可以推出來答案應該是$2^n-1$。 但是看到資料範圍,$N \le 10^{10}$,開玩笑??所以我們需要快速冪並取模。其實就是個板子,推出來公式就很好寫了。 ### 程式碼 ```cpp #include
#define ll unsigned long long ll n; ll pow(ll a,ll b){//注意如果using namespace std了,並且用的萬能庫,函式名千萬別用pow,有衝突但不報錯,我就是這麼掛的…… ll ans = 1;//這塊就是快速冪板子 ll base = a%n; while(b){ if((b & 1) !=0){ ans = (ans*base)%n; } base = (base*base)%n; b>>=1; } return ans; } int main(){//求值,注意過程取模 std::cin>
>n; ll ans = pow(2,n-1); ll jl = (ans+1)%n; std::cout<20 ad ae af ag ah ai aj ak al ads add ade adf adg adh adi adj adk adl aes 5 b a d ad s #### 樣例輸出 >0 20 11 11 2 ### 資料範圍與提示 $20\%$的資料:$n\le 100 , m\le 50$ $60\%$的資料:$n\le 10^4, m\le 500$ $100\%$的資料:$n\le 10^4, m\le 10^5$ ### 分析 題意就是從上邊給出的字串中找出子串含有下邊字串的數量。暴力列舉掃描能過一半的點,後邊的就$TLE$了。那麼正解是什麼呢,我們考慮字典樹($Tire$樹),其實正解應該是AC自動機,~~但是我沒學~~機房裡還有巨佬用的雙雜湊取模,(開了c++11使用unordered_map達到$O(1)$的查詢才過,不然會t。)對於我只是搞了搞字典樹。其中需要注意的是,因為查詢的是子串,所以我們需要把以最後一個字母為字尾的所有子串都存入,這樣才能達到全部能搜尋到,而且同一字串內到達一個節點時$cnt$不能加。然後就可以進行字典樹建樹查詢了。 ### 程式碼 ```cpp #include
using namespace std; const int maxn = 26; struct tree{//指標建立結構體。 struct tree *son[26];//兒子指標 int cnt;//記錄有多少情況到他 int flag;//記錄是第幾個字串,也就是id }*root; void insert(char *p,int id){//建樹,字元取指標 int i,k,j; tree *cur=root,*next;//初始化cur為根 int len=strlen(p); for(i=0;ison[k]!=NULL)//兒子不為空,就讓指標指向兒子 cur=cur->son[p[i]-'a']; else { next=new(tree);//兒子為空就建一個新指標 for(j=0;j<26;j++)//初始化 next->son[j]=NULL; next->cnt=0; next->flag=-1; cur->son[p[i]-'a']=next;//讓空的兒子為下一個 cur=next;//指標指向兒子 } if(cur->flag!=id)//不是同一個字串就cnt++,更新id { cur->cnt++; cur->flag=id; } } } int Find(char *p){//查詢 int i; tree *cur=root; int len = strlen(p); for(i=0;ison[k]==NULL){//子節點為空那麼肯定沒有該子串 break; } else{ cur=cur->son[k];//指標指向兒子 } } if(icnt;//返回數目 } int main(){ int n,m; int i,j; char s[25]; root = new(tree); for(int i=0;i<26;i++){ root->son[i]=NULL; } root->cnt=0; root->flag=-1; scanf("%d",&n); for(i=1;i<=n;i++){//建樹 scanf("%s",s); for(j=0;s[j];j++){ insert(s+j,i); } } cin>>m; for(int i=1;i<=m;++i){ cin>>s; int ans = Find(s); cout< using namespace std; int ans; char s1[10010][22],s2[22]; int n, m; int len1[10010],len2; bool judge(char s1[],char s2[]){ int j,k; int len = strlen(s2); int len2 = strlen(s1); for(int i=0;i>n; for(int i=1;i<=n;++i){ cin>>s1[i]; len1[i] = strlen(s1[i]); } cin>>m; for(int i=1;i<=m;++i){ cin>>s2; ans = 0; int len2=strlen(s2); for(int j=1;j<=n;++j){ if(len2 > len1[j])continue; if(judge(s2,s1[j]) == true){ //cout<5 ### 資料範圍與提示 約翰有$3$頭牛。牛棚在第$0$秒到第$4$秒之間需要打掃。第$1$頭牛想要在$0,1,2$秒內工作,為此她要求的報酬是$3$美元,其餘的依此類推。 ### 分析 此題有許許多多種,在這裡暫時分析一種,因為後邊我沒有理解,但是我會把程式碼放上,只能自行理解了…… 最短路 這個演算法比較巧妙,具體思路主要在建邊上,我們把每頭奶牛工作時間之間建邊,然後邊權為報酬,然後在每個時間點之間建邊,邊權為0,這樣就把開始的時間和結束的時間連起來了。當一個點能到達的時候,這個點和起點之間的點一定是全部都被覆蓋了的。具體的一種覆蓋情況見圖解 ![](https://img2020.cnblogs.com/blog/1996530/202006/1996530-20200629202739781-759569805.png) 上邊是有邊權的,下邊反著的邊沒有邊權 這樣就相當於一種覆蓋。只要從題目給的起點到終點全部被覆蓋,那麼跑一遍最短路就行了。如果到達不了,也就是距離為初始化的最大值,那麼輸出$-1$。值得注意的是,這個建邊需要的是時間段的建邊,所以建邊的時候要把終點加一而達到建的時間段的邊。又因為正著是有邊權的,逆著的是沒有的,所以就可以達到讓一頭牛可以工作到一半,從另一個時間開始讓另一頭牛工作的目的。~~(感性理解一下)~~ ### 程式碼 ```cpp #include using namespace std; const int maxn = 3e5+10; #define ll long long struct Node{ int v,next; ll val; }e[maxn]; int tot; int head[maxn]; int n,m,E; void Add(int x,int y,int val){//建邊 e[++tot].v = y; e[tot].next = head[x]; e[tot].val = val; head[x] = tot; } ll dis[maxn]; int vis[maxn]; priority_queue >q; void Dij(int m){//迪傑斯特拉求最短路,spfa不穩定 memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[m] = 0; q.push(make_pair(0,m)); while(!q.empty()){ int x = q.top().second; q.pop(); if(vis[x])continue; vis[x] = 1; for(int i=head[x];i;i=e[i].next){ int v = e[i].v; int z = e[i].val; if(dis[v] > dis[x]+z){ dis[v] = dis[x]+z; q.push(make_pair(-dis[v],v)); } } } } int main(){ scanf("%d%d%d",&n,&m,&E); for(int i=m;iE)y=E; Add(x,y+1,val);//每頭牛時間段之間建邊 } Dij(m); if(dis[E+1] == 0x3f3f3f3f3f3f3f3f){ printf("-1\n"); } else { printf("%lld",dis[E+1]);//到終點的最短路 } return 0; } ``` ### 暴力dp程式碼 ```cpp #include using namespace std; const int maxn = 1e4+10; const int maxm = 9e4+10; struct Node{ int l,r,w; }a[maxn]; int f[maxm]; int n,m,E; bool cmp(Node a,Node b){ if(a.r == b.r)return a.l>n>>m>>E; for(int i=1;i<=n;++i){ cin>>a[i].l>>a[i].r>>a[i].w; } sort(a+1,a+n+1,cmp); memset(f,0x3f,sizeof(f)); for(int i=1;i<=n;++i){ if(a[i].l<=m)f[i] = a[i].w; } for(int i=2;i<=n;++i){ for(int j=i-1;j>0;--j){ if(a[i].l <= a[j].r+1){ f[i] = min(f[i],f[j]+a[i].w); } else break; } if(a[i].r>=E)ans = min(ans,f[i]); } if(ans == 0x3f3f3f3f){ cout<<"-1"< #include using namespace std; const int lqs=1e4+10,INF=0x7f7f7f7f; struct Node{ int st,et,fee; bool operator <(const Node &A)const{ if(et==A.et)return st>1; if(p[que[mid]].et=f[i])tt--; if(p[que[tt]].et