[补题]2019寒假集训

时间:2021-03-13 09:50:03

慢跑

这题完全是在针对我

还是题意没有充分理解

  • 设跑步者B 跑步如果追上A 那么A速度就降为一样 然后后面的那位C原本追不上可能就追上了
  • 如果B追不上 C不可能追上B

设每组的领跑者的位置和速度 如果他追不上前面那位或者是最后以为则为领跑者

一道模拟题居然卡那么久 哭了

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
#define ll long long
ll a[maxn],b[maxn];
int main()
{
    ll n,t;
    scanf("%lld%lld",&n,&t);
    for(ll i=1; i<=n; ++i)
    {
        scanf("%lld%lld",&a[i],&b[i]);
    }
    ll ans=n;
    ll pre=b[n],pos=a[n];
    for(ll i=n-1; i>=1; --i)
    {
        if(a[i]+b[i]*t>=pos+pre*t)
        {
            ans--;
        }
        else
        {
            pos=a[i];
            pre=b[i];
        }
    }
    printf("%lld\n",ans);
    return 0;
}

迷宫问题

  • DFS求到终点的方案数

很基础的DFS 可能写得太少都忘了

起点的标记要注意

然后每次遇到DFS都有种莫名的恐惧?

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=102;
int s[maxn][maxn];
bool vis[maxn][maxn];
int n,ans;
int dx[]= {0,0,1,-1,1,-1,-1,1};
int dy[]= {1,-1,0,0,1,-1,1,-1};
void dfs(int x,int y){
    for(int i=0; i<8; ++i){
        int xx=x+dx[i];
        int yy=y+dy[i];
        if(xx==1&&yy==n){
            ans++;
            return;
        }
        if(xx>=1&&xx<=n&&yy>=1&&yy<=n&&!s[xx][yy]&&!vis[xx][yy]){
            vis[xx][yy]=true;
            dfs(xx,yy);
            vis[xx][yy]=false;
        }
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1; i<=n; ++i){
        for(int j=1; j<=n; ++j)
            scanf("%d",&s[i][j]);
    }
    vis[1][1]=true;
    dfs(1,1);
    printf("%d\n",ans);
    return 0;
}

星区

对题意坐标的理解…..其实也很好理解

