題目名稱 加密 氣泡排序圖 重建
可執行檔名 encrypt bubble rebuild
輸入檔名 encrypt.in bubble.in rebuild.in
輸出檔名 encrypt.in bubble.out rebuild.in
每個測試點時限 1 秒 1 秒 1 秒
記憶體限制 512MB 512MB 512MB
測試點數目 10 20 10
每個測試點分值 10 5 10
是否有部分分 否 否 否
題目型別 傳統型 傳統型 傳統型
測試題 #3 加密

加密
【問題描述】
有一種不講道理的加密方法是: 在字串的任意位置隨機插入字元。 相應的,
不講道理的解密方法就是從字串中恰好刪去隨機插入的那些字元。
給定原文?和加密後的字串?,求?有多少子串可以通過解密得到原文?。
【輸入格式】
輸入第一行包含一個字串?,第二行包含一個字串?。
【輸出格式】
輸出一行,包含一個整數,代表可以通過解密得到原文的?的子串的數量。
【樣例輸入】
abcabcabc
cba
【樣例輸出】
9
【樣例解釋】
用[?,?]表示子串開頭結尾的下標(從 0 開始編號) ,這 9 種方案是:
[0,6],[0,7],[0,8],[1,6],[1,7],[1,8],[2,6],[2,7],[2,8]
【資料規模和約定】
30%的資料,|?| 1000。
對於100%的資料,1 ≤ |?| ≤ 300,000,1 ≤ ? ≤ 200。

