1.1002.支配树问题,对原图反向建图,然后中心城市变为入度为0的点。把这些点收集一下,建一个原点,所有中心城市与原点连边,问题转化为从原点到目标的两个点的必经之点有多少个?对这个图建立支配树,支配树是个啥玩意就不多说了,因为我也是现学。建完之后就是求两个点到原点加起来有多少个不同的点,LCA问题。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int t,n,m,q,in[maxn],lg[maxn];
queue<int>que;

struct ControlTree
{
struct Node
{
vector<int>e[maxn];
inline void adde(int u,int v)
{
e[u].push_back(v); //有向图
}
} a,b,c,d; //d存的就是支配树
int dfn[maxn],id[maxn],fa[maxn],cnt;
void dfs(int u)
{
dfn[u]=++cnt;
id[cnt]=u;
int sz=a.e[u].size();
for(int i=0; i<sz; i++)
{
int v=a.e[u][i];
if(dfn[v])
continue;
fa[v]=u;
dfs(v);
}
}

int semi[maxn],idom[maxn],bel[maxn],val[maxn];
inline int Find(int x)
{
if(x==bel[x])
return x;
int tmp=Find(bel[x]);
if(dfn[semi[val[bel[x]]]]<dfn[semi[val[x]]])
val[x]=val[bel[x]];
return bel[x]=tmp;
}

inline void tarjan()
{
for(int i=cnt; i>1; i--)
{
int u=id[i],sz=b.e[u].size();
for(int i=0; i<sz; i++)
{
int v=b.e[u][i];
if(!dfn[v])
continue;
Find(v);
if(dfn[semi[val[v]]]<dfn[semi[u]])
semi[u]=semi[val[v]];
}
c.adde(semi[u],u);
bel[u]=fa[u];
u=fa[u];
sz=c.e[u].size();
for(int i=0; i<sz; ++i)
{
int v=c.e[u][i];
Find(v);
if(semi[val[v]]==u)
idom[v]=u;
else
idom[v]=val[v];
}
}
for(int i=2; i<=cnt; i++)
{
int u=id[i];
if(idom[u]!=semi[u])
idom[u]=idom[idom[u]];
}
}


int dep[maxn],father[maxn][22];
void build(int u,int f)
{
dep[u]=dep[f]+1;
father[u][0]=f;
for(int i=1; (1<<i)<=dep[u]; i++)
father[u][i]=father[father[u][i-1]][i-1];
for(int i=0; i<(int)d.e[u].size(); i++)
build(d.e[u][i],u);
}

int lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
while(dep[x]>dep[y])
x=father[x][lg[dep[x]-dep[y]]];
if(x==y)
return x;
for(int k=lg[dep[x]-1]; k>=0; k--)
if(father[x][k]!=father[y][k])
{
x=father[x][k];
y=father[y][k];
k=min(k,lg[dep[x]-1]+1);
}
return father[x][0];
}

void ini()
{
for(int i=0;i<=n+2;i++)
{
a.e[i].clear();
b.e[i].clear();
c.e[i].clear();
d.e[i].clear();
dfn[i]=id[i]=fa[i]=dep[i]=idom[i]=0;
}
cnt=0;
}
} ct;


int main()
{
for(int i=2; i<maxn; i++)
lg[i]=lg[i-1]+(i==(i&-i));
cin>>t;
while(t--)
{
cin>>n>>m;
ct.ini();
for(int i=0;i<=n+2;i++) in[i]=0;
for(int i=1; i<=n+2; i++)
ct.semi[i]=ct.bel[i]=ct.val[i]=i; // 初始化

for(int i=1; i<=m; i++)
{
int u,v;
scanf("%d%d",&u,&v);
in[u]++;
ct.a.adde(v,u); //反向建图
ct.b.adde(u,v); //建立反图
}
for(int i=1;i<=n;i++)
if(in[i]==0) que.push(i);
int s=n+1;
while(!que.empty())
{
int u=que.front();
que.pop();
ct.a.adde(s,u);
ct.b.adde(u,s);
}
ct.dfs(s);
ct.tarjan();
for(int i=1;i<=n;i++)
ct.d.adde(ct.idom[i],i);

ct.build(s,s+1);
cin>>q;
for(int i=1;i<=q;i++)
{
int a,b;
scanf("%d%d",&a,&b);
int ans=ct.dep[a]+ct.dep[b]-ct.dep[ct.lca(a,b)]-1;
printf("%d\n",ans);
}
}
}

2.1004.数据结构维护DP,其实就是k个最大字段和,但是转移状态比较多,所以用数据结构加速一下,可以使用树状数组或者其他。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=2e5+5;
ll a[maxn],f[maxn];

int bit[maxn*2+100];
void update(int x,int d){
for(int i=x;i>0;i-=i&-i) bit[i]=max(bit[i],d);
}
int querysuf(int x,int B){
int ret=-1e9;
for(int i=x;i<B;i+=i&-i) ret=max(bit[i],ret);
return ret;
}
void remove(int x,int B){
for(int i=x;i<B;i+=i&-i) bit[i]=-1e9;
for(int i=x;i>0;i-=i&-i) bit[i]=-1e9;
}

vector<ll> disc;
int getid(ll x){return lower_bound(disc.begin(),disc.end(),x)-disc.begin()+1;}