然后就是DFS求连通块

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=55;
int s[maxn][maxn][maxn];
bool vis[maxn][maxn][maxn];
int n;int X,Y,Z,m;
int dx[]={0,0,-1,1,0,0};
int dy[]={1,-1,0,0,0,0};
int dz[]={0,0,0,0,1,-1};
void dfs(int x,int y,int z,int p){
    vis[x][y][z]=true;
    for(int i=0; i<6; ++i){
        int xx=x+dx[i];
        int yy=y+dy[i];
        int zz=z+dz[i];
        if(xx>=1&&xx<=X&&yy>=1&&yy<=Y&&zz>=1&&zz<=Z&&!vis[xx][yy][zz]&&abs(s[xx][yy][zz]-p)<=m)
        {
            dfs(xx,yy,zz,s[xx][yy][zz]);
        }
    }
}
int main(){
   scanf("%d%d%d%d",&X,&Y,&Z,&m);
    for(int i=1;i<=X;++i){
        for(int j=1;j<=Y;++j){
            for(int k=1;k<=Z;++k){
                scanf("%d",&s[i][j][k]);
            }
        }
    }
    int ans=0;
    for(int i=1;i<=X;++i){
        for(int j=1;j<=Y;++j){
            for(int k=1;k<=Z;++k){
                if(!vis[i][j][k]){
                    dfs(i,j,k,s[i][j][k]);
                    ans++;
                }
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

冠军剑士

  • BFS求最短路径

这三个题算是搜索的基础题

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int cx[]={0,0,1,-1,1,-1,1,-1};
int cy[]={1,-1,1,-1,0,0,-1,1};
bool vis[101][101];
int stx,sty,enx,eny;
inline bool check(int x,int y){
    if(x>=1&&x<=n&&y>=1&&y<=m) return true;
    return false;
}
struct node{
    int x,y;
    int step;
}st,en;
int bfs(){
    queue<node>que;
    st.x=stx;
    st.y=sty;vis[st.x][st.y]=true;
    st.step=0;
    que.push(st);
    while(!que.empty()){
        st=que.front();
        que.pop();
        if(st.x==enx&&st.y==eny){
            return st.step;
        }
        for(int i=0;i<4;++i){
            int xx=st.x+dx[i];
            int yy=st.y+dy[i];
            if(!vis[xx][yy]&&check(xx,yy)){
                en.x=xx;
                en.y=yy;
                en.step=st.step+1;
                vis[xx][yy]=1;
                que.push(en);
            }
        }
    }
    return 0;
}
int main() {
    int t;
    scanf("%d%d%d%d%d%d%d",&n,&m,&stx,&sty,&enx,&eny,&t);
    int u,v;
    for(int i=1;i<=t;++i){
        scanf("%d%d",&u,&v);
        vis[u][v]=1;
        for(int j=0;j<8;++j){
            int xx=u+cx[j];
            int yy=v+cy[j];
            if(check(xx,yy)){
                vis[xx][yy]=1;
            }
        }
    }
    printf("%d\n",bfs());
    return 0;
}

帝国交通

题意要求几个需要联系的王国之间建立最短的道路,然后道路只能建在相邻的两点,并且是环.

数组开两倍然后断环为链 这是基本操作

因为有些区间正更优,有些反过来优,通过枚举链上的起点,这样就可以枚举到所有的情况

左端点+1 右端点-1 for一遍就让res不停加上该位置的值,如果不是0则证明这段需要建路

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=20005;
const int maxm=2000;
struct node
{
    int l,r;
} s[maxn];
int f[maxm];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; ++i)
    {
        scanf("%d%d",&s[i].l,&s[i].r);
        if(s[i].l>s[i].r) swap(s[i].l,s[i].r);
    }
    int ans=n;
    for(int i=0; i<n; ++i){
        int st=1+i;
        int en=n+i;
        int res=0,tmp=0;
        memset(f,0,sizeof(f));
        for(int j=1;j<=m;++j){
            if(s[j].l>=st){
                f[s[j].l]++;
                f[s[j].r]--;
            }
            else if(s[j].l<st&&s[j].r<st){
                f[s[j].l+n]++;
                f[s[j].r+n]--;
            }
            else if(s[j].l<st&&s[j].r>=st){
                f[s[j].l+n]--;
                f[s[j].r]++;
            }
        }
        for(int j=st; j<=en; ++j){
            res+=f[j];
            if(res>0){
                tmp++;
            }
        }
        ans=min(tmp,ans);
    }
    printf("%d\n",ans);
    return 0;
}

矩阵快速幂

设矩阵\(A\)
\[ S=A^1+A^2+A^3+....+A^n \]
直接用矩阵快速幂然后加起来肯定超时

这题可以用等比数列求和的方法 但是矩阵不能除 算逆阵有点麻烦 

把这个矩阵拆成两个部分 就是
\[ S=A^1+A^2+A^3+....+A^{n/2}+A^{n/2}*(A^1+A^2+A^3+....+A^{n/2}) \]
然后通过递归 奇数再加上\(A^{n/2+1}\)就好了

matrix expmod(matrix a,int n)//快速幂
{
    e.init();
    while (n)
    {
        if(n & 1) e=e*a;
        a=a*a;
        n>>=1;
    }
    return e;
}
matrix solve(int k)//二分
{
    if(k==1) return a;
    c=solve(k>>1);
    matrix ans=c+c*expmod(a,(k>>1));
    if(k & 1) ans=ans+expmod(a,k);
    return ans;
}

也可以通过构造嵌套矩阵的方法来求
\[ \begin{matrix} A&A \\ 0&E \\ \end{matrix} \tag{1} \]

发财兔序列

注意审题:非降序列

记下每种数的起点 每个位置属于哪种树

查询时两边的数可以通过其到起点的距离来得出

而中间的数则转化为查询区间最大值

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+1;
int stmax[maxn][20],stmin[maxn][20];
int poww[25],logg[maxn];
int n,q;
int findll[maxn];
int ll[maxn];
int a[maxn],cnt[maxn];
void init(){
    poww[0]=1;
    for(int i=1; i<=20; i++)//预处理次方
    {
        poww[i]=poww[i-1]<<1;
    }
    for(int i=2; i<=n; i++)
    {
        logg[i]=logg[i>>1]+1;
    }
    int temp=1;
    for(int j=1; j<=logg[n]; j++)//temp=2^(j-1)
    {
        for(int i=1; i<=n-temp-temp+1; i++)
        {
            stmax[i][j]=max(stmax[i][j-1],stmax[i+temp][j-1]);
        }
        temp<<=1;
    }
}
inline int query_max(int l,int r){
    if(r<l) return 0;
    int len=r-l+1;
    int k=logg[len];
    return max(stmax[l][k],stmax[r-poww[k]+1][k]);
}
int main(){
    int num;n=0;
    scanf("%d",&num); scanf("%d",&q);
    for(int i=1;i<=num;++i){
        scanf("%d",&a[i]);
        if(a[i-1]!=a[i]||i==1){
            n++;
            cnt[n]++;
            findll[i]=n;
            ll[n]=i;
        }
        else{
            cnt[n]++;
            findll[i]=n;
        }
    }
    for(int i=1;i<=n;++i){
        stmax[i][0]=cnt[i];
    }
    init();int u,v;
 
    while(q--){
        scanf("%d%d",&u,&v);
        if(findll[u]==findll[v]){
            printf("%d\n",v-u+1);
        }
        else{
            int tmp=max(cnt[findll[u]]-u+ll[findll[u]],v-ll[findll[v]]+1);
            printf("%d\n",max(tmp,query_max(findll[u]+1,findll[v]-1)));
        }
    }
    return 0;
}

发财兔fbi树

  • 后序遍历:先左后右再到根

有点像线段树的递归

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e6+7;
char s[1028];
void gao(int l,int r){
    if(r>l){
        gao(l,(l+r)>>1);
        gao(((l+r)>>1)+1,r);
    }
    int fo=0,tmp=0;
    for(int i=l;i<r;++i){
        if(s[i]!=s[i+1]){
            fo=1;
        }
    }
    if(fo){
        cout<<'F';
    }
    else if(!fo&&s[l]=='0'){
        cout<<'B';
    }
    else if(!fo&&s[l]=='1'){
        cout<<'I';
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;cin>>n;
    cin>>s+1;
    int tmp=1<<n;
    gao(1,tmp);
    return 0;
}

滑动窗口

强行用ST表水过去

实际上应该用单调队列

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e6+7;
int s[maxn];int q[maxn],p[maxn],k;
void solve_max(int n){
    int head=1;
    int tail=0;
    for(int i=1;i<=n;++i){
        while(head<=tail&&q[tail]<=s[i]) tail--;
        q[++tail]=s[i];
        p[tail]=i;
        while(p[head]<=i-k)head++;
        if(i>=k)printf("%d ",q[head]);
    }
    printf("\n");
}
void solve_min(int n){
    int head=1;
    int tail=0;
    for(int i=1;i<=n;++i){
        while(head<=tail&&q[tail]>=s[i]) tail--;
        q[++tail]=s[i];
        p[tail]=i;
        while(p[head]<=i-k)head++;
        if(i>=k)printf("%d ",q[head]);
    }
    printf("\n");
}
int main(){
    int n;scanf("%d%d",&n,&k);
    for (int i = 1; i <=n ; ++i) {
        scanf("%d",&s[i]);
    }
    solve_min(n);
    memset(p,0,sizeof(p));
    memset(q,0,sizeof(q));
    solve_max(n);
    return 0;
}

发财兔求和

推公式,找出重复计算的部分优化

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000+1;
const int mod = 10007;
struct node{
    ll x;
    ll val;
}s[maxn];
ll dp[2][maxn];
ll num[2][maxn];
int main(){
    ll n,m;scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;++i) {
        scanf("%lld", &s[i].val);
    }
    for(ll i=1;i<=n;++i){
        scanf("%lld",&s[i].x);
        dp[i%2][s[i].x]=(s[i].val+dp[i%2][s[i].x])%mod;
        num[i%2][s[i].x]=(num[i%2][s[i].x]+1)%mod;
    }
    ll ans=0;
    for(int i=1;i<=n;++i){
        ans=(ans+i*((num[i%2][s[i].x]-2)*s[i].val%mod+dp[i%2][s[i].x])%mod)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}

有趣的数

  • \(n\)是10的倍数的时候,位置一定是 $\log_{10}k $,如果不是的话就返回0
  • 当n不是10的倍数:
    • 如果位置小于n为最大数时n的位置,返回0
    • 如果位置等于n为最大数时n的位置,返回n
    • 大于的话,就让len++,让n增大,模拟找到合适的位置
#include<iostream>
#include <bits/stdc++.h>
using namespace std;
#define ll long long
string a;
ll pos,s[10];
ll gao(){
    int len=(int)a.length();
    ll ans=0,p=0,log=1;
    for(int i=0;i<len;++i){
        s[i]=a[i]-'0';
        p=p*10ll+s[i];
        ans+=p-log;
        log*=10ll;
        if(i!=len-1) ans++;
    }//ans为比n小的有多少
    if(ans+1==pos)return p;
    if(p*10==log)return 0;
    if(pos<ans+1) return 0;
    while(1){
        p*=10ll;
        if(ans+1+p-log>=pos){
            return log+pos-ans-2;
        }//pos-(ans+1)是差多少位 加上log-1就是答案
        ans+=p-log;
        log*=10ll;
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;cin>>t;
    while(t--){
        cin>>a>>pos;
        cout<<gao()<<endl;
    }
    return 0;
}

matrix

神奇的暴力瞎搞

行与行之间可以相互交换 枚举列 那么每行只用在意当前列有没有1 1最多能延伸多少

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=5000+5;
bool arr[maxn][maxn];
int cnt[maxn];
int ans;char str;
int pos[maxn];int n,m;
inline void gao(){
    for(register int i=m;i>=1;--i){
        ans=max(ans,i*cnt[i]);
        cnt[i-1]+=cnt[i];
        cnt[i]=0;
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            cin>>str;
            if(str=='0')arr[i][j]=0;
            else arr[i][j]=1;
        }
    }
    for(register int j=1;j<=m;++j){
        for(register int i=1;i<=n;++i){
            if(!arr[i][j]) continue;
            if(pos[i]<j)pos[i]=j;
            while(pos[i]<=m&&arr[i][pos[i]+1]) pos[i]++;
            cnt[pos[i]-j+1]++;
        }
        gao();
    }
    cout<<ans<<endl;
    return 0;
}

发财兔1C

[l,r]内的卡片所有出现了偶数次的数的异或和是多少

可以转化为区间不同的数的个数与区间所有数的异或和

因为如果一个数出现偶数次 那么这个数异或和为0,奇数则为原来的数

这样就会把原序列中奇数个数的数都去掉

区间不同数通过离线查询,记录这个数最后出现的位置,然后树状数组来实现

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e6+5;
int pre_sum[maxn],unq_sum[maxn],n,q;
inline int lowbit(int x){
    return x&(-x);
}
ll getsum(int pos){
   ll res=0;
   for(ll i=pos;i>0;i-=lowbit(i)){
     res^=unq_sum[i];
   }
   return res;
}
void add(int pos,int val){
   for(int i=pos;i<=n;i+=lowbit(i)){
       unq_sum[i]^=val;
   }
}
struct node{
   int l,r,id;
   bool operator<(const node &c)const{
      return r<c.r;
   }
}s[maxn];
struct find_last{
   int val,id;
   bool operator<(const find_last &c)const{
      if(val==c.val){
        return id<c.id;
      }
      else return val<c.val;
   }
}copy_num[maxn];
int num[maxn];
int last[maxn];
int ans[maxn];
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i){
        scanf("%d",&num[i]);
        copy_num[i].val=num[i];
        copy_num[i].id=i;
        pre_sum[i]=pre_sum[i-1]^num[i];
    }
    for(int i=1;i<=q;++i){
        scanf("%d%d",&s[i].l,&s[i].r);
        s[i].id=i;
    }
    sort(s+1,s+1+q);int pos=1;
    sort(copy_num+1,copy_num+n+1);
    for(int i=2;i<=n;++i){
        if(copy_num[i].val==copy_num[i-1].val){
            last[copy_num[i].id]=copy_num[i-1].id;
        }
    }
    for(int z=1;z<=q;++z){
        while(pos<=s[z].r){
            if(last[pos]) add(last[pos],num[pos]);
            add(pos,num[pos]);
            pos++;
        }
        ans[s[z].id]=getsum(s[z].r)^getsum(s[z].l-1)^pre_sum[s[z].r]^pre_sum[s[z].l-1];
    }
    for(int i=1;i<=q;++i){
        printf("%d\n",ans[i]);
    }
    return 0;
}

桥桥的告白

审题 充分理解题意

记录过程 然后向上递归查询a喜欢b b喜欢c c喜欢d之类的

找到最原始被喜欢的那个人

#include<string>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
#define ll long long
const int maxn=1005;
const int maxm=300000+1;
int pos[maxm][2];
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;cin>>n;
    char s[maxn][25];
    for(register int i=1;i<=n;++i){
        cin>>s[i];
    }
    int m;cin>>m;
    for(register int i=1;i<=m;++i){
        cin>>pos[i][0]>>pos[i][1];
    }
    int pre=1;
    for(register int i=m;i>=1;--i){
        if(pos[i][0]==pre){
            pre=pos[i][1];
            cout<<"I_love_";
        }
    }
    cout<<s[pre]<<endl;
    return 0;
}

