Vigenère密码

Link
模拟。

#include <iostream>
using namespace std;
int main()
{
    string k,c;
    cin>>k>>c;
    for (int i=0;i<c.length();i++) {
        int t=(k[i%k.length()]&31)-1;
        c[i]=(c[i]&31)-t>0?c[i]-t:c[i]-t+26;
    }
    cout<<c<<endl;
    return 0;
}

国王游戏

Link
\(a_ib_i\)升序排序,证明采用邻项交换法即可。
垃圾出题人要我写高精。

#include<bits/stdc++.h>
using namespace std;
struct BIGNUM
{
	int a[100001];
	BIGNUM()
	{
		memset(a,0,100001),a[0]=1;
	}
	BIGNUM operator=(string s)
	{
		a[0]=s.length();
		for(int i=1;i<=a[0];i++)
		    a[i]=s[a[0]-i]-'0';
		return *this;
	}
	BIGNUM operator=(int n)
	{
		char x[100001];
		string s;
		sprintf(x,"%d",n);
		s=x;
		*this=s;
		return *this;
	}
	BIGNUM (int n)
	{
		*this=n;
	}
	BIGNUM (string s)
	{
		*this=s;
	}
	BIGNUM operator*(BIGNUM b)
	{
		BIGNUM c;
		for(int i=1;i<=a[0];i++)
			for(int j=1;j<=b.a[0];j++)
				c.a[i+j-1]+=a[i]*b.a[j],c.a[i+j]+=c.a[i+j-1]/10,c.a[i+j-1]%=10;
		c.a[0]=a[0]+b.a[0]-1;
		if(c.a[c.a[0]+1])
			c.a[0]++;
		return c;
	}
	BIGNUM operator/(int x)
	{
        BIGNUM b;
        b.a[0]=a[0];
        int tmp=0;
        for (int i=a[0];i;i--) 
		{
            tmp=tmp*10+a[i];
            b.a[i]=tmp/x;
            tmp%=x;
        }
        while(!b.a[b.a[0]]&&b.a[0]>1)
			b.a[0]--; 
        return b;
    }
    bool operator<(BIGNUM b)
	{
		if(a[0]!=b.a[0])
			return a[0]<b.a[0];
		for(int i=a[0];i;i--)
			if(a[i]!=b.a[i])
				return a[i]<b.a[i];
		return false;
	}
};
ostream& operator <<(ostream &out,BIGNUM &x)
{
    for(int i=x.a[0];i;i--)
        cout<<x.a[i];
    return out;
}
struct Lovelive
{
	int x,y;
	long long z;
}a[100001];
bool cmp(Lovelive a,Lovelive b)
{
	return a.z<b.z;
}
int n;
int main()
{
	scanf("%d",&n);
	for(int i=0;i<=n;i++)
		scanf("%d%d",&a[i].x,&a[i].y),a[i].z=a[i].x*a[i].y;
	sort(a+1,a+n+1,cmp);
	BIGNUM ans=1,tmp,k=1;
	for(int i=1;i<=n;i++)
	{
		k=k*a[i-1].x;
		tmp=k/a[i].y;
		ans=ans<tmp? tmp:ans;
	}
	cout<<ans;
	return 0;
}

开车旅行

先处理出在每个城市\(A,B\)下一次会去哪儿,然后倍增维护即可。

