1. 程式人生 > >20170820四校聯考

20170820四校聯考

輸出格式 one display 數字 http 初始 adb isp 哥哥

來看看IOIAu巨神zzx的名言技術分享

不用循環輸入就會狗啊哥哥!

上題目:

T1:

填算式(expr)

【題目描述】

填算式是一種簡單的數學遊戲,可以形式化描述如下:n 個數字a1; a2; …… ; an 排成一排(1<= ai<=9),相鄰兩個數字之間有一個空格。你可以在每個空格內填入運算符+- * 之一,也可以不填,要求得到的算式的運算結果等於k。你的任務是計算有多少種不同的正確算式。比如n = 3,3 個數字為2; 2; 2,k = 24時,有兩種不同的正確算式:22 + 2 = 24,2 + 22 = 24。

【輸入格式】
從文件expr.in 中讀入數據。
輸入的第一行包含兩個整數n; k,表示數字個數和要求的答案。
接下來一行,包含n 個整數,第i 個數為ai。相鄰兩個整數用一個空格隔開。
【輸出格式】
輸出到文件expr.out 中。
輸出一個整數,表示不同的正確算式個數。
【樣例1 輸入】
4 11
1 2 3 4
【樣例1 輸出】
3
【樣例1 解釋】
3 個正確的算式為:12 + 3 ?? 4 = 11,1 + 2  3 + 4 = 11,1 ?? 2 + 3  4 = 11。
【樣例2 輸入】
7 1
1 1 1 1 1 1 1
【樣例2 輸出】
241

【樣例3 輸入】
10 3276
7 7 8 6 1 4 1 1 1 4
【樣例3 輸出】
104

【子任務】
子任務會給出部分測試數據的特點。如果你在解決題目中遇到了困難,可以嘗試只
解決一部分測試數據。
每個測試點的數據規模及特點如下表:

技術分享


題解:

比較簡單的搜索吧(雖然我打掛了)
直接DFS從左到右枚舉每個空格填+,-,×還是不填,然後計算這個式子的值,這樣復雜度是4^(n-1)*n,期望得分95分

在DFS的參數裏面存儲當前運算結果,可以一邊DFS一邊計算,具體做法如下:
維護參數 a,b,c,初始 a=c=0,b=1
不填,則 c->10c+x[i]
填+,則 a->a+bc,b=1,c=x[i]
填-,則 a->a+bc,b=-1,c=x[i]
填×,則 b->bc,c=x[i]
最後 a+bc就是運算結果
復雜度優化到4^(n-1),期望得分100分


部分分算法:
如果不會DFS,可以用三重循環通過測試點1~12,期望得分60分

如果不會循環,可以用條件語句通過測試點1~4或者1~8,期望得分20~40分

代碼:

技術分享
#include<cstdio>
#define r register
#define Fn "expr"
typedef long long ll;
int n,k,ans,x[15];
bool check(ll a,ll b,ll c){return a+b*c==k;}
void dfs(int st,ll a,ll b,ll c){
    if(st==n){ans+=check(a,b,c);return;}
    dfs(st
+1,a,b,c*10+x[st]); dfs(st+1,a+b*c,1,x[st]); dfs(st+1,a+b*c,-1,x[st]); dfs(st+1,a,b*c,x[st]); } int main(){ freopen(Fn".in","r",stdin); freopen(Fn".out","w",stdout); scanf("%d%d",&n,&k); for(r int i=0;i<n;i++)scanf("%d",x+i); dfs(1,0,1,x[0]); printf("%d\n",ans); return 0; }
View Code

T2:

閉合子圖(closure)
技術分享


【樣例1 輸入】
5 5
1 3
3 4
5 4
1 5
2 1
【樣例1 輸出】
5
技術分享


題解:

部分分算法:枚舉子集
枚舉 V 的非空子集 S,根據定義判定是否滿足條件,更新答案
判定部分實現優秀的話可以做到O(1),期望得分20~25分


部分分算法:枚舉區間
註意到點集構成的區間只有 n(n+1)/2 個,可以枚舉區間然後將區間內的點作為S,進行判定
直接實現的復雜度 O(n^2*m),期望得分35分