菱菱的旅途

贪心策略

左右端点 先缩小的那个端点 然后找到最大值

因为缩小小的必然优于缩大的 然后就可以不重不漏找到枚举所有可能情况

#include<string>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
#define ll long long
const int maxn=1000000+5;
ll s[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1; i<=n; ++i) scanf("%lld",&s[i]);
    ll l=1,r=n;
    if(n==1) printf("%lld\n",s[1]);
    else
    {
        ll ans=min(s[1],s[n])*n;
        while(l<=r)
        {
            if(s[l]<=s[r]) l++;
            else if(s[l]>s[r])r--;
            ans=max(min(s[l],s[r])*(r-l+1),ans);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

鑫鑫的算术

跟a and b和a or b有关的题要联想到二进制

可以发现这个单纯是二进制1的重组 如何让1重组成最大的数 当然是每位尽量都是1

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod=998244353;
int wei[35];
ll p_pow[35];
void count_one(int n){
    int res=0;
    while(n){
        if(n&1) wei[res]++;
        n>>=1;
        res++;
    }
}
void init(){
    p_pow[0]=1;
    for(int i=1;i<=32;++i){
       p_pow[i]=p_pow[i-1]*2ll;
    }
}
ll gao(){
    bool flag=0;
    ll res=0;
    for(int i=32;i>=0;--i){
        if(wei[i]){
            flag=1;
            res=res+p_pow[i];
            wei[i]--;
            if(res>=mod) res-=mod;
        }
    }
    if(flag==0) return -1;
    return res;
}
int main(){
    int n;scanf("%d",&n);
    int a;
    init();
    for(int i=1;i<=n;++i){
        scanf("%d",&a);
        count_one(a);
    }
    ll ans=0;
    ll tmp=gao();
    while(tmp!=-1){
        ans+=tmp*tmp%mod;
        if(ans>=mod) ans-=mod;
        tmp=gao();
    }
    printf("%lld\n",ans%mod);
    return 0;
}

排队

由题意可知,可以分成很多个链.枚举x所在的链前面有多少链,然后得到可能的位置

可以用dp来求,也可以用bitset这个骚操作

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 1005;
int n,x,fa[maxn];
bool vis[maxn];
int main(){
    scanf("%d%d",&n,&x);
    for(int i=1;i<=n;++i){
        scanf("%d",&fa[i]); vis[fa[i]]=true;
    }int res,p,flag;
    vector<int>vt;
    for(int i=1;i<=n;++i){
        if(vis[i])continue;
        else{
            res=0;p=i;flag=0;
            while(p){
                res++;
                flag|=(x==p);
                p=fa[p];
            }
            if(!flag) vt.push_back(res);
        }
    }
    bitset<maxn>dp; dp[0]=1;
    for(auto x:vt)  dp|=(dp<<x);
    p=x;int c=0;
    while(p){
        c++;p=fa[p];
    }
    for(int i=0;i<=n;++i){
        if(dp[i]) printf("%d\n",i+c);
    }
    return 0;
}

DAVE打扑克

两堆合并用并查集

虽然堆数可能很多,但是不同数量堆最多有1e3个

cnt[i]记录牌数为i的有多少个

相同牌数的贡献是cnt[i] * (cnt[i]-1)/2

不同牌数的贡献是cnt[i] * cnt[j]

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+7;
ll n,m;
ll fa[maxn];
set<ll>st;
ll val[maxn];
ll cnt[maxn];
ll que[maxn*5];
ll Size;
ll findfa(ll x){
   if(fa[x]==x) return x;
   else return fa[x]=findfa(fa[x]);
}
void add(ll u,ll v){
   ll f1=findfa(u);
   ll f2=findfa(v);
   if(f1!=f2){
       cnt[val[f1]]--;cnt[val[f2]]--;
       if(!cnt[val[f1]]) st.erase(val[f1]);
       if(!cnt[val[f2]]) st.erase(val[f2]);
       fa[f1]=f2;
       val[f2]+=val[f1];
       cnt[val[f2]]++;
       st.insert(val[f2]);
       Size--;
   }
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;++i){
        fa[i]=i;
        val[i]=1;
    }
    cnt[1]=n;
    st.insert(1);
    ll op,pos,u,v;
    Size=n;
    for(ll i=1;i<=m;++i){
        scanf("%lld",&op);
        if(op==1){
            scanf("%lld%lld",&u,&v);
            add(u,v);
        }
        else{
            scanf("%lld",&pos);
            set<ll>::iterator it=st.begin();
            ll tail=0,head=0,pre=0;
            ll ans=0;
            while(it!=st.end()){
                while(head<tail&& *it-que[head]>=pos){
                    pre-=cnt[que[head]];
                    head++;
                }
                ans+=cnt[*it]*pre;
                if(pos>0) ans+=(cnt[*it]*(cnt[*it]-1))/2;
                que[tail++]=*it;
                pre+=cnt[*it];
                it++;
            }
            printf("%lld\n",Size*(Size-1)/2-ans);
        }
    }
    return 0;
}

You Like the Cake

对于100的数据,N≤40,X,Vi≤109

背包容量过大采用折半之后二进制枚举子集,再二分查找

upper_bound返回的是第一个大于查找的数的位置

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[25],b[25];
ll s[(1<<21)+1],f[(1<<21)+1];
int main(){
    int n;
    ll m;scanf("%d%lld",&n,&m);
    for(int i=1;i<=n;++i){
        if(i<=n/2){
            scanf("%lld",&a[i]);
        }
        else scanf("%lld",&b[i-n/2]);
    }
    int k=n/2;
    int pos=0;
    for(int i=0;i<(1<<k);++i){
        for(int j=1;j<=k;++j){
            if(i&(1<<(j-1))){
                s[pos]+=a[j];
            }
        }
        pos++;
    }
    int len=(int)(unique(s,s+pos)-s);
    sort(s,s+len);
    pos=0;k=n-k;
    for(int i=0;i<(1<<k);++i){
        for(int j=1;j<=k;++j){
            if(i&(1<<(j-1))){
                f[pos]+=b[j];
            }
        }
        pos++;
    }
    ll ans=0;
    int op;
    for(int i=0;i<pos;++i){
            if(m<f[i]) continue;
            op=upper_bound(s,s+len,m-f[i])-s;
            op--;
            if(op>=0){
               ans=max(ans,s[op]+f[i]);
            }
    }
    printf("%lld\n",m-ans);
    return 0;
}

刚开始感觉是组合数学…没想到是跟喝汤差不多的一道DP.

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
#define ll long long
ll dp[maxn][4][2];
const int mod=1e9+7;
void init(ll s){
    memset(dp,0,sizeof(dp));
    dp[1][1][0]=s;
}
int main(){
    int t;scanf("%d",&t);
    while(t--) {
        ll n, s;
        scanf("%lld%lld", &n, &s);
        init(s);
        for(int i=2;i<=n;++i){
            dp[i][1][0]=(dp[i-1][1][0]*(s-1)%mod+dp[i-1][2][0]*(s-1)%mod)%mod;
            dp[i][2][0]=dp[i-1][1][0];
            dp[i][3][1]=dp[i-1][2][0];
            dp[i][1][1]=((dp[i-1][1][1]*(s-1)%mod+dp[i-1][2][1]*(s-1)%mod)%mod+dp[i-1][3][1]*(s-1)%mod)%mod;
            dp[i][2][1]=dp[i-1][1][1];
        }
        ll res=0;
        res=((dp[n][1][1]+dp[n][2][1])%mod+dp[n][3][1])%mod;
        printf("%lld\n",res);
    }
    return 0;
}

五子棋

模拟 斜线那个注意一下.

#include <bits/stdc++.h>
using namespace std;
int mapp[250][250];
bool check_updown(int x,int y)
{
    int sum=1;
    for(int i=x+1;i<=225;++i){
        if(mapp[x][y]==mapp[i][y]) sum++;
        else break;
    }
    for(int i=x-1;i>=1;--i){
        if(mapp[x][y]==mapp[i][y]) sum++;
        else break;
    }
    if(sum>=5) return 1;
    return 0;
}
bool check_rightle(int x,int y)
{
    int sum=1;
    for(int i=y+1;i<=225;++i){
        if(mapp[x][y]==mapp[x][i]) sum++;
        else break;
    }
    for(int i=y-1;i>=1;--i){
        if(mapp[x][y]==mapp[x][i]) sum++;
        else break;
    }
    if(sum>=5) return 1;
    return 0;
}
bool check_rightxie(int x,int y)
{
   int sum=1;
    for(int i=1;x+i<=225&&y+i<=225;++i){
        if(mapp[x][y]==mapp[x+i][y+i]) sum++;
        else break;
    }
    for(int i=1;x-i>=1&&y-i>=1;++i){
        if(mapp[x][y]==mapp[x-i][y-i]) sum++;
        else break;
    }
    if(sum>=5) return 1;
    return 0;
}
bool check_leftxie(int x,int y)
{
   int sum=1;
    for(int i=1;x+i<=225&&y-i>=1;++i){
        if(mapp[x][y]==mapp[x+i][y-i]) sum++;
        else break;
    }
    for(int i=1;x-i>=1&&y+i<=225;++i){
        if(mapp[x][y]==mapp[x-i][y+i]) sum++;
        else break;
    }
    if(sum>=5) return 1;
    return 0;
}
int check_all(int x,int y)
{
    if(check_leftxie(x,y)||check_updown(x,y)||check_rightle(x,y)||check_rightxie(x,y))
    {
        return 1;
    }
    return 0;
}
int main()
{
    int t;
    scanf("%d",&t);
    int x,y;
    int flag=0;
    for(int i=1; i<=t; ++i)
    {
        scanf("%d%d",&x,&y);
        if(flag) continue;
        if(i%2==1)
        {
            mapp[x][y]=1;
        }
        else
        {
            mapp[x][y]=2;
        }
        if(check_all(x,y))
        {
            flag=i;
        }
    }
    if(!flag) puts("Tie");
    else
    {
        if(flag&1) printf("A ");
        else printf("B ");
        printf("%d\n",flag);
    }
    return 0;
}

凉宫春日的忧郁

取对数…..

#include <bits/stdc++.h>
const double eps=1e-6;
using namespace std;
 
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        double n,m;
        cin>>n>>m;
        double a=1,b=0;
        a=m*log10(n);
        for(int i=1; i<=m; ++i)
        {
            b+=log10(i);
        }
        if(a-b>=eps)
        {
            puts("No");
        }
        else puts("Yes");
    }
    return 0;
}

