20190810

AGC014 E Blue and Red Tree

link

$solution:$

考虑题目的性质,可以发现蓝树与红树肯定有交,且必在最后一次割掉。

所以考虑反向过程,如何从红树变为蓝树。

将最后一次的情况往外推广,每一次也要有类似的交集,。

考虑如何去维护,通过启发式合并 $set$ 与并查集,时间复杂度 $O(n\log^2 n)$ 。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=200001;
map<pair<int,int> ,int> M;
set<int> s[MAXN];
queue<pair<int,int> > que;
void add(int u,int v){
    s[u].insert(v),s[v].insert(u);
    if(u>v) swap(u,v);
    M[make_pair(u,v)]++;
    if(M[make_pair(u,v)]==2) que.push(make_pair(u,v));
    return; 
}
void del(int u,int v){
    s[u].erase(v),s[v].erase(u);
    if(u>v) swap(u,v);
    M.erase(make_pair(u,v));
}
set<int>::iterator it,it1;
int n,f[MAXN];
int find(int x){if(f[x]==x) return x;return f[x]=find(f[x]);}
bool solve(){
    int tot=0;
    for(int i=1;i<=n;i++) f[i]=i;
    while(!que.empty()){
        int u=que.front().first,v=que.front().second;que.pop();
        tot++;
        u=find(u),v=find(v);
        if(s[u].size()<s[v].size()) swap(u,v);
        del(u,v);
        f[v]=u;
        for(it=s[v].begin();it!=s[v].end();){
            it1=it;it++;
            int end=*it1;end=find(end);
            del(v,end),add(u,end);
        }
    }
    return tot==(n-1);
}
int main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    n=read();
    for(int i=1;i<n;i++) add(read(),read());
    for(int i=1;i<n;i++) add(read(),read());
    if(solve()){printf("YES\n");return 0;}
    printf("NO\n");return 0;
}
View Code

 

