【2018ICPC南京网络赛部分题解】E. AC Challenge G. Lpl and Energy-saving Lamps I. Skr

E. AC Challenge
题意:给你n(n<=20)道题,要做第n题之前必须要做si个题, T 1 , T 2 . . . T S i 每分钟只能提交一个题,每次提交第i题能得到的分是 n u m a i + b i ,num表示这个题是第几个提交的。
做法:这种没办法考虑时间先后的问题,而且n很小,我们就考虑状压DP,状态数从小到大转移,每个状态都与每个题进行判断去除这个题是否满足该题的可提交条件,满足则进行转移,这种没办法考虑时间先后的就要用状态数从小到大转移。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn  = 3e6+10;
ll sta[maxn];
ll dp[maxn];
ll num[maxn];
ll a[maxn];
ll b[maxn];
int main()
{
    memset(dp,-1,sizeof(dp));
    for(int i=0;i<(1<<20);i++)
    {
        for(int j=0;j<20;j++)
        {
            if(i&(1<<j)) num[i]++;
        }
    }
    int n;
    ll ans=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        ll x,tt;
        scanf("%lld%lld%lld",&a[i],&b[i],&x);
        while(x--)
        {
            scanf("%lld",&tt);
            tt--;
            sta[i]|=(1<<tt);
        }
    }
    dp[0]=0;
    for(int i=1;i<(1<<n);i++)
    {
        for(int j=0;j<n;j++)
        {
            if((i&(1<<j))!=0)
            {
                ll tmp=(i^(1<<j));
                if(dp[tmp]!=-1&&((tmp&sta[j])==sta[j]))
                {
                    dp[i]=max(dp[i],dp[tmp]+1LL*num[i]*a[j]+b[j]);
                }
            }
        }
        ans=max(ans,dp[i]);
    }
    printf("%lld\n",ans);
    return 0;
}

G题.Lpl and Energy-saving Lamps
题意:给你N个房间,每个房间有ki个旧灯泡,你每个月月初买M个新灯泡,然后每个月买完灯泡你就要按照1-N的顺序遍历房间,看看能不能把某房间里的旧灯泡全部换成新灯泡如果能完全替换就替换,否则就跳到下一个房间,再给你Q个查询,查询第qi个月底有多少个房间被完全替换以及还剩下几个灯泡,
做法:很明显我们最多替换n个房间的灯泡,也就是说如果建一颗线段树最多修改n次,那我们怎么让不需要修改的时候不修改呢,我们保留一个区间最小值,当区间最小值小于当前灯泡数,一定是不需要修改的,而且我们可以判断如果当前左子区间有存在小于当前灯泡数的房间,那么就直接进入左子区间,否则再进入右子区间,这样就保证了优先更新前面的房间。

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<map>
#include<bitset>
#include<stack>
#include<set>
#include<vector>
#include <time.h>
#include<string.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;
typedef pair <ll, int> pli;
typedef pair <db, db> pdd;

const int maxn = 1e5+5;
const int Mod=1000000007;
const int INF = 0x3f3f3f3f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const double e=exp(1);
const db PI = acos(-1);
const db ERR = 1e-10;

#define Se second
#define Fi first
#define pb push_back
#define mk make_pair
#define maxn 100100
int tmp;
int a[maxn];
int minn[maxn*4];
void Build(int i,int l,int r)
{
    if(l==r)    minn[i]=a[l];
    else
    {
        Build(i<<1,l,(l+r)/2);
        Build(i<<1|1,(l+r)/2+1,r);
        minn[i]=min(minn[i<<1],minn[i<<1|1]);
    }
}
void update(int i,int l,int r)
{
    if(l==r)
    {
        tmp-=minn[i];
        minn[i]=INF;
        return ;
    }
    else
    {
       int mid=(l+r)>>1;
       if(minn[i<<1]<=tmp)  update(i<<1,l,mid);
       else update(i<<1|1,mid+1,r);
       minn[i]=min(minn[i<<1],minn[i<<1|1]);
    }
}
int q[maxn];
int ans1[maxn],ans2[maxn];
int main()
{

    int Q,n,m,Max=0,ans=0;
    tmp=0;
    scanf("%d%d",&n,&m);
    for(int i=0;i<=n*4;i++) minn[i]=INF;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    Build(1,1,n);
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++)
    {
        scanf("%d",&q[i]);
        Max=max(Max,q[i]);
    }
    for(int i=1;i<=Max;i++)
    {
        if(ans==n)
        {
            ans1[i]=ans;
            ans2[i]=tmp;
            continue;
        }
        tmp+=m;
        while(minn[1]<=tmp)
        {
            ans++;
            update(1,1,n);
        }
        ans1[i]=ans;
        ans2[i]=tmp;
    }
    for(int i=1;i<=Q;i++)  printf("%d %d\n",ans1[q[i]],ans2[q[i]]);
    return 0;
}