Back and Forth

想练一下DFS 结果题意被别人误导…..

#include <bits/stdc++.h>
const double eps=1e-6;
using namespace std;
int a[12],b[12],ans;
int tmp;
map<int,int>mp;
int sum;
void dfs(int now,int p,int pre){
     if(now==1){
       tmp=ans;
       tmp-=a[p];
       b[11]=a[p];
       a[p]=0;
       for(int j=1;j<=11;++j){
        if(b[j])dfs(2,j,p);
       }
       a[p]=b[11];
     }
     if(now==2){
        tmp+=b[p];
        a[pre]=b[p];
        b[p]=0;
        for(int i=1;i<=10;++i){
            if(a[i])dfs(3,i,p);
        }
        b[p]=a[pre];
        tmp-=b[p];
     }
     if(now==3){
        tmp-=a[p];
        b[pre]=a[p];
        a[p]=0;
        for(int i=1;i<=11;++i){
            if(b[i])dfs(4,i,p);
        }
        a[p]=b[pre];
        tmp+=a[p];
     }
     if(now==4){
        if(!mp[tmp+b[p]]){
            mp[tmp+b[p]]=1;
            sum++;
        }
     }
}
int main(){
    for(int i=1;i<=10;++i){
        scanf("%d",&a[i]);
    }
    ans=1000;
    for(int i=1;i<=10;++i){
        scanf("%d",&b[i]);
    }
    for(int i=1;i<=10;++i){
        dfs(1,i,0);
    }
    printf("%d",sum);
    return 0;
}

