1. 程式人生 > >2018.12.26 考試(雜湊,二分,狀壓dp)

2018.12.26 考試(雜湊,二分,狀壓dp)

T1

傳送門

解題思路

  發現有一個限制是每個字母都必須相等,那麼就可以轉化成首尾的差值相等,然後就可以求出\(k-1\)位的差值\(hash\)一下。\(k\)為字符集大小,時間複雜度為\(O(nk)\)

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>

using namespace std;
typedef unsigned long long ull;
typedef long long LL;
const int MAXN = 100005;
const int mod = 131;
const ull base = 666623333;
const int MOD = 1e9+7;

int n,cnt,sum[MAXN][60];
char s[MAXN];
LL ans;
ull hsh[MAXN];
map<char,int> mp;
map<ull,int> SUM;

int main(){
    scanf("%d%s",&n,s+1);
    for(int i=1;i<=n;i++){
        if(!mp.count(s[i])) mp[s[i]]=++cnt;
        sum[i][mp[s[i]]]=1;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=cnt;j++)
            sum[i][j]+=sum[i-1][j];
    //for(int i=1;i<=n;i++)
        //for(int j=1;j<=cnt;j++)
            //printf("sum[%d][%d]=%d\n",i,j,sum[i][j]);
    for(int i=1;i<=n;i++){
        ull now=1;
        for(int j=2;j<=cnt;j++)
            now=now*base+(ull)(sum[i][j]-sum[i][j-1]+mod);
        hsh[i]=now;
        SUM[now]=SUM[now]+1;        
    }
    hsh[0]=1;
    for(int j=1;j<cnt;j++)
        hsh[0]=hsh[0]*base+mod;
    SUM[hsh[0]]=SUM[hsh[0]]+1;
    for(int i=0;i<=n;i++){
        SUM[hsh[i]]=SUM[hsh[i]]-1;
        ans=(ans+SUM[hsh[i]])%MOD;
    }
    printf("%lld\n",ans);
    return 0;
}

T2

傳送門

解題思路

  二分一個時間,使得一對粒子的距離之和\(<=L\),然後重複\(k\)次,時間複雜度\(O(nklog(MAX)\)

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 50005;
const double eps = 1e-6;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) f=ch=='-'?0:1,ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return f?x:-x;
}

void out(int x){
    if(!x) return ;out(x/10);putchar('0'+x%10);
}

int n,L,k,Maxx[3],Maxy[3],ans[105][2];
bool visx[MAXN],visy[MAXN];
double disx[3],disy[3],now;
struct Data{
    int v,t;
}a[MAXN],b[MAXN];

inline bool check(int tmp,double lim){
    Maxx[1]=Maxx[2]=Maxy[1]=Maxy[2]=disx[1]=disx[2]=disy[1]=disy[2]=0;
    for(register int i=1;i<=n;i++){
        if(!visx[i] && a[i].t<=lim) {
            if((double)(lim-a[i].t)*a[i].v>disx[1]) {
                Maxx[2]=Maxx[1];disx[2]=disx[1];
                disx[1]=(double)(lim-a[i].t)*a[i].v;Maxx[1]=i;
            }
            else if((double)(lim-a[i].t)*a[i].v>disx[2]) Maxx[2]=i,disx[2]=(double)(lim-a[i].t)*a[i].v;
        }
        if(!visy[i] && b[i].t<=lim){
            if((double)(lim-b[i].t)*b[i].v>disy[1]){
                Maxy[2]=Maxy[1];disy[2]=disy[1];
                disy[1]=(double)(lim-b[i].t)*b[i].v;Maxy[1]=i;
            }
            else if(((double)(lim-b[i].t)*b[i].v>disy[2])) Maxy[2]=i,disy[2]=(double)(lim-b[i].t)*b[i].v;
        }
        if(disx[2]+disy[2]>=L) return 0;
    }
    if(!Maxx[1] || !Maxy[1]) return 1;
    if(disx[1]+disy[1]<L) return 1;
    if(disx[2]+disy[2]>=L) return 0;
    ans[tmp][0]=Maxx[1];ans[tmp][1]=Maxy[1];now=lim;
    return 0;
}