[2017集训队作业自选题#119] 众数MAX

$solution:$

众数肯定是要枚举的,考虑暴力求出对于众数 $x$ 的个数。

设 $A_i$ 表示 $a_x=i$ 的出现次数,$B_i$ 亦如此。

则 $Ans_i=\sum min\{A_j,B_{i-j}\}$ 。这个式子很像能卷积的式子,但是无法计算 $min$ 操作。

但是可以将式子拆开去做,则 $$Ans_i=\sum_{x=1}^{n(k)} \sum_{j=0}^i [A_j\geq x]\times [B_{i-j}\geq x]\\=\sum_{x=1}^n\sum_{j=0}^i c_j\times d_{i-j}$$

时间复杂度 $O(k\times n\log n)$ 。

但是此刻 $k$ 与 $n$ 同阶。通过简单思考发现对于 $A$ 中出现 $k$ 次的不超过 $\dfrac{n}{k}$ 个,所以可以通过设置 $k$,$NTT$ 与暴力同时处理两种不同情况。

时间复杂度 $O(k\times n\log n+\dfrac{n^2}{k^2})$ ,在 $k$ 取 $10$ 时应为最优。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
#define mod 998244353
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=600001;
namespace Poly{
    int f[MAXN],g[MAXN],flip[MAXN];
    inline int ksm(int a,int b){
        int ans=1;
        while(b){
            if(b&1) ans*=a,ans%=mod;
            a*=a,a%=mod;
            b>>=1;
        }return ans;
    }
    inline int Get(int lim){
        int len=1;while(len<=lim) len<<=1;
        for(register int i=0;i<len;i++) flip[i]=((flip[i>>1]>>1)|(i&1?len>>1:0));
        return len;
    }
    inline void NTT(int *f,int lim,int opt){
        for(register int i=0;i<lim;i++) if(i<flip[i]) swap(f[i],f[flip[i]]);
        for(register int p=2;p<=lim;p<<=1){
            int len=p>>1,buf=ksm(3,(mod-1)/p);
            if(opt==-1) buf=ksm(buf,mod-2);
            for(register int be=0;be<lim;be+=p){
                int tmp=1;
                for(register int l=be;l<be+len;l++){
                    int t=f[l+len]*tmp;t%=mod;
                    f[l+len]=(f[l]-t+mod)%mod,f[l]=(f[l]+t)%mod;
                    tmp*=buf,tmp%=mod;
                }
            }
        }
        if(opt==-1){
            int Inv=ksm(lim,mod-2);
            for(register int i=0;i<lim;i++) f[i]*=Inv,f[i]%=mod;
        }return;
    }
}
int n,Maxn,a[MAXN],b[MAXN],A[MAXN],B[MAXN],f[MAXN],g[MAXN],ans[MAXN],staa[MAXN],stab[MAXN];
signed main(){
    freopen("max.in","r",stdin);
    freopen("max.out","w",stdout); 
    n=read();
    for(int i=1;i<=n;i++) a[i]=read(),A[a[i]]++;
    for(int i=1;i<=n;i++) b[i]=read(),B[b[i]]++;
    int lim=Poly::Get(200000);
    for(register int i=1;i<=10;i++){
        memset(f,0,sizeof(f)),memset(g,0,sizeof(g));
        for(register int j=1;j<=100000;j++) f[j]=(A[j]>=i),g[j]=(B[j]>=i);
        Poly::NTT(f,lim,1),Poly::NTT(g,lim,1);
        for(register int j=0;j<lim;j++) f[j]*=g[j],f[j]%=mod;
        Poly::NTT(f,lim,-1);
        for(int j=0;j<lim;j++) ans[j]+=f[j];
    }
    for(register int i=1;i<=100000;i++){
        if(A[i]>10) staa[++staa[0]]=i;
        if(B[i]>10) stab[++stab[0]]=i;
    }
    for(register int i=1;i<=staa[0];i++)
        for(int j=1;j<=stab[0];j++) ans[staa[i]+stab[j]]+=min(A[staa[i]],B[stab[j]])-10;
    for(register int i=1;i<=200000;i++) Maxn=max(Maxn,ans[i]);
    printf("%lld\n",Maxn);return 0;
}
View Code

 

 AGC005 D ~K Perm Counting

link

$solution:$

考虑容斥原理,设 $g_i$ 表示至少有 $i$ 个点满足 $|a_j-j|=k$ 的方案数。

则 $Ans=\sum_i (-1)^i\times g_i\times (n-i)!$ 。

考虑建立二分图,左为下标,右为权值,若有边 $(u,v)$ ,则下标为 $u$ 不能选 $v$ 。

将二分图拉出来简单 $dp$ 即可。

时间复杂度 $O(n^2)$ 。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
#define mod 924844033
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int MAXN=4001;
int fac[MAXN],f[MAXN][MAXN][2],n,k,sta[MAXN],col[MAXN];
int Mod(int x){return ((x%mod)+mod)%mod;}
signed main(){
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    n=read(),k=read();fac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=(fac[i-1]*i)%mod;
    for(int i=1;i<=k;i++){
        col[0]++;
        for(int j=i;j<=n;j+=k) sta[++sta[0]]=j,col[sta[0]]=col[0];
        col[0]++;
        for(int j=i;j<=n;j+=k) sta[++sta[0]]=j,col[sta[0]]=col[0]; 
    }
    f[1][0][0]=1;
    for(int i=2;i<=sta[0];i++){
        for(int j=0;j<=n;j++){
            f[i][j][0]=f[i-1][j][0]+f[i-1][j][1];f[i][j][0]=Mod(f[i][j][0]);
            if(j!=0&&col[i]==col[i-1]) f[i][j][1]=f[i-1][j-1][0],f[i][j][1]%=mod;
        }
    }
    int Ans=0,pw=1;
    for(int i=0;i<=n;i++){
        Ans+=pw*fac[n-i]*((f[sta[0]][i][0]+f[sta[0]][i][1])%mod),Ans=Mod(Ans),pw*=-1;
    }
    printf("%lld\n",Ans);return 0;
}
View Code

 

posted @ 2019-08-10 18:25  siruiyang_sry  阅读(196)  评论(0编辑  收藏  举报