5.31模拟赛

5.31模拟赛


第一题:yist

题目大意:
给定n根直的木棍,要从中选出6根木棍,满足:能用这6 根木棍拼出一个正方形。
注意木棍不能弯折。问方案数。

数据规模:
60% n<=1000
100% n<=5000

题解:

60分:

#include 
#include 
#include 
#include 
#define MP make_pair
#define D(x) cout<<#x<<" = "<
#define E cout<
using namespace std;
typedef pair<int,int> pii;
const int N = 1005;
typedef long long LL;

int n,m,a[N],b[N*N/2];
pii tp[N*N/2]; int tpcnt;
int C[N][5];

int read(int &num){
    num=0; char c;
    while(!isdigit(c=getchar()));
    num=c-'0';
    while(isdigit(c=getchar())) num=num*10+c-'0';
}

void init(int n){
    C[1][0]=C[1][1]=1;
    for(int i=2;i<=n;i++){
        C[i][0]=1;
        for(int j=1;j<=3;j++){
            C[i][j]=C[i-1][j]+C[i-1][j-1];
        }
    }
}

struct Stack{
    int num[N*N/2],cnt[N*N/2]; int top;
    Stack(){ top=0; }
    void push(int x,int c){
        num[++top]=x; cnt[top]=c;
    }
    int find(int x){
        int pos=lower_bound(num+1,num+1+top,x)-num;
        return num[pos]==x ? cnt[pos] : 0;
    }
    void output(){
        D(top); E;
        for(int i=1;i<=top;i++) D(num[i]), D(cnt[i]), E;
    }
} c,d,e;

int main(){
    freopen("yist.in","r",stdin);
    freopen("yist.out","w",stdout);

    init(1000);
    read(n); m=0;
    for(int i=1;i<=n;i++){ read(a[i]); }
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            b[++m]=a[i]+a[j];
        }
    }
    sort(a+1,a+1+n); sort(b+1,b+1+m);

    for(int i=1;i<=n;i++){
        int cnt=1;
        while(a[i]==a[i+1]) i++,cnt++;
        c.push(a[i],cnt);
    }
    for(int i=1;i<=m;i++){
        if(b[i]>a[n]) break;
        int cnt=1;
        while(b[i]==b[i+1]) i++,cnt++;
        d.push(b[i],cnt);
    }
    for(int i=1;i<=c.top;i++){
        for(int j=i+1;j<=c.top;j++){
            int cnt=C[c.cnt[i]][2]*C[c.cnt[j]][2];
            if(cnt) tp[++tpcnt]=MP(c.num[i]+c.num[j],cnt);
        }
        if(c.cnt[i]>=2) tp[++tpcnt]=MP(c.num[i]*2,C[c.cnt[i]][2]);
    }
    sort(tp+1,tp+1+tpcnt);
    for(int i=1;iint cnt=tp[i].second;
        while(tp[i].first==tp[i+1].first) cnt+=tp[i+1].second,i++;
        e.push(tp[i].first,cnt);
    }

//  c.output(); d.output(); e.output();

    LL ans=0;
    for(int i=1;i<=c.top;i++){
        if(c.cnt[i]>=2){
//          D(c.num[i]);
            int p1=C[c.cnt[i]][2];
            int p2=e.find(c.num[i]);
            ans+=p1*p2;
//          D(ans); E;
        }
    }
//  D(ans); E;
    LL tp=0;
    for(int i=1;i<=c.top;i++){
        if(c.cnt[i]>=3){
            for(int j=1;jint p1=C[c.cnt[i]][3];
                int p2=c.cnt[j];
                int p3=d.find(c.num[i]-c.num[j])-c.find(c.num[i]-c.num[j]*2);
//              D(c.num[i]); D(c.num[j]); D(p1); D(p2); D(p3); E;
                tp+=p1*p2*p3;
            }
        }
    }
    ans+=tp/3;
    printf("%d\n",ans);
}

第二题:ernd

题目大意:
一棵有根树,定义一个节点u的k-子树为u子树中距离u不超过k的部分。(当没有距离为k的节点时,不存在k-子树)
定义两棵树是相同的,当且仅当 考虑节点的标号时,它们的形态相同。给定一棵有根树,问最大的k,使得存在两棵相同的k-子树。

数据规模:
40% n<=2000
100% n<=10000

题解:
40分:
二分k,hash每个点k-子树的括号序,用hash比较即可。

100分:
预处理整棵树括号序的hash,构造前缀和,这样我们可以快速得到任意一段的hash。
二分k,我们已经知道了任意点u整个子树的hash,如果能把距离为k+1的那些点的hash抠掉就好了。可惜hash不支持中间抠去的操作。。。
把抠去改为合并!每个点一个vector,标记需要抠去的段的左右端点,我们只需要把没有标记的区间的hash依次取出来连起来即可。
dfs一下,当前在u,则把u及其子树对应的区间放进u的k级父亲的vector里面。当返回到u时,计算hash即可。
复杂度分析:每个点代表的区间只会被扔进一个点的vector,因此要抠去的区间个数是O(n)的,因此合并的个数也是O(n)的。整个算法复杂度O(nlogn)

Code:

//NULL

你可能感兴趣的:(<,模拟赛,>)