int main(){
    n=rd(),L=rd(),k=rd();double l,r,mid;
    for(register int i=1;i<=n;i++)
        a[i].t=rd(),a[i].v=rd();
    for(register int i=1;i<=n;i++)
        b[i].t=rd(),b[i].v=rd();
    for(register int i=1;i<=k;i++){
        l=now;r=1e9+2e8;
        while(r-l>=eps){
            mid=(l+r)/2;
            if(check(i,mid)) l=mid+eps;
            else r=mid-eps;
        }
        visx[ans[i][0]]=1;visy[ans[i][1]]=1;
        out(ans[i][0]),putchar(' '),out(ans[i][1]),putchar('\n');
    }
    return 0;
}

T3

傳送門

解題思路

  狀壓\(dp\),考場上腦抽了寫了個\(2^{3^{質因數}}\)級別的垃圾做法。。正解十分玄學,前\(6\)位三進位制狀壓表示每個質因數出現的次數,後\(21\)為二進位制狀壓表示哪兩個質因數不能同時出現,然後用\(map\)來存狀態。

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<map>
#define int long long

using namespace std;
typedef long long LL;
const int MOD = 1e9+7;
const int poww[8] = {1,3,9,27,81,243,729,2187};

LL n;
int ans,a[55],cnt,num[55],ban[55];
map<int,int> mp;
map<int,int>::iterator it;

inline void init(){
    int x=n;
    for(LL i=2;i*i<=x;i++){
        if(x%i) continue;
        a[cnt]=i;
        while(!(x%i)) x/=i,num[cnt]++;
        cnt++;
    }
    if(x!=1) a[cnt]=x,num[cnt++]=1;
}

int get_new(int S,int now){
    int ret=S,bit=1;
    for(int i=0;i<cnt;i++){
        if((now&(1<<i)) && S/bit%3<2) ret+=bit;
        bit*=3;
    }
    for(int i=0;i<cnt;i++)
        for(int j=i+1;j<cnt;j++){
            if((now&(1<<i)) && ((S/poww[j])%3>0) && (!((S/bit)&1))) ret+=bit;
            else if((now&(1<<j)) && ((S/poww[i])%3>0) && (!((S/bit)&1))) ret+=bit;
            bit<<=1;
        }
    return ret;
}

inline int get_num(int S){
    int ret=1;
    for(int i=0;i<cnt;i++)
        if(S&(1<<i)) ret*=num[i],ret%=MOD;
    return ret;
}

signed main(){
    scanf("%lld",&n);int kk=0;
    init();it=mp.insert(make_pair(0,1)).first;
    while(it!=mp.end()){kk++;
        ans+=it->second;ans%=MOD;
        int tmp=it->first,now=0,tot=0,flag,k;
        for(int i=0;i<cnt;i++){
            if(tmp%3<2) now|=(1<<i);
            tmp/=3;
        }
        for(int i=0;i<cnt;i++)
            for(int j=i+1;j<cnt;j++){
                if(tmp&1) ban[++tot]=(1<<i)|(1<<j);
                tmp>>=1;
            }
        for(int i=now;i;i=(i-1)&now){
            flag=false;
            for(int j=1;j<=tot;j++) 
                if((ban[j]|i)==i) {flag=true;break;}
            if(flag) continue;
            k=get_new(it->first,i);
            if(mp.find(k)==mp.end()) mp.insert(make_pair(k,0));
            mp[k]+=it->second*get_num(i)%MOD;mp[k]%=MOD;
        }
        it++;
    }
    printf("%lld\n",(ans-1+MOD)%MOD);
    return 0;
}