In mathematics you don't understand things, you just get used to them.

分类 笔记 下的文章

Part. 1 Preface

没什么 preface。

Part. 2 实现

具体来说就是把所有关键点按 $\text{dfn}$ 排序,去重,然后求出相邻结点的 $\text{LCA}$,然后加入关键点,去重;然后把关键点和加入的 $\text{LCA}$ 一起按 $\text{dfn}$ 排序,最后把两两之间的 $\text{LCA}$ 拿出来当爸爸然后把右边当儿子。

草说不清楚自己看代码,比传统维护右链代码不知道短到哪里去了。

struct VirtualTree
{
    vector<int> e[1000010]; // 连出来的虚树
    vector<int> build(vector<int> poi) // poi:关键点
    {
        sort(poi.begin(),poi.end(),cmp);
        poi.erase(unique(poi.begin(),poi.end()),poi.end());
        int len=poi.size();
        for(int i=0;i<len-1;++i)    poi.push_back(LCA(poi[i],poi[i+1]));
        sort(poi.begin(),poi.end(),cmp);
        poi.erase(unique(poi.begin(),poi.end()),poi.end());
        len=poi.size();
        for(int i=1;i<len;++i)    e[LCA(poi[i-1],poi[i])].push_back(poi[i]);
        return poi;
    }
}VRT;

「ABC 193A」Discount

Link.

略。

#include<cstdio>
int main()
{
    int a,b;
    scanf("%d %d",&a,&b);
    printf("%.2f\n",(1.0-(double)b/(double)a)*100.0);
    return 0;
}

「ABC 193B」Play Snuke

Link.

排序。

#include<cstdio>
#include<algorithm>
using namespace std;
struct node
{
    int tim,pay,ps;
}nodes[100010];
bool cmp(node one,node ano)
{
    return one.pay<ano.pay;
}
int ans=-1;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)    scanf("%d %d %d",&nodes[i].tim,&nodes[i].pay,&nodes[i].ps);
    sort(nodes+1,nodes+n+1,cmp);
    for(int i=1;i<=n;++i)
    {
        if(((int)((double)nodes[i].tim-0.5)+1)<nodes[i].ps)
        {
            ans=nodes[i].pay;
            break;
        }
    }
    printf("%d\n",ans);
    return 0;
}

「ABC 193C」Unexpressed

Link.

可以容斥,也可以暴力枚举 std::set 去重。

#include<set>
#include<iostream>
#define int long long
using namespace std;
set<int> app;
signed main()
{
    int n,ians=0;
    cin>>n;
    for(int i=2;i*i<=n;++i)
    {
        for(int j=i*i;j<=n;j*=i)
        {
            if(!app.count(j))
            {
                ++ians;
                app.insert(j);
            }
        }
    }
    cout<<n-ians;
    return 0;
}

「ABC 193D」Poker

Link.

开个答案+单位贡献的结构体,然后模拟。

#include<iostream>
#include<iomanip>
using namespace std;
#define int long long
#define ID(c) ((c)-'0')
int k,num[20];
char s[20],t[20];
struct node
{
    int iths[20],sum,c[20];
    int spow(int fur)
    {
        int res=1;
        for(int i=1;i<=fur;++i)    res*=10;
        return res;
    }
    void getscor(char x[])
    {
        for(int i=1;i<=4;++i)    ++c[ID(x[i])];
        for(int i=1;i<=9;++i)
        {
            iths[i]=i*spow(c[i]);
            sum+=iths[i];
        }
    }
    void Op(int pos,int delta)
    {
        c[pos]+=delta;
        sum-=iths[pos];
        if(~delta)    iths[pos]*=10;
        else    iths[pos]/=10;
        sum+=iths[pos];
    }
}tak,aok;
signed main()
{
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
    cin>>k>>(s+1)>>(t+1);
    for(int i=1;i<=9;++i)    num[i]=k;
    for(int i=1;i<=4;++i)    --num[ID(s[i])],--num[ID(t[i])];
    tak.getscor(s);
    aok.getscor(t);
    int winans=0,allans=0;
    for(int i=1;i<=9;++i)
    {
        for(int j=1;j<=9;++j)
        {
            tak.Op(i,1);
            aok.Op(j,1);
            if(tak.sum>aok.sum)
            {
                if(i^j)    winans+=num[i]*num[j];
                else    winans+=num[i]*(num[j]-1);
            }
            tak.Op(i,-1);
            aok.Op(j,-1);
        }
    }
    for(int i=1;i<=9;++i)
    {
        for(int j=1;j<=9;++j)
        {
            if(i^j)    allans+=num[i]*num[j];
            else    allans+=num[i]*(num[j]-1);
        }
    }
    cout<<fixed<<setprecision(6)<<(double)winans/(double)allans<<endl;
    return 0;
}