[DP]烤乐滋喝汤

一个很简单的DP.就是有一种情况他可能前面的都不管然后直接下药没考虑到.

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mp make_pair
#define met(a,x) memset(a,x,sizeof(a))
using namespace std;
const int maxn=1e5+7;
ll dp[maxn][2];
const int inf=0x3f3f3f3f;
int main()
{
    ll n,m;
    scanf("%lld%lld",&n,&m);
    ll ans=-inf;ll s;
    memset(dp,0,sizeof(dp));
    for(int i=1; i<=n; ++i){
        scanf("%lld",&s);
        if(i==1){
            dp[i][0]=s;
            dp[i][1]=max(dp[i-1][0]+m,m);
        }
        else{
            dp[i][0]=max(s,dp[i-1][0]+s);
            dp[i][1]=max(max(dp[i-1][0]+m,dp[i-1][1]+s),m);
        }
        ans=max(ans,dp[i][1]);
    }
    printf("%lld\n",ans);
    return 0;
}

[取模运算]阿卡吃馅饼

一直想着怎么把数弄得很大,忘记取摸这个操作

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+7;
ll qpow(ll a,ll n,ll mod){
    ll res=1;
    while(n){
        if(n&1){
            res*=a;
            res%=mod;
        }
        n>>=1;
        a=(a*a)%mod;
    }
    return res;
}
 