#include<set>
#include<cmath>
#include<cctype>
#include<cstdio>
using i64=long long;
const int N=100007;
int n,m,h[N],f[N][17],mn1[N],mn2[N];
i64 A[N][17],B[N][17],s1,s2;
struct cmp{int operator()(const int&i,const int&j){return h[i]<h[j];}};
std::set<int,cmp>s;
int read(){int x=0,c=getchar(),f=1;while(isspace(c))c=getchar();if(c=='-')f=-1,c=getchar();while(isdigit(c))(x*=10)+=c&15,c=getchar();return f*x;}
int check(int i,int j,int k){return abs(h[i]-h[k])>abs(h[i]-h[j])||(abs(h[i]-h[k])==abs(h[i]-h[j])&&h[j]<h[k]);}
void check(int i,int j)
{
    if(!mn1[i]||check(i,j,mn1[i])) mn2[i]=mn1[i],mn1[i]=j;
    else if(!mn2[i]||check(i,j,mn2[i])) mn2[i]=j;
}
void go(int i,int x)
{
    for(int j=16;~j;--j) if(f[i][j]&&s1+s2+A[i][j]+B[i][j]<=x) s1+=A[i][j],s2+=B[i][j],i=f[i][j];
    if(mn2[i]&&s1+s2+A[i][0]<=x) s1+=A[i][0];
}
void solve(int x)
{
    int a=1e9,b=0,p=0;
    for(int i=1;i<=n;++i)
    {
	s1=s2=0,go(i,x);
	if(!s2) {if(!b&&h[i]>h[p])p=i;}
	else if(s1*b<s2*a||(s1*b==s2*a&&h[i]>h[p])) p=i,a=s1,b=s2;
    }
    printf("%d\n",p);   
}
int main()
{
    n=read();
    for(int i=1;i<=n;++i) h[i]=read(),s.insert(i);
    for(int i=1;i<=n;i++)
    {
	auto it=s.lower_bound(i);
	if(it!=s.begin())
	{
	    auto pre=prev(it);check(i,*pre);
	    if(pre!=s.begin()) check(i,*prev(pre));
	}
	if(++it!=s.end()) if(check(i,*it),++it!=s.end()) check(i,*it);
	s.erase(i);
    }
    for(int i=1;i<=n;++i) f[i][0]=mn1[mn2[i]],A[i][0]=abs(h[i]-h[mn2[i]]),B[i][0]=abs(h[mn2[i]]-h[mn1[mn2[i]]]);
    for(int j=1;j<=16;++j) for(int i=1;i<=n;++i) f[i][j]=f[f[i][j-1]][j-1],A[i][j]=A[i][j-1]+A[f[i][j-1]][j-1],B[i][j]=B[i][j-1]+B[f[i][j-1]][j-1];
    solve(read());
    for(int s,x,m=read();m;--m) s=read(),x=read(),s1=s2=0,go(s,x),printf("%lld %lld\n",s1,s2);
}

同余方程

Link
exgcd。
注意exgcd求的是绝对值最小的解。

#include<bits/stdc++.h>
using namespace std;
int a,b,x,y,k;
void exgcd(int a,int b){!b? (x=1,y=0):(exgcd(b,a%b),k=x,x=y,y=k-a/b*y);}
int main(){cin>>a>>b,exgcd(a,b),cout<<(x+b)%b;}

借教室

Link
二分一个答案\(lim\)。把前\(lim\)个操作全部加上,然后看看有没有哪个时间超了。
需要实现区间加,可以先转差分最后还原。

#include<cstdio>
#include<cstring>
const int N=1000001;
int l[N],r[N],d[N],num[N],a[N],n,m,i;
int read(){int x;scanf("%d",&x);return x;}
int check(int p)
{
    memset(a,0,sizeof a);
    for(i=1;i<=p;++i) a[l[i]]+=d[i],a[r[i]+1]-=d[i];
    for(i=1;i<=n;++i)
    {
	a[i]+=a[i-1];
	if(a[i]>num[i]) return 0;
    }
    return 1;
}
int main()
{
    n=read(),m=read();
    int L,R,mid;
    for(i=1;i<=n;++i) num[i]=read();
    for(i=1;i<=m;++i) d[i]=read(),l[i]=read(),r[i]=read();
    L=1,R=m;
    if(check(m)) return !printf("0");
    while(L<R) if(check(mid=L+R>>1)) L=mid+1; else R=mid;
    return !printf("-1\n%d",L);
}

疫情控制