「ABC 193E」Oversleeping

Link.

开始想到计算几何去了,打了很久。

实际上因为 $500$ 的范围,你可以直接枚举余数然后 exCRT 合并。

#include<cstdio>
#include<climits>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL INF=LONG_LONG_MAX;
namespace NumbTheo
{
    LL ftimes(LL bas,LL fur,LL mod)
    {
        LL res=0;
        while(fur)
        {
            if(fur&1)    res=(res+bas)%mod;
            bas=(bas+bas)%mod,fur>>=1;
        }
        return res;
    }
    LL exGCD(LL one,LL ano,LL &x,LL &y)
    {
        if(ano==0)    return (x=1,y=0,one);
        else
        {
            LL tmp=exGCD(ano,one%ano,y,x);
            y-=(one/ano)*x;
            return tmp;
        }
    }
    LL exCRT(LL n,LL one[],LL ano[])
    {
        LL x,y,res=one[1],M=ano[1];
        for(int i=2;i<=n;++i)
        {
            LL a=M,b=ano[i],c=(one[i]-res%b+b)%b,tmp=exGCD(a,b,x,y),d=b/tmp;
            if(c%tmp)    return -1;
            x=ftimes(x,c/tmp,d);
            res+=x*M,M*=d,res=(res%M+M)%M;
        }
        return (res%M+M)%M;
    }
};
int t;
LL one[10],ano[10],ans;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        LL X,Y,P,Q;
        scanf("%lld %lld %lld %lld",&X,&Y,&P,&Q);
        ans=INF;
        for(LL i=X;i<X+Y;++i)
        {
            for(int j=P;j<P+Q;++j)
            {
                using namespace NumbTheo;
                one[1]=i,ano[1]=((X+Y)<<1),one[2]=j,ano[2]=P+Q;
                LL tmp=exCRT(2,one,ano);
                if(~tmp)    ans=min(ans,tmp);
            }
        }
        if(ans==INF)    printf("infinity\n");
        else    printf("%lld\n",ans);
    }
    return 0;
}

「ABC 193F」Zebraness

Link.

把 $i+j$ 为奇数的地方反转一下,然后连边求最小割。