int main(){
    int n;scanf("%d",&n);
    while(n--) {
        int t, r;
        scanf("%d%d", &t, &r);
        int ans=0,i;
        for(i=0;i<=1000;++i){
            ans=(ans+qpow(10,i,r)*t%r)%r;
            if(ans==0) break;
        }
        if(i==1001){
            puts("GG");
        }
        else printf("%d\n",i);
    }
}

[求回文串个数]大战幻想珠

两种情况

一个是从奇数出发,一个是从偶数出发

当出现?直接相等就好

#include <bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define ull unsigned long long
#define mp make_pair
#define met(a,x) memset(a,x,sizeof(a))
using namespace std;
const int maxn=1e5+7;
string s;
int n;
 ll gao(){
        ll ans = 0;
        for (register int i=0; i<n;++i){
            for (register int j=0; i+ j <n && i-j>=0 ; ++j){
            if(s[i-j]==s[i+j]||s[i-j]=='?'||s[i+j]=='?')
                ans++;
                else break;
            }
            for (register int j=0; i + j <n && i-1-j>=0; ++j){
                if(s[i-1-j]==s[i+j]||s[i-1-j]=='?'||s[i+j]=='?')
                ans++;
                else break;
            }
        }
        return ans;
}
int main(){
     cin>>n;cin>>s;
     cout<<gao()<<endl;
    return 0;
}