I.skr
题意:求一个字符串所有本质不同的回文子串的贡献和,每个符合条件的子串的贡献就是该子串代表的数字值。
该问题可以转化为两个子问题来考虑,第一个子问题就是找出所有本质不同的回文子串,第二个问题就是计算该子串对答案的贡献。
我们首先来考虑第一个问题,我们首先要解决的是如何找出所有的回文子串,如果以某个点为中心去搜就太慢了,我们发现manacher算法寻找最长回文子串的过程中其实已经找了每个可能出现过的回文子串,我们怎么o(1)的判断是否该子串已经出现过呢,当然就是判重神器hash表,(听大佬说pdbs的hash_table也能过,没用过这个库,暂且咕咕),我们首先将整个串hash一下,然后对每个找到的回文子串的hash值丢到hash表中,如果当前hash值没有出现过,我们就要计算这个子串的贡献,我们怎么计算贡献呢,就先把整个串逆置过来hash一下,
比如
12324 h a s h 42321
232
1232 1000 232
便 O ( 1 )
于是这道题就结束了,要注意的地方就是判重一定要用hash表,相减操作可能比mod小很多的时候要对被减得先模mod

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<queue>
#include<map>
#include<bitset>
#include<stack>
#include<set>
#include<vector>
#include <time.h>
#include<string.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;
typedef pair <ll, int> pli;
typedef pair <db, db> pdd;

const int maxn = 2000000+10;
const int MOD = 2000007;
const int Mod = 1000000007;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double e=exp(1);
const db PI = acos(-1);
const db ERR = 1e-10;
const int seed = 13131;

#define Se second
#define Fi first
#define pb push_back
#define mk make_pair

ull P = 13331;
ull sqr[maxn],Hash_[maxn];
char str[maxn];
ull sumHash[maxn];
struct StringHash
{
    int first[MOD+2],num;
    unsigned long long EdgeNum[maxn];
    int next[maxn],close[maxn];
    void init ()
    {
        num = 0;
        memset (first,0,sizeof first);
        return ;
    }
    int insert (unsigned long long val,int id)
    {
        int u = val % MOD;
        for (int i = first[u]; i ; i = next[i])
        {
            if (val == EdgeNum[i])
            {
                int t = close[i];
                close[i] = id;
                return t;
            }
        }
        ++num;
        EdgeNum[num] = val;
        close[num] = id;
        next[num] = first[u];
        first[u] = num;
        return 0;
    }
} H;
int r[maxn];
long long sum[maxn];
long long ans=0;
long long xp[maxn];
void init()
{
    xp[0]=1;
    for(int i=1;i<maxn;i++)
        xp[i]=(xp[i-1]*10)%Mod;
    return ;
}
void make_hash(char str[])//处理出str的hash值
{

    int len=strlen(str+1);
    sum[len+1]=0;
    for(int i=len;i>=1;i--)
    {
        sum[i]=(sum[i+1]*10+str[i]-'0')%Mod;
    }
    return ;
}
ll Get_hash(int i,int L)
{
    return (sum[i]%Mod-sum[i+L]*xp[L]%Mod+Mod)%Mod;//注意这里要对被减的先取模
}
void insert_(int i,int j)
{
    ull now=Hash_[j]-Hash_[i-1]*sqr[j-i+1];
    if(!H.insert(now,1))//先判重,再插入
    {
        ans=(ans+Get_hash(i,j-i+1))%Mod;
    }
}
void solve(int n)
{
    sqr[0]=1;
    for(int i=1;i<=n;++i)
    {
        sqr[i]=sqr[i-1]*P;
        Hash_[i]=Hash_[i-1]*P+str[i];
    }
}
ll manacher(int n)
{
    int x=0,pos=0;
    for(int i=1;i<=n;i++)
    {
        int j=0;
        insert_(i,i);
        if(pos>i) j=min(r[2*x-i],pos-i);
        while(i+j+1<=n&&str[i+j+1]==str[i-j-1])
        {
            insert_(i-j-1,i+j+1);
            j++;
        }
        r[i]=j;
        if(i+j>pos)
        {
            pos=i+j;
            x=i;
        }
    }
    x=0,pos=0;
    for(int i=2;i<=n;i++)
    {
        int j=0;
        if(pos>i)j=min(r[2*x-i],pos-i+1);
        while(i+j<=n&&str[i+j]==str[i-j-1])
        {
            insert_(i-j-1,i+j);
            ++j;
        }
        r[i]=j;
        if(i+j-1>pos)
        {
            pos=i+j-1;
            x=i;
        }
    }
}
int main()
{
    scanf("%s",str+1);
    int n=strlen(str+1);
    init();//hash每一位的贡献预处理
    make_hash(str);//hash贡献值预处理
    solve(n);//hash表预处理
    manacher(n);//manacher
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38891827/article/details/82318694