#include<queue>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
#define getID(x,y) (((x)-1)*n+(y))
namespace MaxFlow
{
    queue<int> q;
    const LL INF=1e9;
    int S,T,head[10010],Cur[10010],cntot=1,all,vis[10010];
    LL dep[10010];
    struct Edge
    {
        int to,nxt;
        LL c;
        Edge(int A=0,int B=0,LL C=0){to=A,nxt=B,c=C;}
    }as[500010];
    bool bfs()
    {
        for(int i=1;i<=all;++i)    vis[i]=0,dep[i]=INF;
        q.push(S),dep[S]=0,vis[S]=1;
        while(!q.empty())
        {
            int now=q.front();
            q.pop(),vis[now]=0;
            for(int i=head[now];i;i=as[i].nxt)
            {
                int y=as[i].to;
                LL c=as[i].c;
                if(c&&dep[y]>dep[now]+1)
                {
                    dep[y]=dep[now]+1;
                    if(!vis[y])    q.push(y),vis[y]=1;
                }
            }
        }
        return dep[T]<INF;
    }
    LL dfs(int x,LL lav)
    {
        if(x==T)    return lav;
        LL used=0;
        vis[x]=1;
        for(int &i=Cur[x];i;i=as[i].nxt)
        {
            int y=as[i].to;
            LL c=as[i].c;
            if(c&&dep[y]==dep[x]+1&&!vis[y])
            {
                LL tmp=dfs(y,min(lav-used,c));
                as[i].c-=tmp,as[i^1].c+=tmp,used+=tmp;
                if(lav==used)    break;
            }
        }
        if(used<lav)    dep[x]=INF;
        vis[x]=0;
        return used;
    }
    LL Dinic()
    {
        LL res=0;
        while(bfs())
        {
            for(int i=1;i<=all;++i)    vis[i]=0,Cur[i]=head[i];
            res+=dfs(S,INF);
        }
        return res;
    }
}using namespace MaxFlow;
int n;
int rop()
{
    char res=getchar();
    while((res^'B')&&(res^'W')&&(res^'?'))    res=getchar();
    if(res=='?')    return -1;
    else if(res=='B')    return 1;
    else    return 0;
}
void addEdge(int one,int ano,int lav)
{
    as[++cntot]=Edge(ano,head[one],lav);
    head[one]=cntot;
    as[++cntot]=Edge(one,head[ano],0);
    head[ano]=cntot;
}
int main()
{
    scanf("%d",&n),all=n*n;
    S=(++all),T=(++all);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)
        {
            int x=rop();
            if(~x)
            {
                if((i+j)&1)    x^=1;
                if(x)    addEdge(S,getID(i,j),INF);
                else    addEdge(getID(i,j),T,INF);
            }
        }
    }
    for(int i=1;i<n;++i)
    {
        for(int j=1;j<=n;++j)    addEdge(getID(i,j),getID(i+1,j),1),addEdge(getID(i+1,j),getID(i,j),1);
    }
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<n;++j)    addEdge(getID(i,j),getID(i,j+1),1),addEdge(getID(i,j+1),getID(i,j),1);
    }
    printf("%lld\n",2*n*(n-1)-Dinic());
    return 0;
}

Description

Link.

  • 游戏在 $4\times4$ 的菱形棋盘上进行;
  • 两名玩家轮流放置弹珠,可以在横向、纵向、$45$ 度斜线、$135$ 度斜线方向未放置弹珠的位置连续放置 $1$ 至 $3$ 颗弹珠,玩家在可以放置弹珠的情况下,必须至少放置 $1$ 颗弹珠。
  • 如果某位玩家无法再继续放置弹珠,则该名玩家输掉游戏,另外一名玩家获胜。

Solution

虽然是套路,但毕竟是之前没做过的套路,写篇题解记一下。

首先我们可以直接考虑状压,棋盘编号见图:

qw7lsky0.png

然后你打个表出来,表示所有能走的情况(状压),比如我要放棋子在 $1-5-9$ 上面,就是 $(100010001)_{2}$。

因为是用 C++ 输出的形式手打的 $82$ 种情况表,所以 generator 就不附了。

然后你打个 DP,设 $f_{S}$ 为当前棋盘状态为 $S$($S$ 的第 $i$ 为 $1$ 表示这个格子被占据,反之亦然)是先手必胜还是先手必输或者不知道(分别对应数字 $1/0/-1$)。

初始状态为 $\forall i\in[0,2^{n}-1),f_{i}=-1$;$f_{2^{n}-1}=0$。

然后你记搜一下,把所有状态搜出来。

然后就回答询问即可,只是不太清楚为什么要搞这么多字符读入卡 IO,明明多不多组都一样。