[暴力枚举]烤乐滋打虎

#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mp make_pair
#define met(a,x) memset(a,x,sizeof(a))
using namespace std;
int s[101];
int d[101][101];
const int inf=0x3f3f3f3f;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1; i<=n; ++i)
    {
        scanf("%d",&s[i]);
    }
    for(int i=2; i<=n; ++i)
    {
        scanf("%d",&d[i][i-1]);
        d[i-1][i]=d[i][i-1];
    }
    int ans=inf,tmp;
    for(int i=1; i<=n; ++i)
    {
        int t=0;
        tmp=0;
        if(i!=n){
            for(int j=i; j<=n; ++j){
                tmp+=s[j]+t;
                t+=d[j][j+1];
            }
            int p=i-1;
            t*=2;
            t+=d[p][i];
            for(int j=p; j>=1; --j){
                tmp+=s[j]+t;
                t+=d[j][j-1];
            }
            ans=min(ans,tmp);
            //cout<<ans<<" "<<i<<endl;
        }
        if(i!=1){
            t=0;
            tmp=0;
            for(int j=i; j>=1; --j){
                tmp+=s[j]+t;
                t+=d[j][j-1];
            }
            t*=2;
            t+=d[i][i+1];
            for(int j=i+1; j<=n; ++j){
                tmp+=s[j]+t;
                t+=d[j][j+1];
            }
            ans=min(ans,tmp);
            //cout<<ans<<" "<<i<<endl;
        }
    }
    printf("%d\n",ans);
    return 0;
}