Link
首先我们二分一下答案。
然后我们用倍增让军队往上跳,最多先跳到根的子节点。
如果当前军队可以到达根节点,那么记录一下它的编号和它到达根节点后还可以走的时间。
并且我们记录根节点的叶子节点上到根节点后剩余时间最短的军队。
如果不可以到达根节点,那么在它跳到的节点设置检查点。
如果一个节点建立了检查点或者它的所有子树都设立了检查点,则说明以这个节点为根的子树已经被封死。记录根节点的所有子树中,未被封死的子树。
将我们已经记录好了的可以到根节点的军队按照剩余时间从大到小排序。
将未被封死的子树按照到子树到根节点的距离从大到小排序。
然后依次处理未被封死的子树要由哪支军队来管辖。
离根节点远的子树由剩余时间大的军队来管辖是没有问题的,不过更优的是由本来就在这棵子树上的军队来管辖。
所以我们先查看我们事先记录的(在子树中可以到达根节点,且到根节点后剩余时间最小的军队)是否被使用,如果被使用,再看当前没有被使用的军队里剩时间最大的可否到达这棵子树。

#include<bits/stdc++.h>
#define LL long long
#define pb push_back
#define P pair<int,int>
#define fir first
#define sec second
using namespace std;
namespace IO
{
    char ibuf[(1<<21)+1],*iS,*iT;
    char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
    int read(){int x=0;char ch=Get();while(ch>57||ch<48)ch=Get();while(ch>=48&&ch<=57)x=x*10+(ch^48),ch=Get();return x;}
}
using namespace IO;
const int N=50007;
vector<P>E[N];
int pos[N],F[N][20],f[N],vis[N],used[N],r[N],n,m,cnt1,cnt2;
LL d[N][20];
struct node{int r,id;}a[N],b[N];
int operator<(node a,node b){return a.r>b.r;}
void dfs(int u,int fa,int dis)
{
    F[u][0]=fa,d[u][0]=dis;
    for(int i=1;i<=17;++i) F[u][i]=F[F[u][i-1]][i-1],d[u][i]=d[u][i-1]+d[F[u][i-1]][i-1];
    for(P p:E[u]) if(p.fir^fa) dfs(p.fir,u,p.sec);
}
int checkd(int u,int fa)
{
    int f1=1,f2=0;
    if(vis[u]) return 1;
    for(P p:E[u])
    {
        if(p.fir==fa) continue;
	f2=1;
	if(!checkd(p.fir,u))
	{
            f1=0;
            if(u==1) b[++cnt2]=(node){p.sec,p.fir};
            else return 0;
        }
    }
    return f1&f2;
}
int check(int T)
{
    cnt1=cnt2=0,memset(vis,0,sizeof vis),memset(f,0,sizeof f),memset(used,0,sizeof used);int i,j,u,s;
    for(i=1;i<=m;++i)
    {
	for(u=pos[i],s=0,j=17;~j;--j) if(F[u][j]>1&&d[u][j]+s<=T) s+=d[u][j],u=F[u][j];
	if(F[u][0]==1&&d[u][0]+s<=T)
	{
	    a[++cnt1]=(node){T-s-d[u][0],i};
            if(!f[u]||a[cnt1].r<r[u]) r[u]=a[cnt1].r,f[u]=i;
        }
        else vis[u]=1;
    }
    if(checkd(1,0)) return 1;
    sort(a+1,a+1+cnt1),sort(b+1,b+1+cnt2),u=used[0]=1;
    for(i=1;i<=cnt2;++i)
    {
        if(!used[f[b[i].id]]){used[f[b[i].id]]=1;continue;}
        while(u<=cnt1&&(used[a[u].id]||a[u].r<b[i].r)) ++u;
        if(u>cnt1) return 0;
	used[a[u].id]=1;
    }
    return 1;
}
main()
{
    n=read();int i,u,v,w,l=0,r=2e9,ans=-1,mid;
    for(i=1;i<n;++i) u=read(),v=read(),w=read(),E[u].pb(P(v,w)),E[v].pb(P(u,w));
    dfs(1,0,0),m=read();
    for(i=1;i<=m;++i) pos[i]=read();
    while(l<=r) check(mid=l+r>>1)? ans=mid,r=mid-1:l=mid+1;
    return !printf("%d",ans);
}