#include<bits/stdc++.h>
using namespace std;
int t,n=7,m[8]={1,2,3,4,3,2,1},id,f[(1<<16)+10];
char s[10];
const int upper=(1<<16);
const int ID[10][10]={{0},{4,1},{8,5,2},{12,9,6,3},{13,10,7},{14,11},{15}};
const int walking[90]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,17,3,18,272,48,34,6,288,36,4352,768,544,96,68,12,4608,576,72,12288,8704,1536,1088,192,9216,1152,24576,17408,3072,2176,18432,49152,34816,33,528,66,8448,1056,132,16896,2112,33792,136,273,7,1057,4368,16912,112,546,2114,14,292,1792,8736,224,33824,1092,4672,584,28672,3584,17472,2184,9344,57344,34944};
inline int unionset(int x,int y){return x|y;}
inline int intersection(int x,int y){return x&y;}
inline bool emptyset(int x){return x==0;}
void dfs(int board)
{
    if(~f[board])    return;
    for(int i=0;i<82;++i)
    {
        if(emptyset(intersection(board,walking[i])))
        {
            int newset=unionset(board,walking[i]);
            dfs(newset);
            if(f[newset]==0)
            {
                f[board]=1;
                return;
            }
        }
    }
    f[board]=0;
}
inline char fgc()
{
    static char buf[1<<17],*p=buf,*q=buf;
    return p==q&&(q=buf+fread(p=buf,1,1<<17,stdin),p==q)?EOF:*p++;
}
inline char fgop()
{
    char res=0;
    while((res^'*')&&(res^'.'))    res=fgc();
    return res;
}
inline void read(int &x)
{
    x=0;
    char c=fgc();
    while(isdigit(c)==0)    c=fgc();
    while(isdigit(c))    x=(x<<3)+(x<<1)+(c^'0'),c=fgc();
}
int main()
{
    read(t);
    memset(f,-1,sizeof(f));
    f[upper-1]=0;
    for(int i=0;i^upper;++i)
    {
        if(f[i]==-1)    dfs(i);
    }
    while(t--)
    {
        int board=0;
        for(int i=0;i<n;++i)
        {
            for(int j=0;j<m[i];++j)    board+=(fgop()=='*')?(1<<ID[i][j]):0;
        }
        printf(f[board]?"Possible.":"Impossible.");
        printf("\n");
    }
    return 0;
}

「ARC 113A」A*B*C

Link.

就是算 $\sum_{i=1}^{k}\sum_{j=1}^{\lfloor\frac{k}{i}\rfloor}\lfloor\frac{k}{j\times j}\rfloor$。

直接调和级数。

#include<cstdio>
long long k;
int main()
{
    scanf("%lld",&k);
    long long ans=0;
    for(long long i=1;i<=k;++i)
    {
        for(long long j=1;j<=k/i;++j)    ans+=(k/i/j);
    }
    printf("%lld\n",ans);
    return 0;
}

「ARC 113B」A^B^C

Link.

扩展欧拉定理裸题,$A^{B^{C}}\bmod10=A^{(B^{C}\bmod\varphi(10))+\varphi(10)}\bmod10$。

#include<cstdio>
long long getphi(long long x)
{
    long long res=x;
    for(long long i=2;i*i<=x;++i)
    {
        if(x%i==0)
        {
            res=res/i*(i-1);
            while(x%i==0)    x/=i;
        }
    }
    if(x>1)    res=res/x*(x-1);
    return res;
}
long long cqpow(long long bas,long long fur,long long mod)
{
    long long res=1;
    while(fur)
    {
        if(fur&1)    res=res*bas%mod;
        bas=bas*bas%mod;
        fur>>=1;
    }
    return res;
}
long long a,b,c;
int main()
{
    scanf("%lld %lld %lld",&a,&b,&c);
    printf("%lld\n",cqpow(a,cqpow(b,c,getphi(10))+getphi(10),10));
    return 0;
}

「ARC 113C」String Invasion

Link.

正序枚举 $i\in[1,n]$,如果满足条件,那么后面的字符串都可以执行操作,则 $ans:=ans+n-i$。

当然,由于后面可能存在一个字符就是 $s_{i}$,所以要记录当前操作的字符,特判 $ans:=ans-1$。