註意到對於一個點u的出邊(u,v),只有最小的v和最大的v是有用的,可以將m縮小到2n
復雜度 O(n^3),期望得分40分

進一步地,可以枚舉左端點,然後從小到大枚舉右端點,維護區間內出邊指向點的最小值和最大值
復雜度 O(n^2),期望得分50分


部分分算法:樹的數據

對於測試點11,12,顯然所有形如[1,i]的區間都滿足條件
所以答案為n,復雜度O(n),期望得分10分,結合上述算法期望得分60分


部分分算法:DAG的數據

對於測試點13,14,判定區間[l,r]是否滿足要求只需要[l,r]內的點指向的點編號均不超過r
用p[i]表示i指向的點的編號(不存在則p[i]=i),則可以枚舉r,找出第一個滿足p[i]>r的i,那麽l=i+1,i+2,...,r都是合法的,答案加上r-i
從左到右維護p[i]的遞減單調棧即可,復雜度O(n),期望得分15分。
結合上述算法期望得分75分


算法:分治

考慮分治,假設當前統計的是[L,R]有多少個子區間合法,取mid=[(L+R)/2],統計包含mid和mid+1的合法區間個數
記p[i],q[i]分別表示i的出邊指向點的最小值和最大值(不存在則為i)
則區間[l,r]合法的條件是
對於l<=i<=mid,p[i]>=l (1) 且 q[i]<=r (2)
對於mid<i<=r,p[i]>=l (3) 且 q[i]<=r (4)
條件(1)只和l有關,條件(4)只和r有關,做一遍前綴/後綴最值把合法的l,r處理出來即可
記Q[l]=max{q[i]|l<=i<=mid},條件(2)就是r>=Q[l]
如果記r‘為最小的r>=mid滿足p[r‘]<l,那麽條件(3)就是r<r‘,且r‘隨l的減小而增大
那麽只需從大到小枚舉l,維護Q[l],單調維護r‘,答案加上[Q[l],r‘)內的合法r個數,這一部分復雜度是線性的
總復雜度O(nlogn),期望得分100分
如果復雜度不小心多寫了一個log可能只有95分

代碼:

技術分享
#include<cstdio>
#define maxn 300010
#define reg register
#define Fn "closure"
#define mod 1000000007
#define mid (lt+rt>>1)
typedef long long ll;
int n,m,ans;
int l[maxn],r[maxn],s[maxn];
inline int read(){
    reg int x=0,f=1;reg char c=getchar();
    for(;c<0||c>9;f=c==-?-1:1,c=getchar());
    for(;c>=0&&c<=9;x=(x<<3)+(x<<1)+c-0,c=getchar());
    return x*f;
}
void bs(int lt,int rt){
    if(rt-lt==1){ans+=l[lt]==lt&&r[lt]==lt;return;}
    reg int mr=0;
    s[mid-1]=0;
    for(reg int i=mid;i<rt;i++){
        if(r[i]>mr)mr=r[i];
        s[i]=s[i-1]+(mr==i);
    }
    reg int ml=1<<30,pos=mr=mid;
    for(reg int i=mid;i-->lt;){
        if(l[i]<ml)ml=l[i];
        if(r[i]>mr)mr=r[i];
        while(pos<rt&&l[pos]>=i)pos++;
        if(ml==i&&pos>mr)(ans+=s[pos-1]-s[mr-1])%=mod;
    }
    bs(lt,mid);
    bs(mid,rt);
}
int main(){
    freopen(Fn".in","r",stdin);
    freopen(Fn".out","w",stdout);
    n=read();m=read();
    for(reg int i=1;i<=n;)l[i]=r[i]=i++;
    while(m--){
        reg int s=read(),t=read();
        if(t<l[s])l[s]=t;
        if(t>r[s])r[s]=t;
    }
    bs(1,n+1);
    printf("%d\n",ans);
    return 0;
}
View Code

由於時間原因,T3暫時沒有打,請大佬們諒解!

20170820四校聯考