/*
這題沒想出來不應該啊 .....
考試的時候考慮這樣做 找每個最小的覆蓋區間
但是有重複 似乎就搞到了容斥原理上
其實每找到一個區間 然後回頭找 找到最近的合法的
假設 大區間是 [s,t] 回頭找的是 [ss,t] 那麼
我們累計貢獻 (s-last)*(n-s+1) 下一次從ss開始找
last是上次一的ss在哪 這樣就很好的解決了這個區間重複的問題
因為每次的起點限制在ss之後 而之前統計的區間起點都一定在ss之前
這樣就好了
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#define maxn 300010
using namespace std;
int n,m;
long long ans;
char a[maxn],b[maxn];
int main()
{
freopen("encrypt.in","r",stdin);
freopen("encrypt.out","w",stdout);
scanf("%s%s",a+,b+);
n=strlen(a+);m=strlen(b+);
int s=,t=,st=;while(s<=n){
if(a[++s]==b[t+])t++;
if(t==m){
for(int i=s;i>=;i--){
if(a[i]==b[t])t--;
if(t==){t=i;break;}
}
ans+=(t-st)*(n-s+);s=st=t;t=;
}
}
cout<<ans<<endl;
fclose(stdin);fclose(stdout);
return ;
}

測試題 #3 氣泡排序圖
氣泡排序圖
【問題描述】
有一段使用氣泡排序產生一張圖的虛擬碼如下:
function bubbleSortGraph(n, a[]):
graph = emptyGraph()
repeat
swapped = false
for i = 1 to n - 1:
if a[i] > a[i + 1]:
graph.addEdge(a[i], a[i + 1])
swap(a[i], a[i + 1])
swapped = true
until not swapped
return graph
函式的輸入為長度為?的排列?[],輸出為一張?個節點的無向圖。函式中,
emptyGraph()創造了一張空的無向圖,addEdge(x, y)向圖中添加了一條 x
和 y 之間的無向邊,最後返回的 graph 即產生的無向圖。
圖的點獨立集為圖中節點集合的一個子集。如果集合?是圖?的點獨立集,
那麼?中任意兩個節點在圖?中都沒有邊直接相連。
給定1~?的一個排列, 請求出按照虛擬碼生成的無向圖的最大點獨立集的大
小,以及一定會存在於最大點獨立集中的節點。
【輸入格式】
輸入第一行包含一個整數?。接下來一行包含?個空格分隔的整數,代表序
列?[]。
【輸出格式】
輸出兩行。 第一行包含一個整數, 代表生成的無向圖的最大點獨立集的大小。
第二行輸出最大點獨立集中一定會包含的節點在輸入序列中對應的下標, 按照從
小到大的順序輸出,以空格分隔。
【樣例輸入】
3
3 1 2
【樣例輸出】
2
2 3
測試題 #3 氣泡排序

【資料規模和約定】
36。
60%的資料,? ≤ 1000。
對於100%的資料,1 ≤ ? ≤ 100,000。
【提示】
一定存在於最大點獨立集中的節點數未必等於最大點獨立集的大小。
測試題 #3 重建

傻逼暴力55

#include<iostream>
#include<cstdio>
#define maxn 100010
using namespace std;
int n,a[maxn],f[maxn],g[maxn],ans;
int init(){
int x=,f=;char s=getchar();
while(s<''||s>''){if(s=='-')f=-;s=getchar();}
while(s>=''&&s<=''){x=x*+s-'';s=getchar();}
return x*f;
}
void Dfs(int x,int y){
for(int i=x-;i>=;i--)
if(a[i]<a[x]&&f[i]+==y){
g[i]=;Dfs(i,f[i]);
}
}
int main()
{
freopen("bubble.in","r",stdin);
freopen("bubble.out","w",stdout);
n=init();
for(int i=;i<=n;i++)
a[i]=init(),f[i]=;
for(int i=;i<=n;i++)
for(int j=;j<i;j++)
if(a[i]>a[j])f[i]=max(f[i],f[j]+);
for(int i=;i<=n;i++)
ans=max(ans,f[i]);
for(int i=n;i>=;i--)
if(f[i]==ans){
g[i]=;Dfs(i,ans);
}
printf("%d\n",ans);
for(int i=;i<=n;i++){
int falg=;if(!g[i])continue;
for(int j=i+;j<=n;j++)
if(f[i]==f[j]&&g[j]){
g[j]=;falg=;
}
if(!falg)printf("%d ",i);
}
fclose(stdin);fclose(stdout);
return ;
}
/*
自己改的跑了5s多 基本一個點就是同桌的total
這特麼就很尷尬了
然後打開了std
.....
果然是把dfs找路徑的過程改了 正著最長上升 到這最長
然而沒有自己想的那麼麻煩 a[i]=-a[i](就像懶得過載優先佇列一樣的*-1)
想到這句就好多了 多了 剩下的一毛一樣
*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 100010
using namespace std;
int n,a[maxn],c[maxn],num,f[maxn],g[maxn],r[maxn];
int init(){
int x=,f=;char s=getchar();
while(s<''||s>''){if(s=='-')f=-;s=getchar();}
while(s>=''&&s<=''){x=x*+s-'';s=getchar();}
return x*f;
}
int main()
{
//freopen("bubble.in","r",stdin);
//freopen("bubble.out","w",stdout);
n=init();
for(int i=;i<=n;i++)a[i]=init();
for(int i=;i<=n;i++){
int x=a[i];if(x>c[num]){
c[++num]=x;f[i]=num;continue;
}
int pos=lower_bound(c+,c++num,x)-c;
c[pos]=x;f[i]=pos;
}
printf("%d\n",num);num=;c[]=-maxn;
for(int i=n;i>=;i--){
int x=-a[i];if(x>c[num]){
c[++num]=x;g[i]=num;continue;
}
int pos=lower_bound(c+,c++num,x)-c;
c[pos]=x;g[i]=pos;
}
for(int i=;i<=n;i++)
if(f[i]+g[i]==num+)r[f[i]]++;
for(int i=;i<=n;i++){
if(f[i]+g[i]==num+&&r[f[i]]==)
printf("%d ",i);
}
fclose(stdin);fclose(stdout);
return ;
}

重建
【問題描述】
給定一個?個點?條邊的有向圖。你可以選擇一個節點?,然後重建所有能
從?到達,而且能到達?的所有節點(包括?自身) 。此外,你還可以先將一條邊
改成雙向邊,然後再進行上面的選擇。
請你求出最多可以重建的節點數, 並求出通過選擇哪些邊改成雙向邊才能使
重建的節點達到最多。
【輸入格式】
輸入的第一行包含兩個整數?和?。
接下來?行,每行包含兩個整數?和?,描述一條有向邊。
保證圖中任意兩點在任意方向上最多隻有一條邊直接相連。
【輸出格式】
輸出三行。第一行輸出一個整數,最多可以重建的節點數。
第二一個整數?,代表有?條邊能使重建節點數達到最多。
第三行輸出?個整數,代表可以選擇的邊的編號。邊按照輸入順序從 1 開始
編號。請按照從小到大的順序輸出,並以空格分隔。
【樣例輸入 1】
5 4
1 2
2 3
1 3
4 1
【樣例輸出 1】
3
1
3
【樣例輸入 2】
3 4
1 2
2 1
1 3
3 1
測試題 #3 重建
第 6 頁 共 6 頁
【樣例輸出 2】
3
4
1 2 3 4
【資料規模和約定】
3?,? ≤ 10。
60%的資料,? ≤ 1500,? ≤ 100,000。
對於100%的資料,1 ≤ ? ≤ 2000,? ≤ ? × ?。

30

/*
自己寫的縮點然後求兩點之間的最長路
wa了幾個點 好奇的打開了資料
然而看不出啥 造了一坨小資料和暴力排
終於 發現了錯誤
不是求兩點之間的最長路 有可能有兩條路 或者更多
就比如 1->2->3
1->4->3
1->3
如果跑1->3的最長路 就弄丟了另一條
這特麼就很尷尬了
資料還給了30分23333
經過一個小時的對拍+畫圖 終於找到了錯誤
然而不會改了.....不會改了
打開了std 很機智的bitset 然而不會用 於是乎棄療了
答題的思路就是 維護每個點能到那些點 這裡用位運算優化
然後剩下的思路就差不多了
*/
#include<cstdio>
#include<queue>
#include<iostream>
#define inf 1e8
#define maxn 2010
using namespace std;
int n,m,num,head[maxn],mx[maxn][maxn],bl[maxn],cnt,ans[maxn*maxn],vis[maxn][maxn];
int N,M,Head[maxn],v[maxn],dfn[maxn],low[maxn],f[maxn],s[maxn],top,topt;
queue<int>q;
struct node{
int v,pre;
}e[maxn*maxn],p[maxn*maxn];
struct edge{
int u,v;
}E[maxn*maxn];
int init(){
int x=,f=;char s=getchar();
while(s<''||s>''){if(s=='-')f=-;s=getchar();}
while(s>=''&&s<=''){x=x*+s-'';s=getchar();}
return x*f;
}
void Add(int from,int to){
num++;e[num].v=to;
e[num].pre=head[from];
head[from]=num;
}
void Ad(int from,int to){
printf("%d %d\n",from,to);
if(vis[from][to])return;
vis[from][to]=;
M++;p[M].v=to;
p[M].pre=Head[from];
Head[from]=M;
}
void Tarjan(int x){
dfn[x]=low[x]=++topt;
f[x]=;s[++top]=x;
for(int i=head[x];i;i=e[i].pre){
int v=e[i].v;
if(dfn[v]==){
Tarjan(v);low[x]=min(low[x],low[v]);
}
else if(f[v])low[x]=min(low[x],dfn[v]);
}
if(low[x]==dfn[x]){
N++;while(x!=s[top]){
v[N]++;bl[s[top]]=N;f[s[top]]=;--top;
}
v[N]++;bl[s[top]]=N;f[s[top]]=;--top;
}
}
/*void Dfs(int s,int now,int from){
mx[s][now]=max(mx[s][now],mx[s][from]+v[now]);
for(int i=Head[now];i;i=p[i].pre){
int v=p[i].v;Dfs(s,v,now);
}
}*/
void SPFA(int s){
for(int i=;i<=n;i++)f[i]=;
f[s]=;mx[s][s]=v[s];q.push(s);
while(!q.empty()){
int k=q.front();f[k]=;q.pop();
for(int i=Head[k];i;i=p[i].pre){
int to=p[i].v;
if(mx[s][to]<mx[s][k]+v[to]){
mx[s][to]=mx[s][k]+v[to];
if(f[to]==){
f[to]=;q.push(to);
}
}
}
}
}
int main()
{
freopen("rebuild.in","r",stdin);
freopen("rebuild.out","w",stdout);
n=init();m=init();int u,v;
for(int i=;i<=m;i++){
u=init();v=init();Add(u,v);
E[i].u=u;E[i].v=v;
}
for(int i=;i<=n;i++)
if(dfn[i]==)Tarjan(i);
printf("\n");
for(int u=;u<=n;u++)
for(int i=head[u];i;i=e[i].pre){
int v=e[i].v;if(bl[u]==bl[v])continue;
Ad(bl[u],bl[v]);
}printf("\n");
for(int i=;i<=N;i++)SPFA(i);
//Dfs(i,i,0);
int mxx=;
for(int i=;i<=m;i++){
u=bl[E[i].u];v=bl[E[i].v];
if(mx[u][v]>mxx){
mxx=mx[u][v];
cnt=;ans[++cnt]=i;
}
else if(mx[u][v]==mxx)
ans[++cnt]=i;
}
printf("%d\n%d\n",mxx,cnt);
for(int i=;i<=cnt;i++)
printf("%d ",ans[i]);
return ;
}