#include<cstdio>
#include<cstring>
int n;
long long ans;
char s[200010];
int main()
{
    scanf("%s",s+1);
    n=strlen(s+1);
    char las=0;
    for(int i=1;i<=n;++i)
    {
        if(i!=n&&s[i]==s[i+1]&&s[i]!=s[i+2]&&s[i]!=las)    ans+=n-i,las=s[i];
        else if(s[i]==las)    --ans;
    }
    printf("%lld\n",ans);
    return 0;
}

「ARC 113D」Sky Reflector

Link.

显然只要 $\forall i\in[1,m],b_{i}\ge a_{\max}$ 即可,那么枚举 $i\in[1,k]=a_{\max}$,有:

$$ ans=\sum_{i=1}^{k}(i^{n}-(i-1)^{n})\times(k-i+1)^{m}\bmod998244353 $$

#include<cstdio>
const int mod=998244353;
long long cqpow(long long bas,int fur)
{
    long long res=1;
    while(fur)
    {
        if(fur&1)    res=res*bas%mod;
        bas=bas*bas%mod;
        fur>>=1;
    }
    return res;
}
int n,m,k;
long long ans;
int main()
{
    scanf("%d %d %d",&n,&m,&k);
    if(n==1)    ans=cqpow(k,m);
    else if(m==1)    ans=cqpow(k,n);
    else
    {
        for(int i=1;i<=k;++i)    ans=(ans+((cqpow(i,n)-cqpow(i-1,n)+mod)%mod)*cqpow(k-i+1,m)%mod)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

「ARC 113E」Rvom and Rsrev

Link.

「ARC 113F」Social Distance

Link.

「ABC 113A」Star

Link.

略。

#include<cstdio>
int x;
int main()
{
    scanf("%d",&x);
    for(int i=1;;++i)
    {
        if((x+i)%100==0)
        {
            printf("%d\n",i);
            break;
        }
    }
    return 0;
}

「ABC 192B」uNrEaDaBlE sTrInG

Link.

略。

#include<cstdio>
#include<cstring>
char s[1010];
int main()
{
    scanf("%s",s+1);
    bool flag=1;
    int n=strlen(s+1);
    for(int i=1;i<=n;++i)
    {
        if(i&1)
        {
            if(s[i]<'a'||s[i]>'z')
            {
                flag=0;
                break;
            }
        }
        else
        {
            if(s[i]<'A'||s[i]>'Z')
            {
                flag=0;
                break;
            }
        }
    }
    printf("%s\n",flag?"Yes":"No");
    return 0;
}

「ABC 192C」Kaprekar Number

Link.

照题意模拟。

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
long long las,now,n;
int k;
long long f(long long x)
{
    long long one=0,ano=0;
    vector<long long> num;
    while(x>0)
    {
        num.push_back(x%10);
        x/=10;
    }
    sort(num.begin(),num.end(),greater<long long>());
    for(auto val:num)    one=one*10+val;
    reverse(num.begin(),num.end());
    for(auto val:num)    ano=ano*10+val;
//    printf("%lld %lld\n",one,ano);
    return one-ano;
}
int main()
{
    scanf("%lld %d",&n,&k);
    las=n;
    if(k==0)    return printf("%lld\n",n)&0;
    while(k--)
    {
        now=f(las);
        las=now;
    }
    printf("%lld\n",now);
    return 0;
}

「ABC 192D」Base n

Link.

显然随着进制增大数字也会增大,所以可以二分。

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
struct bigInt : vector<long long>{
    bigInt &check( ){
        while( ! empty( ) && ! back( ) ) pop_back( );
        if( empty( ) )    return *this;
        for( unsigned i = 1; i < size( ); ++ i ){ ( *this )[i] += ( *this )[i - 1] / 10; ( *this )[i - 1] %= 10; }
        while( back( ) >= 10 ){ push_back( back( ) / 10 ); ( *this )[size( ) - 2] %= 10; }
        return *this;
    }
    bigInt( long long tpN = 0 ){ push_back( tpN ); check( ); }
};
istream &operator >> ( istream &is, bigInt &tpN ){
    string s;
    is >> s; tpN.clear( );
    for( int i = s.size( ) - 1; i >= 0; --i ) tpN.push_back( s[i] - '0' );
    return is;
}
ostream &operator << ( ostream &os, const bigInt &tpN ){
    if( tpN.empty( ) )    os << 0;
    for( int i = tpN.size( ) - 1; i >= 0; --i )    os << tpN[i];
    return os;
}
bool operator != ( const bigInt &one, const bigInt &another ){
    if( one.size( ) != another.size( ) )    return 1;
    for( int i = one.size( ) - 1; i >= 0; --i ){
        if( one[i] != another[i] )    return 1;
    }
    return 0;
}
bool operator == ( const bigInt &one, const bigInt &another ){
    return ! ( one != another );
}
bool operator < ( const bigInt &one, const bigInt &another ){
    if( one.size( ) != another.size( ) )    return one.size( ) < another.size( );
    for( int i = one.size( ) - 1; i >= 0; --i ){
        if( one[i] != another[i] )    return one[i] < another[i];
    }
    return 0;
}
bool operator > ( const bigInt &one, const bigInt &another ){ return another < one; }
bool operator <= ( const bigInt &one, const bigInt &another ){ return ! (one > another ); }
bool operator >= ( const bigInt &one, const bigInt &another ){ return ! (one < another ); }
bigInt &operator += ( bigInt &one, const bigInt &another ){
    if( one.size( ) < another.size( ) )    one.resize(another.size( ) );
    for( unsigned i = 0; i != another.size( ); ++ i ) one[i] += another[i];
    return one.check( );
}
bigInt operator + ( bigInt one, const bigInt &another ){ return one += another; }
bigInt &operator -= ( bigInt &one, bigInt another ){
    if( one < another )    swap( one, another );
    for( unsigned i = 0; i != another.size( ); one[i] -= another[i], ++ i ){
        if( one[i] < another[i] ){
            unsigned j = i + 1;
            while( ! one[j] ) ++ j;
            while( j > i ){ -- one[j]; one[--j] += 10; }
        }
    }
    return one.check( );
}
bigInt operator - ( bigInt one, const bigInt &another ){ return one -= another; }
bigInt operator * ( const bigInt &one, const bigInt &another ){
    bigInt tpN;
    tpN.assign( one.size( ) + another.size( ) - 1, 0 );
    for( unsigned i = 0; i != one.size( ); ++ i ){
        for( unsigned j = 0; j != another.size( ); ++ j ) tpN[i + j] += one[i] * another[j];
    }
    return tpN.check( );
}
bigInt &operator *= ( bigInt &one, const bigInt &another ){ return one = one * another; }
bigInt divMod( bigInt &one, const bigInt &another ){
    bigInt ans;
    for( int t = one.size( ) - another.size( ); one >= another; -- t ){
        bigInt tpS;
        tpS.assign( t + 1, 0 );
        tpS.back( ) = 1;
        bigInt tpM = another * tpS;
        while( one >= tpM ){ one -= tpM; ans += tpS; }
    }
    return ans;
}
bigInt operator / ( bigInt one, const bigInt &another ){ return divMod(one, another ); }
bigInt &operator /= ( bigInt &one, const bigInt &another ){ return one = one / another; }
bigInt &operator %= ( bigInt &one, const bigInt &another ){ divMod( one, another ); return one; }
bigInt operator % ( bigInt one, const bigInt &another ){ return one %= another; }
char s[70];
int n,cntot;
bigInt m,num[70],mx;
bool check(bigInt bas)
{
    bigInt res=0,sab=1;
    for(int i=1;i<=cntot;++i)
    {
        res+=num[i]*sab;
        sab*=bas;
        if(res>m)    return false;
    }
    return true;
}
int main()
{
    cin>>(s+1)>>m;
    n=strlen(s+1);
    for(int i=n;i>=1;--i)
    {
        num[++cntot]=s[i]-'0';
        mx=max(mx,num[cntot]);
    }
    if(cntot==1)    cout<<(num[cntot]<=m)<<"\n";
    else
    {
//        bigInt l=0,r=1e18,ans=0;
//        while(l<=r)
//        {
//            bigInt mid=(l+r)/2;
//            if(check(mid))
//            {
//                l=mid+1;
//                ans=mid;
//            }
//            else    r=mid-1;
//        }
//        bigInt l=mx,r=m+1;
//        while(l+1<r)
//        {
//            bigInt mid=(l+r)/2;
//            if(check(mid))    l=mid;
//            else    r=mid;
//        }
        bigInt l=mx+1,r=m+1,ans=mx;
        while(l<=r)
        {
            bigInt mid=(l+r)/2;
            if(check(mid))    l=mid+1,ans=mid;
            else    r=mid-1;
        }
        cout<<ans-mx<<"\n";
    }
    return 0;
}

「ABC 192E」Train

Link.

我也不知道我怎么过的,反正就是 Dijkstra 板子套上去后把 if(dis[y]>dis[x]+z) 改成了 if(dis[y]>get(dis[x],k)+z),其中 get(dis[x],k) 就是算下一班车来的时间加上 dis[x] 本身。

然后就莫名其妙过了,可能算个贪心?

#include<queue>
#include<cstdio>
using namespace std;
const long long INF=1e18;
priority_queue<pair<long long,long long>,vector<pair<long long,long long> >,greater<pair<long long,long long> > > q;
vector<pair<long long,pair<long long,long long> > > e[100010];
long long n,m,st,ed,dis[100010],vis[100010];
long long get(long long val,long long k)
{
    if(val%k==0)    return val;
    else    return val+k-(val%k);
}
void find()
{
    for(long long i=1;i<=n;++i)    dis[i]=INF;
    dis[st]=0;
    q.push(make_pair(dis[st],st));
    while(!q.empty())
    {
        long long now=q.top().second;
        q.pop();
        if(!vis[now])
        {
            vis[now]=1;
            for(long long i=0;i<e[now].size();++i)
            {
                long long y=e[now][i].first,w=e[now][i].second.first,k=e[now][i].second.second;
                if(dis[y]>get(dis[now],k)+w)
                {
                    dis[y]=get(dis[now],k)+w;
                    q.push(make_pair(dis[y],y));
                }
            }
        }
    }
}
int main()
{
    scanf("%lld %lld %lld %lld",&n,&m,&st,&ed);
    for(long long i=1;i<=m;++i)
    {
        long long u,v,w,k;
        scanf("%lld %lld %lld %lld",&u,&v,&w,&k);
        e[u].push_back(make_pair(v,make_pair(w,k)));
        e[v].push_back(make_pair(u,make_pair(w,k)));
    }
    find();
    printf("%lld\n",dis[ed]==INF?-1:dis[ed]);
    return 0;
}

「ABC 192F」Potion

Link.

考虑枚举 $k$,设 $f_{i,j,c}$ 为前 $i$ 位选了 $j$ 个数 balabala。

我也不知道怎么 DP 的,可能是本能做出来的。

后面自己意会吧,反正也没难度了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
long long n,x,a[110],f[110][110][110],ans=1145141919810233454;
int main()
{
    scanf("%lld %lld",&n,&x);
    for(int i=1;i<=n;++i)    scanf("%lld",&a[i]);
    for(int s=1;s<=n;++s)
    {
        memset(f,-0x3f,sizeof(f));
        f[0][0][0]=0;
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=min(s,i);++j)
            {
                for(int k=0;k<n;++k)    f[i][j][k]=max(f[i-1][j][k],f[i-1][j-1][((k-a[i])%s+s)%s]+a[i]);
            }
        }
        if(f[n][s][x%s]>=0)    ans=min(ans,(x-f[n][s][x%s])/s);
    }
    printf("%lld\n",ans);
    return 0;
}