旅程

先删掉一条边(赋值为INF)

跑FLOYD 记录询问 从后往前 

遇到删边操作则枚举起点和终点\(n^2\)暴力更新答案

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=205;
int d[maxn][maxn];
int dis[maxn][maxn];
int n,m;
void floyd(){
   for(int k=1;k<=n;++k){
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
        }
    }
   }
}
struct node{
  int u,v,op,val;
}s[100000+1];
void gao(int k){
    int u=s[k].u;
    int v=s[k].v;
    int val=min(d[u][v],dis[u][v]);
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            d[i][j]=min(d[i][j],d[i][u]+val+d[v][j]);
        }
    }
}
int vis[maxn][maxn];
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            scanf("%d",&dis[i][j]);
            d[i][j]=dis[i][j];
        }
    }
    for(int i=1;i<=m;++i){
        scanf("%d%d%d",&s[i].op,&s[i].u,&s[i].v);
        if(s[i].op==1){
            s[i].val=d[s[i].u][s[i].v];
            d[s[i].u][s[i].v]=1e9;
            vis[s[i].u][s[i].v]++;
        }
    }
    floyd();
    vector<int>vt;
    for(int i=m;i>=1;--i){
        if(s[i].op==2){
            vt.push_back(d[s[i].u][s[i].v]);
        }
        else {
            --vis[s[i].u][s[i].v];
            if(!vis[s[i].u][s[i].v]){
                gao(i);
            }
        }
    }
    int len=(int)vt.size();
    for(int i=len-1;i>=0;--i){
        if(vt[i]>=1e9) puts("-1");
         else  printf("%d\n",vt[i]);
    }
    return 0;
}

子序列

数组数组求逆序数模板题

记住相同数的处理和相同和离散化

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int n;
const int maxn=2e5+7;
ll c[maxn];
const int mod=1e9+7;
int lowbit(int i) {return i&(-i);}
ll pre_min[maxn],pre_max[maxn];
ll las_min[maxn],las_max[maxn];
int num[maxn];
int b[maxn];
ll sum(int x){
    ll res=0;
    for(int i=x;i>0;i-=lowbit(i)){
        res+=c[i];
    }
    return res;
}
void add(int x,ll val){
    for (int i =x; i <=n ; i+=lowbit(i)) {
        c[i]+=val;
    }
}
struct node{
    ll val;
    int id;
}s[maxn];
bool cmp(node a,node b){
    if(a.val==b.val) return a.id<b.id;
    return a.val<b.val;
}
int main() {
        scanf("%d",&n);
        for(int i=1;i<=n;++i){
            scanf("%lld",&s[i].val);
            c[i]=0;
            s[i].id=i;
        }
        sort(s+1,s+1+n,cmp);
        b[s[1].id]=1;
        for(int i=2;i<=n;++i){
            if(s[i].val!=s[i-1].val)b[s[i].id]=b[s[i-1].id]+1;
            else if(s[i].val==s[i-1].val)b[s[i].id]=b[s[i-1].id];
        }
        for(int i=1;i<=n;++i){
            add(b[i],1);
 
             pre_max[i]=i-sum(b[i]);
             pre_min[i]=(i-1-num[b[i]])-pre_max[i];
             num[b[i]]++;
        }
        reverse(b+1,b+1+n);
        memset(c,0,sizeof(c));
        memset(num,0,sizeof(num));
        for(int i=1;i<=n;++i){
              add(b[i],1);
             las_max[n-i+1]=sum(n)-sum(b[i]);
             las_min[n-i+1]=(i-1-num[b[i]])-las_max[n-i+1];
             num[b[i]]++;
        }
        ll ans=0;
        for(int i=2;i<n;++i){
            ans=(ans+pre_max[i]*las_min[i]%mod)%mod;
            ans=(ans+pre_min[i]*las_max[i]%mod)%mod;
           // cout<<i<<" "<<pre_max[i]<<" "<<las_min[i]<<endl;
           // cout<<i<<" "<<pre_min[i]<<" "<<las_max[i]<<endl;
        }
        printf("%lld\n",ans);
    return 0;
}