集訓模擬賽3【啥也不會的一天】
阿新 • • 發佈:2020-06-29
# 前言
今天$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