bool check(ll ans,int n,int k){// sum<=ans
disc.clear();
for(int i=1;i<=n;i++) {
disc.push_back(a[i]);
disc.push_back(a[i]-ans);
}
disc.push_back(0);
sort(disc.begin(),disc.end());
disc.erase(unique(disc.begin(),disc.end()),disc.end());

for(int i=1;i<=disc.size();i++) remove(i,disc.size());
f[0]=0;
update(getid(0),f[0]);
for(int i=1;i<=n;i++) {
f[i]=querysuf(getid(a[i]-ans),disc.size()+1)+1;
update(getid(a[i]),f[i]);
if(f[i]>=k) return true;
}
return false;
}

int main(){
int T; scanf("%d",&T);
while(T--){
int n,k;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) {
scanf("%lld",a+i);
a[i]+=a[i-1];
}
ll l=-1e9*1e6,r=1e9*1e6;
while(l+1<r){// ans -> ( ]
ll mid=(l+r)/2;//
if(check(mid,n,k)) r=mid;//
else l=mid;
}
printf("%lld\n",r);
}
}

3.1006威尔逊定理。因为两个素数的间隔是很小的,int范围内不会超过500.所以直接向后找,对于每个数MR判定一下。

#include<bits/stdc++.h>
using namespace std;

#define inf 1000000000
//#define mod 1000000007
#define maxn 100005

#define pb push_back
#define mp make_pair
#define F first
#define S second
#define pii pair

#define lowbit(x) (x&(-x))
#define debug cout<<"hi"<<endl;

typedef long long ll;
typedef long double ld;

//inline void addmod(int &a,int b){a+=b;if(a>=mod) a-=mod;}
//inline void decmod(int &a,int b){a-=b;if(a<0)a+=mod;}
//inline void addmod(ll &a,ll b){a+=b;if(a>=mod) a-=mod;}
//inline void decmod(ll &a,ll b){a-=b;if(a<0)a+=mod;}

/********** show time **********/

ll p,t;

using namespace std;

namespace ddd
{
//****************************************************************
// Miller_Rabin 算法进行素数测试
//速度快,而且可以判断 <2^63的数
//****************************************************************
const int S = 20;//随机算法判定次数,S越大,判错概率越小


//计算 (a*b)%c. a,b都是long long的数,直接相乘可能溢出的
// a,b,c <2^63
long long mult_mod(long long a, long long b, long long c)
{
a %= c;
b %= c;
long long ret = 0;
while (b)
{
if (b & 1) { ret += a; ret %= c; }
a <<= 1;
if (a >= c)a %= c;
b >>= 1;
}
return ret;
}



//计算 x^n %c
long long pow_mod(long long x, long long n, long long mod)//x^n%c
{
if (n == 1)return x % mod;
x %= mod;
long long tmp = x;
long long ret = 1;
while (n)
{
if (n & 1) ret = mult_mod(ret, tmp, mod);
tmp = mult_mod(tmp, tmp, mod);
n >>= 1;
}
return ret;
}





//以a为基,n-1=x*2^t a^(n-1)=1(mod n) 验证n是不是合数
//一定是合数返回true,不一定返回false
bool check(long long a, long long n, long long x, long long t)
{
long long ret = pow_mod(a, x, n);
long long last = ret;
for (int i = 1; i <= t; i++)
{
ret = mult_mod(ret, ret, n);
if (ret == 1 && last != 1 && last != n - 1) return true;//合数
last = ret;
}
if (ret != 1) return true;
return false;
}


bool Miller_Rabin(long long n)
{
if (n < 2)return false;
if (n == 2)return true;
if ((n & 1) == 0) return false;//偶数
long long x = n - 1;
long long t = 0;
while ((x & 1) == 0) { x >>= 1; t++; }
for (int i = 0; i < S; i++)
{
long long a = rand() % (n - 1) + 1;//rand()需要stdlib.h头文件
if (check(a, n, x, t))
return false;//合数
}
return true;
}
}


ll qm(ll a, ll b,ll mod)
{
ll ans = 0;
while(b)
{
if(b & 1) //看b的末位是否为1
{
ans = (ans + a) % mod;
}
a = (a * 2) % mod;
b >>= 1;
}
return ans;
}

ll qp(ll a,ll b,ll mod)
{
ll res=1;
while(b)
{
if(b&1)
res=qm(res,a,mod);//res=res*a%mod;
b>>=1;
a=qm(a,a,mod);
//a=a*a%mod;
}
return res;
}

void gao()
{
ll q;
ll x=p;
while(1)
{
x--;
if(ddd::Miller_Rabin(x))
{
q=x;
break;
}
}
ll ans=-1;
ans+=p;
//cout<<ans<<endl;
for(ll i=q+1;i<=p-1;i++)
{
ans=qm(ans,qp(i,p-2,p),p);
//ans=ans*qp(i,p-2,p);
ans%=p;
}
printf("%lld\n",ans);
}



int main()
{
cin>>t;
while(t--)
{
cin>>p;
gao();
}
}

4.1007CF原题。。。。

5.k上升子序列和最大问题。费用流+拆点限流。很多人都是直接网络流过去的,但是其实真的要说网络流是不能AC的,需要优化建图。所以我觉得这个题目有点XX。