文章目录
快速幂
//求a^k的前三位和后三位
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
ll fast_power(ll base,ll power,ll mod){
ll result=1;
while(power>0){
if(power&1)
result=result*base%mod;
power>>=1;
base=(base*base)%mod;
}
return result;
}
int main(){
int t,v=1;
scanf("%d",&t);
while(t--){
ll n,k,x;
double ka,y,xy,j;
scanf("%lld %lld",&n,&k);
ka=k*log10(1.0*n);
x=(int)ka;
y=ka-x;
xy=pow(10,y)*100;
printf("Case %d: %d %03lld\n",v++,(int)xy,fast_power(n,k,1000));
//后三位用0补
}
return 0;
}
//求前三位:
//
//每一个数n都可以写作10^t(t大多是小数),则此时n^k可写作10^kt
//n=10^a
//n^k=10^ka
//ka=x+y(x是整数部分,y是小数部分)
//n^k=10^x * 10^y
//因为x是整数,所示10^x是1,10,100,1000…表示的是n^k有多少位,
//y是小数,10^y表示的是n^k的具体的数值。
//所以我们要求n^k的前a位,即求10^(a-1+y)
//(为什么是a-1? 答:指数函数10^x,当x>0时,10^x>1)
//https://blog.csdn.net/qq_43746332/article/details/96750087
快读
inline int read() {
char c = getchar();
int x = 0, f = 1;
while (c < '0' || c > '9') {
if (c == '-')
f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
马拉车
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
char s[210010],str[210010];
int len[210010];//i位置最长回文串的半径
int main()
{
while(~scanf("%s",str))
{
int i,j,k=2,l;
s[0]='$';
s[1]='#';
l=strlen(str);
for(i=0;i<l;i++)
{
s[k++]=str[i];
s[k++]='#';
}
int id,mx=0,ans=0;
for(i=1;i<k;i++)
{
len[i]=i<mx ? min(mx-i,len[2*id-i]) : 1;
/*当 mx - i > P[j] 的时候,以 S[j] 为中心的回文子串包含在
以 S[id]为中心的回文子串中,由于 i 和 j 对称,
以 S[i] 为中心的回文子串必然包含在
以 S[id]为中心的回文子串中,
所以必有 P[i] = P[j],其中 j = 2id - i
*/
while(s[i+len[i]]==s[i-len[i]])
len[i]++;
if(i+len[i]>mx)
{
mx=i+len[i];
id=i;
}
ans=max(ans,len[i]-1);//最长子串的长度是半径减1,
}
printf("%d\n",ans);
}
return 0;
}
最小生成树
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int MAX=1e3+10,INF=0x7fffffff;
char s[MAX*3][10];
int e[MAX*2][MAX*2],dis[MAX*2],book[MAX*2];
int n;
void init()
{
memset(e,0,sizeof(e));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i==j)
e[i][j]=0;
else
e[i][j]=INF;
}
}
}
void solve()
{
while(~scanf("%d",&n)&&n)
{
int i,j,k;
memset(s,0,sizeof(s));
memset(dis,0,sizeof(dis));
memset(book,0,sizeof(book));
getchar();
init();
for(i=1;i<=n;i++)
gets(s[i]);
int num=0;
for(i=1;i<n;i++)
{
for(j=i+1;j<=n;j++)
{
int sum=0;
for(k=0;k<7;k++)
{
if(s[i][k]!=s[j][k])
{
sum++;
}
}
e[j][i]=e[i][j]=sum;
}
}
for(i=1;i<=n;i++)
dis[i]=e[1][i];
book[1]=1;
int count=1,minn,s=0;
while(count<n)
{
minn=INF;
for(i=1;i<=n;i++)
{
if(book[i]==0&&dis[i]<minn)
{
minn=dis[i];
j=i;
}
}
book[j]=1;
count++;
s+=dis[j];
for(int k=1;k<=n;k++)
{
if(book[k]==0&&dis[k]>e[j][k])
dis[k]=e[j][k];
}
}
printf("The highest possible quality is 1/%d.\n",s);
}
}
int main()
{
solve();
return 0;
}
最短路
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x7fffffff,MAX=1e3+10;
int e[MAX][MAX],book[MAX],vis[MAX],s[MAX];
int T,S,D,r,n,minn,x,y,k,a;
void dij() {
memset(book,0,sizeof(book));
for(int i=0; i<=n; i++)
vis[i]=e[0][i];
book[0]=1;
for(int i=0; i<n; i++) {
minn=INF;
for(int j=0; j<=n; j++) {
if(vis[j]<minn&&!book[j]) {
k=j;
minn=vis[j];
}
}
book[k]=1;
for(int j=0; j<=n; j++)
if(vis[j]>vis[k]+e[k][j]&&!book[j])
vis[j]=vis[k]+e[k][j];
}
}
void init() {
memset(e,0x3f,sizeof(e));
for(int i=0; i<MAX; i++) {
e[i][i]=0;
}
}
void solve() {
while(scanf("%d%d%d",&T,&S,&D)!=EOF) {
n=0;
init();
for(int i=1; i<=T; i++) {
scanf("%d%d%d",&x,&y,&r);
n=max(max(n,x),y);
if(e[x][y]>r)
e[x][y]=e[y][x]=r;
}
minn=INF;
for(int i=0; i<S; i++) {
scanf("%d",&a);
e[0][a]=e[a][0]=0;
}
for(int i=0; i<D; i++)
scanf("%d",&s[i]);
dij();
for(int i=0; i<D; i++)
minn=min(minn,vis[s[i]]);
printf("%d\n",minn);
}
}
int main() {
solve();
return 0;
}
字典树
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e6+10;
int tot=1,n;
int ch[maxn][30],po[maxn];
void insert(char *s){
int l=strlen(s),u=0;
for(int i=0;i<l;i++){
int v=s[i]-'a';
if(!ch[u][v])
ch[u][v]=++tot;
u=ch[u][v];
po[u]++;
}
}
bool search(char *s){
int l=strlen(s),u=0;
for(int i=0;i<l;i++){
int v=s[i]-'a';
if(!ch[u][v])
return false;
u=ch[u][v];
}
return po[u];//删除删的是po数组,而ch[u][v]还存在
}
void dele(char *s){
int l=strlen(s),u=0;
for(int i=0;i<l;i++){
int v=s[i]-'a';
if(!ch[u][v])
return ;
u=ch[u][v];
}
int e=po[u];
u=0;
for(int i=0;i<l;i++){
int v=s[i]-'a';
u=ch[u][v];
po[u]-=e;
}
for(int i=0;i<26;i++)
ch[u][i]=0;
}
void solve(){
scanf("%d",&n);
memset(ch,0,sizeof(ch));
memset(po,0,sizeof(po));
while(n--){
char s[40],a[40];
scanf("%s %s",s,a);
switch (s[0]){
case 'i':insert(a);break;
case 's':
if(search(a))
printf("Yes\n");
else
printf("No\n");
break;
default :dele(a);break;
}
}
}
int main(){
solve();
return 0;
}
优先队列
priority_queue <int,vector,greater > q;//升序队列
priority_queue <int,vector,less >q;//降序队列
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
/* ______________ ______________ ________________
/ ____________| / ____________ \ | ____________ \
/ / / / \ \ | | \ \
/ / / / \ \ | | \ \
/ / _________ / / \ \ | | \ \
\ \ |___ ___/ \ \ / / | | / /
\ \ \ \ \ \ / / | | / /
\ \__________\ \ \ \_____________/ / | |____________/ /
God Bless \_______________\ \_______________/ |________________/
*/
#include <stdio.h>
#include <string.h>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
priority_queue<ll,vector<ll>,greater<ll> > q;//按升序排列 0 1 2 3
int main()
{
ll i,j,n,k,x;
scanf("%lld",&n);
ll num=0,sum=0;
for(i=1;i<=n;i++)
{
scanf("%lld",&x);
if(x>=0)//如果是正值肯定要喝
{
sum+=x;
num++;
q.push(x);
}
else
{
if((sum+x)>=0)//如果是负值但喝了没死先喝再说
{
sum+=x;
num++;
q.push(x);
}
else
{
/*如果是负值这瓶喝完就死,
找到(优先队列顶端(greater从小到大排序)就是最小)
之前喝的比这个还'毒'的,喝这个不喝之前那个 */
if(!q.empty() &&x>q.top() )
{
sum-=q.top() ;
q.pop() ;
q.push(x);
sum+=x;
}
}
}
}
printf("%lld\n",num);
return 0;
}
中国剩余定理
#include <stdio.h>
#include <string.h>
struct xa
{
int prim;
int mod;
}a[1000];
int main()
{
int n,m,flag,i,j,v;
memset(a,0,sizeof(a));
scanf("%d",&n);
for(j=1;j<=n;j++)
scanf("%d %d",&a[j].prim ,&a[j].mod );
int ans=a[1].mod ,s=1;
for(i=1;i<n;i++)
{
s*=a[i].prim ;//质数的最小公倍数就是他们的乘积
while(ans%a[i+1].prim !=a[i+1].mod )
{
ans+=s;
}
}
printf("%d\n",ans);
return 0;
}
线段树
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
ll sum[maxn*4],ma[maxn*4],num[maxn];
void build(int l,int r,int root){
if(l==r){
sum[root]=ma[root]=num[l];return;
}
int m=l+r>>1;
build(l,m,root<<1);
build(m+1,r,root<<1|1);
sum[root]=sum[root<<1]+sum[root<<1|1];
ma[root]=max(sum[root<<1],sum[root<<1|1]);
}
void update(int ul,int ur,int l,int r,int root){
if(ma[root]==1||ma[root]==0)
return;
if(l==r){
ma[root]=sum[root]=sqrt(sum[root]);
return;
}
int m=l+r>>1;
if(ul<=m)
update(ul,ur,l,m,root<<1);
if(m<ur)
update(ul,ur,m+1,r,root<<1|1);
sum[root]=sum[root<<1]+sum[root<<1|1];
ma[root]=max(ma[root<<1],ma[root<<1|1]);
}
ll query(int ql,int qr,int l,int r,int root){
if(ql<=l&&r<=qr)
return sum[root];
int m=l+r>>1;
ll ans=0;
if(ql<=m)
ans+=query(ql,qr,l,m,root<<1);
if(m<qr)
ans+=query(ql,qr,m+1,r,root<<1|1);
return ans;
}
int main()
{
ll n,m;
scanf("%lld",&n);
for( int i=1; i<=n; i++ )
scanf("%lld",&num[i]);
build(1,n,1);
scanf("%lld",&m);
for( int i=0; i<m; i++ ){
ll op,a,b;
scanf("%lld %lld %lld",&op,&a,&b);
if(op==1)
printf("%lld\n",query(a,b,1,n,1));
else
update(a,b,1,n,1);
}
return 0;
}
唯一分解定理
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <algorithm>
using namespace std;
int a[10000000];
long long prim[10000000],num=1;
void getprim()//素数打表
{
for(int i=2;i<=10000000;i++)
{
if(a[i]==0)
{
prim[num++]=i;
for(int j=i;j<=10000000;j+=i)
{
a[j]=1;
}
}
}
}
long long only(long long n)//唯一分解定理公式模板
{
long long cnt,divisor=1;
for(int i=1;i<num&&prim[i]*prim[i]<=n;i++)
{
if(n%prim[i]==0)
{
cnt=0;
while(n%prim[i]==0)
{
cnt++;
n/=prim[i];
}
divisor*=(cnt+1);
}
}
if(n > 1)
divisor*=2;
//大于1就乘2,因为打表只能打到1e6,数据给的到了1e12
//所以在大于1e6时还会有另一半因子
//比如1e3对应的1e9
return divisor;
}
int main()
{
getprim();
int t,v=1;
scanf("%d",&t);
while(t--)
{
long long n,m;
scanf("%lld%lld",&n,&m);
printf("Case %d: ",v++);
if(n/m<m)
{
printf("0\n");
continue;
}
long long ans=only(n)/2;
for(int i=1;i<m;i++)
{
if(n%i==0)//除掉小于最小边长m的错误数据
ans--;
}
printf("%lld\n",ans);
}
return 0;
}
网络流之最大流
#include <iostream>
#include <queue>
#include<string.h>
using namespace std;
#define arraysize 201
int maxData = 0x7fffffff;
int capacity[arraysize][arraysize]; //记录残留网络的容量
int flow[arraysize]; //标记从源点到当前节点实际还剩多少流量可用
int pre[arraysize]; //标记在这条路径上当前节点的前驱,同时标记该节点是否在队列中
int n,m;
queue<int> myqueue;
int BFS(int src,int des) {
int i,j;
while(!myqueue.empty()) //队列清空
myqueue.pop();
for(i=1; i<m+1; ++i) {
pre[i]=-1;
}
pre[src]=0;
flow[src]= maxData;
myqueue.push(src);
while(!myqueue.empty()) {
int index = myqueue.front();
myqueue.pop();
if(index == des) //找到了增广路径
break;
for(i=1; i<m+1; ++i) {
if(i!=src && capacity[index][i]>0 && pre[i]==-1) {
pre[i] = index; //记录前驱
flow[i] = min(capacity[index][i],flow[index]); //关键:迭代的找到增量
myqueue.push(i);
}
}
}
if(pre[des]==-1) //残留图中不再存在增广路径
return -1;
else
return flow[des];
}
int maxFlow(int src,int des) {
int increasement= 0;
int sumflow = 0;
while((increasement=BFS(src,des))!=-1) {
int k = des; //利用前驱寻找路径
while(k!=src) {
int last = pre[k];
capacity[last][k] -= increasement; //改变正向边的容量
capacity[k][last] += increasement; //改变反向边的容量
k = last;
}
sumflow += increasement;
}
return sumflow;
}
int main() {
int i,j;
int start,end,ci;
while(cin>>n>>m) {
memset(capacity,0,sizeof(capacity));
memset(flow,0,sizeof(flow));
for(i=0; i<n; ++i) {
cin>>start>>end>>ci;
if(start == end) //考虑起点终点相同的情况
continue;
capacity[start][end] +=ci; //此处注意可能出现多条同一起点终点的情况
}
cout<<maxFlow(1,m)<<endl;
}
return 0;
}
完全背包
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int num,cost,dp[110000],w[110000];
int main()
{
int k,n,m,i,v,s,p;
while(~scanf("%d %d",&k,&n))
{
v=1;
memset(dp,0,sizeof(dp));
//完全背包的二进制优化
for(i=1;i<=n;i++)
{
scanf("%d %d",&num,&cost );
s=1;
while(s<=num)
{
w[v++]=cost*s;
num-=s;
s=s*2;
}
if(num)
w[v++]=cost*num;
}
for(i=1;i<v;i++)
for(int j=k;j>=w[i];j--)
dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
printf("%d\n",dp[k]);
}
return 0;
}
贪心
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int x[1001];
int main()
{
int r,n,sum;
while(scanf("%d%d",&r,&n)&&(r!=-1||n!=-1))
{
int i,j,k;
for(i=0;i<n;i++)
scanf("%d",&x[i]);
sort(x,x+n);
i=0,sum=0;
while(i<n)
{
j=x[i++];
while(i<n&&x[i]<=j+r)
i++;
k=x[i-1];
while(i<n&&x[i]<=k+r)
i++;
sum++;
}
printf("%d\n",sum);
}
return 0;
}
拓扑排序
#include <stdio.h>
#include <string.h>
int book[301][301];
int s[300];
int vis[300];
int main()
{
int n,m,t;
scanf("%d",&t);
while(t--)
{
int x,y,j,flag=0;
scanf("%d %d",&n,&m);
memset(book,0,sizeof(book));
memset(vis,0,sizeof(vis));
for(int i=0;i<m;i++)
{
scanf("%d %d",&y,&x);//坑一,题目给的后者比前者重;
if(book[x][y]==0)
{
book[x][y]=1;
vis[y]++;
}
}
int num=n,t,i;
for(i=1;i<=n;i++)
{
for(j=n;j>=1;j--)//输出为字典序最小,因此从最大开始遍历
{
if(vis[j]==0)
{
s[j]=num--;//记录j号球的重量num【从大到小排序】
vis[j]--;
t=j;
break;
}
}
if(j==0)
{
flag=1;
break;
}
for(int k=1;k<=n;k++)
{
if(book[t][k])
vis[k]-- ;
}
}
if(flag)
{
printf("-1\n");
continue;
}
for(int i=1;i<=n;i++)
{
if(i!=n)
printf("%d ",s[i]);
else
printf("%d\n",s[i]);
}
}
return 0;
}
/*
2
5 4
5 1
4 2
1 3
2 3
10 5
4 1
8 1
7 8
4 1
2 8
重量:1 2 3 4 5 (1)
ans: **从轻到重 顺序:5 1 4 2 3 (2)
**输出每个测试用例输出单行从标签1到标签N的球的权重
**(2)中从1到5对应(1)的权值 :即:2 4 5 3 1
2 4 5 3 1
5 1 6 2 7 8 3 4 9 10
*/
约瑟夫环
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int k,n,m,ans;
int main() {
while(~scanf("%d %d %d",&n,&k,&m)&&(n||m||k)) {
ans=0;
for(int j=2; j<n; j++)
ans=(ans+k)%j;
ans=(ans+m)%n;
printf("%d\n",ans+1);
}
return 0;
}
欧拉函数
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ll;
const int MAX=1e6+10;
ll a[MAX],n;
void init()
{
a[1]=0;
for(int i=2;i<=MAX;i++)
a[i]=i;
for(int i=2;i<=MAX;i++)
if(a[i]==i)
for(int j=i;j<=MAX;j+=i)
a[j]=a[j]/i*(i-1);
for(int i=2;i<=MAX;i++)
a[i]=a[i]+a[i-1];
}
void solve() {
init();
while(~scanf("%llu",&n)&&n)
printf("%llu\n",a[n]);
}
int main() {
solve();
return 0;
}
树状数组
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,k,l,r;
int a[100010],b[100010];
int lowbit(int x){
return x&(-x);
}
void add1(int x,int y){
while(x<=n){
a[x]+=y;
x+=lowbit(x);
}
}
void add2(int x,int y){
while(x<=n){
b[x]+=y;
x+=lowbit(x);
}
}
int sum1(int x){
int ans=0;
while(x>0){
ans+=a[x];
x-=lowbit(x);
}
return ans;
}
int sum2(int x){
int ans=0;
while(x>0){
ans+=b[x];
x-=lowbit(x);
}
return ans;
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d %d %d",&k,&l,&r);
if(k==1){
add1(l,1);
add2(r,1);
}
else
printf("%d\n",sum1(r)-sum2(l-1));
}
return 0;
}
三分
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
double H,D,h;
double check(double x)//我们走到的点的位置
{
double l1=(D-x);
double l2=H-(H-h)*D/x;
return l1+l2;
}
int main()
{
int t; scanf("%d",&t);
while(t--)
{
scanf("%lf%lf%lf",&H,&h,&D);
double l=(H-h)*D/H,r=D;//l,r表示的是人走的距离
//注意l要从(H-h)*D/H开始,不然墙上没有影子
//人走的最大距离自然就是D
while(r-l>=1e-6)
{
double mid1=l+(r-l)/3.0,mid2=r-(r-l)/3.0;//三分不解释
if(check(mid1)>check(mid2)) r=mid2;//求最小不解释
else l=mid1;
}
printf("%.3lf\n",check(r));
//check(l)和check(r)都可以,没理解?
//大概是因为无限接近,并且直接的差小于10的-6次方
//所以保留三位不影响结果
}
return 0;
}
区间查质数
/* ______________ ______________ ________________
/ ____________| / ____________ \ | ____________ \
/ / / / \ \ | | \ \
/ / / / \ \ | | \ \
/ / _________ / / \ \ | | \ \
\ \ |___ ___/ \ \ / / | | / /
\ \ \ \ \ \ / / | | / /
\ \__________\ \ \ \_____________/ / | |____________/ /
God Bless \_______________\ \_______________/ |________________/
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const long long MAX_L = 2e6+10;
const long long MAX_SQRT_B = 2e6+10;
bool is_prime[MAX_L];
bool is_prime_small[MAX_SQRT_B];
void segment_sieve(ll a,ll b){
for(int i=0;(ll)i*i<b;i++)
is_prime_small[i] = true;
// a= 4 b= 7
// is_prime_small[0]=1;
// is_prime_small[1]=1;
// is_prime_small[2]=1;
for(int i=0;i<b-a;i++)
is_prime[i] = true;
//b-a=3;
// is_prime[0]=0;
// is_prime[1]=1;
// is_prime[2]=0;
for(int i=2;(ll)i*i<b;i++){
if(is_prime_small[i]){
for(int j=2*i;(ll)j*j<b;j+=i)
is_prime_small[j] = false;
for(ll j=max(2ll,(a+i-1)/i)*i;j<b;j+=i)
is_prime[j-a] = false;
}
}
}
int main(){
ll a,b,sum=0;
scanf("%lld %lld",&a,&b);
segment_sieve(a, b);
for(int i=0;i<b-a;i++)
if(is_prime[i])
sum++;
printf("%lld\n",sum);
return 0;
}
强连通分量
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
const int MAX=1e3+10,inf=0x7fffffff;
vector<int> v[MAX];//存路
stack<int> s;//栈,用来储存和清除连接的点,若连通该强连通分量所有点都会出栈
int n,m,x,y,num,sum,cnt;
int dfn_clock;//每个时间戳的标号
int s_s[MAX];//记录当前点所在哪一个强连通分量内
int dfn[MAX];//时间戳
bool instack[MAX];//判断该点是否在栈内
int low[MAX];//记录所属强连通
int cost[MAX];//记录ACMer打电话到每个人的费用
int min_cost[MAX];//记录联系当前强连通分量中电话费最少的
int in[MAX];//入度
void tarjan(int x) {
dfn[x]=low[x]=++dfn_clock;
s.push(x);
instack[x]=true;
for(int i=0; i<v[x].size(); i++) {
int u=v[x][i];
if(!dfn[u]) {
tarjan(u);
low[x]=min(low[x],low[u]);
} else if(instack[u])
low[x]=min(low[x],dfn[u]);
}
if(dfn[x]==low[x]) {
int u,minn=inf;
cnt++;
do {
u=s.top();
s.pop() ;
instack[u]=false;
s_s[u]=cnt;
if(cost[u]<minn)
minn=cost[u];
} while(u!=x);
min_cost[cnt]=minn;
}
}
void find() {//如果是两个图中间或某一个点与其它点都不连通,也要算作一个强连通;
dfn_clock=cnt=0;
for(int i=1; i<=n; i++)
if(!dfn[i])
tarjan(i);
}
void init() {
memset(min_cost,0,sizeof(min_cost));
memset(s_s,0,sizeof(s_s));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(cost,0,sizeof(cost));
memset(in,0,sizeof(in));
for(int i=1;i<=n;i++)
v[i].clear() ;
}
void solve() {
while(~scanf("%d %d",&n,&m)) {
init();
for(int i=1; i<=n; i++)
scanf("%d",&cost[i]);
for(int i=1; i<=m; i++) {
scanf("%d %d",&x,&y);
v[x].push_back(y);
}
find();
for(int i=1; i<=n; i++) {
for(int j=0; j<v[i].size() ; j++) {
int u=v[i][j];
if(s_s[i]!=s_s[u])
in[s_s[u]]++;
}
}
num=0,sum=0;
for(int i=1; i<=cnt; i++)
if(in[i]==0) {
num++;
sum+=min_cost[i];
}
printf("%d %d\n",num,sum);
}
}
int main() {
solve();
return 0;
}
欧拉回路
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=1e6+10;
int head[maxn],nex[maxn],to[maxn];
bool flag[maxn];
int in[maxn],out[maxn];
int tot=1,t,n,m;
vector<int> v;
inline void add(int v,int u){
to[++tot]=u;
nex[tot]=head[v];
head[v]=tot;
}
void dfs(int x){
for(int &i=head[x],y;y=to[i],i;i=nex[i]){
int c=(t==1?i/2:i-1);
int sig=i%2;
if(flag[c])
continue;
flag[c]=1;
dfs(y);
if(t==1)
v.push_back(sig?-c:c);
else
v.push_back(c);
}
}
int main(){
scanf("%d %d %d",&t,&n,&m);
for(int i=1;i<=m;i++){
int v,u;
scanf("%d %d",&v,&u);
add(v,u);
if(t==1)
add(u,v);
out[v]++;
in[u]++;
}
if(t==1){
for(int i=1;i<=n;i++){
if((in[i]+out[i])%2){
printf("NO\n");
return 0;
}
}
}
else
{
for(int i=1;i<=n;i++){
if(in[i]!=out[i]){
printf("NO\n");
return 0;
}
}
}
for(int i=1;i<=n;i++){
if(head[i]){
dfs(i);
break;
}
}
if(v.size()!=m){
printf("NO\n");
return 0;
}
printf("YES\n");
for(int i=m-1;i>=0;i--)
printf("%d ",v[i]);
return 0;
}
记忆化优先搜索
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int mapp[200][200],dp[200][200],n,m;
int find(int x,int y)
{
if(dp[x][y])
return dp[x][y];
int i,j,dx,dy;
int k=mapp[x][y];
for(i=0; i<=k; i++)
{
for(j=0; j<=k-i; j++)
{
dx=x+i;
dy=y+j;
if(i==0&&j==0)
continue;
if(dx<1||dy<1||dx>n||dy>m)
continue;
dp[x][y]=(find(dx,dy)+dp[x][y])%10000;
}
}
return dp[x][y];
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int i,j;
memset(dp,0,sizeof(dp));
scanf("%d %d",&n,&m);
for(i=1; i<=n; i++)
for(j=1; j<=m; j++)
scanf("%d",&mapp[i][j]);
dp[n][m]=1;
printf("%d\n",find(1,1));
}
return 0;
}
字符串哈希
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef unsigned long long ULL;
const int maxn=1e6+10;
ULL power[maxn],hs[maxn],b=233333;
char s[maxn];
void solve()
{
power[0]=1;
for(int i=1;i<=maxn;i++)
power[i]=power[i-1]*b;
while(~scanf("%s",s+1))
{
if(s[1]=='.')
break;
int l=strlen(s+1);
hs[0]=0;
for(int i=1;i<=l;i++)
hs[i]=hs[i-1]*b+(ULL)(s[i]-'a'+2333);
int ans=1;
for(int i=1;i<=l;i++)
{
if(l%i!=0)
continue;
ULL mm=hs[i]-hs[0]*power[i];
// printf("%ull****%d\n",mm,i);
int j;
for(j=i;j<=l-i;j+=i)
{
// ULL nn=(ULL)(hs[j+i]-hs[j]*power[i]);
// printf("%ull@@@\n",nn);
if(mm!=(ULL)(hs[j+i]-hs[j]*power[i]))
break;
}
// printf("%d****\n",j);
if(j>=l)
{
ans=l/i;
break;
}
}
printf("%d\n",ans);
}
}
int main()
{
solve();
return 0;
}
哈希表
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int mod1=99999,mod2=999999,p1=233,p2=2333,maxn=3e4+10;
int tot=0,nxt[maxn],poi[mod1],endd[maxn];
void insert(int x,int y)
{
nxt[++tot]=poi[x];
poi[x]=tot;
endd[tot]=y;
}
int query(int x,int y)
{
for(int i=poi[x];i!=0;i=nxt[i])
if(endd[i]==y)
return 1;
return 0;
}
void solve()
{
int t;
char op[12],s[205];
scanf("%d",&t);
while(t--)
{
scanf("%s",op);
gets(s);
int len=strlen(s),sum1=0,sum2=0;
for(int i=0;i<len;i++)
{
sum1=(sum1*p1+s[i])%mod1;
sum2=(sum2*p2+s[i])%mod2;
}
if(op[0]=='a')
insert(sum1,sum2);
else
{
if(query(sum1,sum2))
printf("yes\n");
else
printf("no\n");
}
}
}
int main()
{
solve();
return 0;
}
割点
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,tot,root,rs,dfn_clock;
const int maxn=1e3+5;
int head[maxn],to[maxn],nex[maxn],cut[maxn];
int dfn[maxn],low[maxn];
inline void add(int v,int u){
to[tot]=u;
nex[tot]=head[v];
head[v]=tot++;
}
void tarjan(int u,int f){
low[u]=dfn[u]=++dfn_clock;
for(int i=head[u];i!=-1;i=nex[i]){
int v=to[i];
if(!dfn[v]){
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]){
if(u!=root)
cut[u]=1;
else
rs++;
}
}
else if(v!=f)
low[u]=min(low[u],dfn[v]);
}
}
int main(){
while(~scanf("%d",&n)&&n)
{
memset(head,-1,sizeof(head));
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(cut,0,sizeof(cut));
tot=0;
root=0;
rs=0;
dfn_clock=0;
int x,y;
while(~scanf("%d",&x)&&x){
while(getchar()!='\n'){
scanf("%d",&y);
add(x,y);
add(y,x);
}
}
for(int i=1;i<=n;i++){
if(!dfn[i]){
root=i;
rs=0;
tarjan(i,i);
if(rs>=2)
cut[i]=1;
}
}
int sum=0;
for(int i=1;i<=n;i++){
if(cut[i]==1)
sum++;
}
printf("%d\n",sum);
}
}
二分图最大匹配
#include <stdio.h>
#include <string.h>
int n,m,book[100],match[100],e[110][110];
int bfs(int u)
{
int i;
for(i=1;i<=n;i++)
{
if(book[i]==0 && e[u][i]==1)
{
book[i]=1;
if(match[i]==0 || bfs(match[i]))
{
match[i]=u;
match[u]=i;
return 1;
}
}
}
return 0;
}
int main()
{
int i,j,t1,t2,sum=0;
scanf("%d %d",&n,&m);//n个点m条边
for(i=1;i<=m;i++)
{
scanf("%d%d",&t1,&t2);
e[t1][t2]=1;
e[t2][t1]=1;//无向图
}
memset(match,0,sizeof(match));
for(i=1;i<=n;i++)
{
memset(book,0,sizeof(book));
if(bfs(i))
sum++;
}
printf("%d",sum);
return 0;
}
多重背包
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
struct xa
{
int cost;
int weight;
int number;
}a[1100];
int dp[300];
int main()
{
int c;
scanf("%d",&c);
while(c--)
{
int n,m,i,j,k;
memset(dp,0,sizeof(dp));
scanf("%d %d",&n,&m);
for(i=1;i<=m;i++)
scanf("%d %d %d",&a[i].cost,&a[i].weight ,&a[i].number );
for(i=1;i<=m;i++)
for(k=1;k<=a[i].number ;k++)
for(j=n;j>=a[i].cost ;j--)
dp[j]=max(dp[j],dp[j-a[i].cost]+a[i].weight );
printf("%d\n",dp[n]);
}
return 0;
}
01背包
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int p[105];
double f[105];
int n,t;
double ff;
double dp[100005];//抢到价值为i的钱逃走的最大概率
int sum;
int main()
{
scanf("%d",&t);
while(t--)
{
sum=0;
scanf("%lf %d",&ff,&n);
for(int i=0;i<n;i++)
{
scanf("%d %lf",&p[i],&f[i]);
sum+=p[i];
}
memset(dp,0,sizeof(dp));
dp[0]=1;
for(int i=0;i<n;i++)//遍历所有能选项
for(int j=sum;j>=p[i];j--)//根据p[i]确定背包容量范围进行遍历
dp[j]=max(dp[j],dp[j-p[i]]*(1-f[i]));//选p[i]和不选p[i]求最大值
int m=0;
for(int i=sum;i>=0;i--)//从容量最大开始遍历
{
if(dp[i]>=1-ff)//找到能逃走的临界值
{
m=i;
break;
}
}
printf("%d\n",m);
}
}
kmp
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
char s[1000000],t[1000000];
int nexx[1000000];
int n,T,num,lens,lent;
void getnexx()
{
int j=0,k=-1;
nexx[0]=-1;
while(j<lent)
{
if(k==-1||t[k]==t[j])
{
j++;
k++;
nexx[j]=k;
}
else
k=nexx[k];
}
}
void kmp()
{
int i=0,j=0;
while(i<lens)
{
if(j==-1||s[i]==t[j])
{
i++;
j++;
}
else
j=nexx[j];
if(j==lent)
{
num++;
j=nexx[j];
}
}
}
void solve()
{
scanf("%d",&T);
while(T--){
memset(s,0,sizeof(s));
memset(t,0,sizeof(t));
memset(nexx,0,sizeof(nexx));
scanf("%s %s",t,s);
lens=strlen(s);
lent=strlen(t);
num=0;
getnexx();
kmp();
printf("%d\n",num);
}
}
int main()
{
solve();
return 0;
}
RMQ
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e5+10,p=17;
int n,k,a[maxn],log[maxn],f[maxn][p+1],z[maxn][p+1];
int main(){
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
log[0]=-1;
for(int i=1;i<=n;i++){
f[i][0]=z[i][0]=a[i];
log[i]=log[i>>1]+1;
}
for(int j=1;j<=p;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
z[i][j]=min(z[i][j-1],z[i+(1<<j-1)][j-1]);
}
}
n=n-k+1;
int x,y,s;
for(int i=1;i<=n;i++){
x=i;
y=i+k-1;
s=log[y-x+1];
printf("%d %d\n",max(f[x][s],f[y-(1<<s)+1][s]),min(z[x][s],z[y-(1<<s)+1][s]));
}
return 0;
}
倍增求LCA
#include <bits/stdc++.h>
using namespace std;
const int ONE=100010;
int n,Q,x,y;
int nexx[ONE*2],first[ONE*2],go[ONE*2],tot;
int Dep[ONE];
int f[ONE][22];
void Add(int u,int v){
nexx[++tot]=first[u];
first[u]=tot;
go[tot]=v;
}
void Deal_first(int u,int father){
Dep[u]=Dep[father]+1;
for(int i=0;i<=19;i++){
f[u][i+1]=f[f[u][i]][i];
}
for(int e=first[u];e;e=nexx[e]){
int v=go[e];
if(v==father)
continue;
f[v][0]=u;
Deal_first(v,u);
}
}
int LCA(int x,int y){
if(Dep[x]<Dep[y])
swap(x,y);
for(int i=20;i>=0;i--){
if(Dep[f[x][i]]>=Dep[y])
x=f[x][i];
if(x==y)
return x;
}
for(int i=20;i>=0;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];
y=f[y][i];
}
}
return f[x][0];
}
int dist(int x,int y){
return Dep[x]+Dep[y]-2*Dep[LCA(x,y)];
}
int main(){
tot=0;
scanf("%d",&n);
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
Add(x,y);
Add(y,x);
}
Deal_first(1,0);
scanf("%d",&Q);
while(Q--){
scanf("%d%d",&x,&y);
printf("%d\n",dist(x,y));
}
return 0;
}
并查集
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
char s[10];
int f[100000],ke[100000],book[100000];
int Case=1,a,b,all,n,m;
int getf(int v) {
return f[v]==v?v:f[v]=getf(f[v]);
}
void merge(int v,int u) {
int t1=getf(v);
int t2=getf(u);
if(t1!=t2)
f[t2]=t1;
}
void init() {
for(int i=0; i<n; i++)
f[i]=i,ke[i]=i;
}
void solve() {
while(~scanf("%d %d",&n,&m)&&(n||m)) {
init();
all=n;
memset(book,0,sizeof(book));
for(int i=1; i<=m; i++) {
scanf("%s",s);
if(s[0]=='M') {
scanf("%d %d",&a,&b);
merge(ke[a],ke[b]);
} else {
scanf("%d",&a);
f[all]=all;
ke[a]=all;
all++;
}
}
int ans=0;
for(int i=0;i<n;i++)
{
int t=getf(ke[i]);
if(!book[t])
{
ans++;
book[t]=1;
}
}
printf("Case #%d: %d\n",Case++,ans);
}
}
int main() {
solve();
return 0;
}
尺取法
/* ______________ ______________ ________________
/ ____________| / ____________ \ | ____________ \
/ / / / \ \ | | \ \
/ / / / \ \ | | \ \
/ / _________ / / \ \ | | \ \
\ \ |___ ___/ \ \ / / | | / /
\ \ \ \ \ \ / / | | / /
\ \__________\ \ \ \_____________/ / | |____________/ /
God Bless \_______________\ \_______________/ |________________/
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
int a[100010];
int main()
{
int t,i,j;
scanf("%d",&t);
while(t--)
{
int n,s;
memset(a,0,sizeof(a));
scanf("%d %d",&n,&s);
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
int minn=n+1;
int sum=0,end=1,top=1;
/*top表示头部,end表示尾部
尺取:将头部一直往前去,知道满足条件为止,然后将尾巴往前移动一位。
将此时长度与minn比较,将更小的赋值给minn,循环这个操作,直到不能再满足条件了
*/
while(1)
{
while(top<=n&&sum<s)
{
sum+=a[top];
top++;
}
if(sum<s)
break;
minn=min(minn,top-end);//top-end表示长度
sum-=a[end++];
}
if(minn<n+1)
printf("%d\n",minn);
else
printf("0\n");
}
return 0;
}
叉乘
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
struct point//记录点的坐标
{
double x,y;
};
struct line//记录线段的始端点和尾端点
{
point st,ed;
};
//判断两条线段是否相交:https://www.cnblogs.com/tuyang1129/p/9390376.html
//叉乘:ab X ac
double mul(point a,point b,point c)
{
return (b.x -a.x )*(c.y -a.y )-(b.y -a.y )*(c.x -a.x );
}
bool find(line l1,line l2)
{
//如果l1(或l2)两端点的横坐标最大值仍比l2(或l1)两端点的横坐标最小值小,那么一定不相交;
if(max(l1.st.x ,l1.ed.x )<min(l2.st.x ,l2.ed.x )||max(l2.st.x ,l2.ed.x )<min(l1.st.x ,l1.ed.x ))
return false;
//同理,纵坐标
if(max(l1.st.y ,l1.ed.y )<min(l2.st.y ,l2.ed.y )||max(l2.st.y ,l2.ed.y )<min(l1.st.y ,l1.ed.y ))
return false;
/*
定理:向量a×向量b(×为向量叉乘),
若结果小于0,表示向量b在向量a的顺时针方向;
若结果大于0,表示向量b在向量a的逆时针方向;
若等于0,表示向量a与向量b平行。
如果线段CD的两个端点C和D,
与另一条线段的一个端点(A或B,只能是其中一个)连成的向量,
与向量AB做叉乘,
若结果异号,表示C和D分别在直线AB的两边,
若结果同号,则表示CD两点都在AB的一边,则肯定不相交。
*/
//叉乘,判断l2的两端点是否满足与l1相交的条件
if(mul(l1.st,l1.ed,l2.st)*mul(l1.st,l1.ed,l2.ed)>0)
return false;
//同理,判断l1的两端点是否满足与l2相交的条件
if(mul(l2.st,l2.ed,l1.st)*mul(l2.st,l2.ed,l1.ed)>0)
return false;
return true;
}
bool judge(point a,point b,point c,point d,line l1)
{
line l2;
//l2依次代表矩形四边,分别判断是否相交
l2.st =a;
l2.ed =b;
if(find(l1,l2))
return true;
l2.st =a;
l2.ed =d;
if(find(l1,l2))
return true;
l2.st =c;
l2.ed =b;
if(find(l1,l2))
return true;
l2.st =c;
l2.ed =d;
if(find(l1,l2))
return true;
return false;
}
void solve()
{
int t;
point a,b,c,d;
line l;
scanf("%d",&t);
while(t--)
{
scanf("%lf %lf %lf %lf %lf %lf %lf %lf",&l.st.x ,&l.st.y ,&l.ed.x ,&l.ed.y ,&a.x ,&a.y ,&c.x ,&c.y );
//并非按照左上,右下输入,需判断
if(a.x >c.x )
swap(a.x ,c.x );
if(a.y <c.y )
swap(a.y ,c.y );
int flag=0;
//逆时针记录矩形四点
b.x =a.x ,b.y =c.y ;
d.x =c.x ,d.y =a.y ;
//判断线段是否与矩形四条边相交
if(judge(a,b,c,d,l))
flag=1;
//特殊情况:判断线段是否有一端点在矩形内,若果有那么一定相交(可以理解为这是一个实心矩阵)
if((l.st.x >=a.x &&l.st.x <=c.x&&l.st.y>=c.y &&l.st.y<=a.y )||(l.ed.x >=a.x &&l.ed.x <=c.x&&l.ed.y>=c.y &&l.ed.y<=a.y ))
flag=1;
if(flag)
printf("T\n");
else
printf("F\n");
}
}
int main()
{
solve();
return 0;
}
差分约束系统
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e5+10,inf=0x3f3f3f3f;
int a,b,d,n,ml,md,tot=0;
int head[maxn],nex[maxn],to[maxn],weight[maxn];
int dis[maxn],book[maxn],cnt[maxn];
void add(int v,int u,int k){
nex[tot]=head[v];
to[tot]=u;
weight[tot]=k;
head[v]=tot++;
}
bool spfa(int x){
queue<int> q;
memset(dis,inf,sizeof(dis));
memset(book,0,sizeof(book));
book[x]=1;
dis[x]=0;
q.push(x);
cnt[x]++;
while(!q.empty()){
int u=q.front();
q.pop();
book[u]=0;
for(int i=head[u];i!=-1;i=nex[i]){
int v=to[i];
int w=weight[i];
if(dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
if(!book[v]){
cnt[v]++;
book[v]=1;
q.push(v);
if(cnt[v]>n)
return true;
}
}
}
}
return false;
}
int main(){
memset(head,-1,sizeof(head));
memset(cnt,0,sizeof(cnt));
scanf("%d %d %d",&n,&ml,&md);
for(int i=1;i<=ml;i++){
scanf("%d %d %d",&a,&b,&d);
add(a,b,d);
}
for(int i=1;i<=md;i++){
scanf("%d %d %d",&a,&b,&d);
add(b,a,-d);
}
for(int i=1;i<=n;i++){
add(0,i,0);
}
if(spfa(0)){
printf("-1\n");
return 0;
}
spfa(1);
if(dis[n]==inf)
printf("-2\n");
else
printf("%d\n",dis[n]);
return 0;
}
佐威夫博弈
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
double p=(sqrt(5.0)+1)/2.0;//差值*1.618==minn(两堆中较小那堆的数量)
//若相等为奇异局势,先手必败
void solve()
{
double n,m,c;
while(~scanf("%lf %lf",&n,&m))
{
c=(n-m)>0?n-m:m-n;
n=n<m?n:m;
if(int(c*p)==int(n))
printf("0\n");
else
printf("1\n");
}
}
int main()
{
solve();
return 0;
}