D. Fragmentation merging
題意:告訴你一段全排列的數組,你可以任意從中獲取兩端區間的數分別是兩個集合A,B,當然前提是這兩段數的交集為空集,當一開始的整個數組的長度大於1的時候A與B可以為空集。令C = A U B,C的最大值減去最小值加上一要等於C的size。問C有幾種。(Notice:任何集合並上空集都為原集合本身)
題解:對每一種情況進行判斷,看是不是由最多兩段組成就行了。我們獲取數組的本身下標,對下標進行操作。
枚舉左右端點,依次加入 l 到 r 的元素
維護當前的聯通塊數量:
若 x-1 和 x+1 沒有被標記,聯通塊個數加 1
若 x-1 和 x+1 都被標記,聯通塊個數減 1
若 x-1 和 x+1 有一個被標記,聯通塊不變
若聯通塊數目為 1 或 2,則答案加 1
AC代碼
#include "bits/stdc++.h" using namespace std; #define ll long long const int maxn=5e3+10; const int inf=0x3f3f3f3f; inline int rd(){ int a; scanf("%d",&a); return a; } int t; int n,m; int vis[maxn],a[maxn]; int main() { t=rd(); while(t--) { n=rd(); for(int i=1;i<=n;i++){ int aa=rd(); a[aa]=i; } if(n==1){ printf("0\n"); continue; } ll res=0; for(int i=1;i<=n;i++){ int cnt=0; for(int j=1;j<=5005;j++)vis[j]=0;//初始化 for(int j=i;j<=n;j++) { if (vis[a[j] - 1] && vis[a[j] + 1]) {//該點左右兩邊都已經加入,就將這兩段合並 cnt--; } else if (vis[a[j] - 1] || vis[a[j] + 1]) {//該點加入左邊或右邊 //*別做任何處理 } else cnt++;//新建一段 vis[a[j]]=1; if(cnt<=2)res++; } } printf("%lld\n",res); } return 0; }
I. Sequence
題意:自己看
題解:把一維的ABLR轉化成二維的坐標,然后就可以了
對於每一種情況進行數矩形的個數來判斷,就能轉化成單調棧求矩形個數的問題
方法一:容斥定理(容易MLE)
#include "bits/stdc++.h" using namespace std; //#define int long long #define ll long long inline int rd(){ int a; scanf("%d",&a); return a; } const int N=5001; ll maxn,sum; short last[N][N]; int f[N][N]; short n,a[N][N]; int m; vector<short> s[N]; int main() { scanf("%d %d",&n,&m); for (int i=1;i<=m;i++){ int aa=rd(),ab=rd(); a[aa][ab]=1; } for (int i=1;i<=n;i++) s[i].push_back(0); for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) { if (a[i][j]) last[i][j]=j; else last[i][j]=last[i][j-1]; while (s[j].size()>1 && last[s[j].back()][j]<last[i][j]) s[j].pop_back(); maxn+=i*j; f[i][j]=f[s[j].back()][j]+(i-s[j].back())*last[i][j]; sum+=f[i][j]; s[j].push_back(i); } printf("%lld\n",maxn-sum); return 0; }