good problem-1

1.蜃楼观光
求解式子的转化十分巧妙

2.灵力之泉
树形换根dp
注意vector边界等问题

3.「Nhk R1 C」Zet'ubou Another
一共就2500个障碍
我们可以对nn的图进行转化,
对于每一列把连续的无障碍的区间缩成一个点
对于每个障碍物的纵坐标排序、离散化,把2个障碍物纵坐标之间的区间缩成一个点,最多有2k个点
对于行也同理,这样就转化成了2k
2k的图,暴力搜即可

4.F. Interacdive Problem
交互题
二分判断\(\lfloor \frac{x}{n} \rfloor\)的余数在什么范围,具体实现见代码

点击查看代码
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=500000+10101;
const int MOD=998244353;
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
int n;
int main(){
    n=read();
    int l=1,r=n,d=0;
    while(r>l+1){
        int mid=(r+l)>>1;
        cout<<"+ "<<n-mid<<endl;
        cout.flush();
        int now=read();
        if(d<now)l=mid;
        else r=mid+l;
        l=(l+n-mid)%n;
        r=(r+n-mid)%n;
        if(r==0)r=n;
        d=now;
    }
    cout<<"! "<<l<<endl;
    return 0;
}

5.G. MinOr Tree
考虑贪心
首先考虑最小生成树,此时最大使用的边的最大权值是必须要选的,那么我们考虑把最大权值的最高二进制位k所表示的值\(2^k\)加入答案,并且把所有大于\(2^k\)的边权减去\(2^k\)再进行最小生成树,一直到权值都为0
此外所有大于\(2^{k+1}\)的边权可以不用考虑

点击查看代码
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=500000+10101;
const int MOD=998244353;
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
int t,n,m,f[maxn];
struct wzq{
    int x,y,val;
}a[maxn];
int find(int x){
    if(f[x]==x)return x;
    return f[x]=find(f[x]);
}
bool cmp(wzq i,wzq j){return i.val<j.val;}
int solve(){
    sort(a+1,a+m+1,cmp);
    for(int i=1;i<=n;i++)f[i]=i;
    int last=0,ans=0;
    for(int i=1;i<=m;i++){
        int xx=find(a[i].x),yy=find(a[i].y);
        if(xx!=yy){
            f[xx]=yy;
            while((1<<(ans+1))<=a[i].val)ans++;
            last=i;
        }
    }
    if(a[last].val==0)return -1;
    for(int i=m;i>=1;i--){
        if(a[i].val>=(1<<(ans+1)))m--;
        if(a[i].val>=(1<<ans))a[i].val-=(1<<ans);
        else break;
    }
    return ans;
}
int main(){
    t=read();
    while(t--){
        n=read(),m=read();
        for(int i=1;i<=m;i++){
            a[i].x=read(),a[i].y=read();a[i].val=read();
        }
        int ans=0;
        while(1){
            int now=solve();
            if(now==-1)break;
            ans+=(1<<now);
        }
        printf("%d\n",ans);
    }
    return 0;
}

6.E. Masha-forgetful
dp题,考虑每一个提取的区间一定是长度为2和3的相互组合,这样把所有给定的串的长度为2和3的提取出来,记录区间和位置
dp[i]表示匹配到第i位,最后匹配的串
pre[i]表示由哪里转移的

点击查看代码
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=500000+10101;
const int MOD=998244353;
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}
int t,n,m,pre[1001+101];
string dp[1001+101];
int main(){
    t=read();
    while(t--){
        n=read();m=read();
        memset(dp,0,sizeof(dp));
        map< string, tuple<int,int,int> >mm;
        map< string, bool> book;
        for(int i=1;i<=n;i++){
            string ch;cin>>ch;
            for(int j=1;j<m;j++){
                string a;
                a+=ch[j-1];
                a+=ch[j];
                mm[a]=make_tuple(j,j+1,i);
                book[a]=1;
            }
            for(int j=2;j<m;j++){
                string a;
                a+=ch[j-2];a+=ch[j-1];a+=ch[j];
                mm[a]=make_tuple(j-1,j+1,i);
                book[a]=1;
            }
        }
        for(int i=0;i<=m;i++)pre[i]=-1;
        string ch;cin>>ch;
        string now;now+=ch[0];now+=ch[1];
        if(book[now])dp[2]=now,pre[2]=0;
        now+=ch[2];
        if(book[now])dp[3]=now,pre[3]=0;
        for(int i=4;i<=m;i++){
            string j1,j2;
            for(int j=i-2+1;j<=i;j++)j1+=ch[j-1];
            for(int j=i-2;j<=i;j++)j2+=ch[j-1];
            if(pre[i-2]!=-1 && book[j1]){
                dp[i]=j1;pre[i]=i-2;continue;
            }
            if(pre[i-3]!=-1 && book[j2]){
                dp[i]=j2;pre[i]=i-3;continue;
            }
        }
        vector< tuple<int,int,int> >ans;
        if(pre[m]!=-1){
            while(pre[m]!=-1){
                ans.push_back(mm[dp[m]]);
                m=pre[m];
            }
            printf("%lu\n",ans.size());
            reverse(ans.begin(),ans.end());
            for(auto [i,j,k]:ans){
                printf("%d %d %d\n",i,j,k);
            }
        }
        else printf("-1\n");
    }
    return 0;
}

7.E - MST + 1
题意就是q次询问,每次给出一条边,问这条边能否加入最小生成树里
显然的算法是先求出最小生成树,再倍增维护路径的最大值
但这道题是离线的,可以对q次询问的边权排序
判断就是看小于询问的边权的所有非询问的边构建的生成树,判断当前询问的边的两点是否联通,联通就是No

点击查看代码
 #include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=1e6+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int n,m,q,f[maxn];
int find(int x){
    if(f[x]==x)return x;
    return f[x]=find(f[x]);
}
struct wzq{int x,y,z;}a[maxn];
bool cmp(wzq i,wzq j){return i.z<j.z;}
int tot,head[maxn],nx[maxn],to[maxn],v[maxn];
void add(int x,int y,int z){to[++tot]=y;nx[tot]=head[x];head[x]=tot;v[tot]=z;}
int dp[maxn][20],ff[maxn][20],dep[maxn];
void dfs(int x,int fa){
    for(int i=1;i<20;i++){
        ff[x][i]=ff[ff[x][i-1]][i-1];
        dp[x][i]=max(dp[ff[x][i-1]][i-1],dp[x][i-1]);
    }
    for(int i=head[x];i;i=nx[i]){
        int u=to[i];if(u==fa)continue;
        dp[u][0]=v[i];ff[u][0]=x;dep[u]=dep[x]+1;
        dfs(u,x);
    }
}
int get(int x,int y){
    if(dep[x]>dep[y])swap(x,y);
    int ans=0;
    for(int i=19;i>=0;i--){
        if(dep[ff[y][i]]>dep[x])ans=max(ans,dp[y][i]),y=ff[y][i];
    }
    if(ff[y][0]==x)return max(ans,dp[y][0]);
    if(dep[x]!=dep[y])ans=max(ans,dp[y][0]),y=ff[y][0];
    for(int i=19;i>=0;i--){
        if(ff[y][i]==ff[x][i])continue;
        ans=max(ans,dp[y][i]);
        ans=max(ans,dp[x][i]);
        y=ff[y][i];x=ff[x][i];
    }
    ans=max(ans,dp[x][0]);ans=max(ans,dp[y][0]);
    return ans;
}
int main(){
    n=read();m=read();q=read();  
    for(int i=1;i<=n;i++)f[i]=i; 
    for(int i=1;i<=m;i++){a[i].x=read(),a[i].y=read();a[i].z=read();} 
    sort(a+1,a+m+1,cmp);
    for(int i=1;i<=m;i++){
        int x=a[i].x,y=a[i].y,z=a[i].z;
        int xx=find(f[x]),yy=find(f[y]);
        if(xx==yy)continue;
        f[xx]=yy;add(x,y,z);add(y,x,z);
    }
    dfs(1,0);
    for(int i=1;i<=q;i++){
        int x=read(),y=read(),z=read();
        int vv=get(x,y);
        //cout<<vv<<endl;
        if(x==y || vv<z)printf("No\n");
        else printf("Yes\n");
    }
    return 0;
}

8.F - Variety of Digits
数位dp
dp[i][j][k][s]表示:
i=0,1滚动数组
j=0表示记录次数,j=1表示和
k=0表示当前位有限制,k=1无限制
s以二进制表示当前位所代表的数是否使用

点击查看代码
 #include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=1e4+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
int m,mask;
ll dp[2][2][2][2048];  //
int main(){
    char ch[maxn];cin>>ch;vector<int>a;int lena=strlen(ch);
    for(int i=0;i<lena;i++)a.push_back((int)(ch[i]-'0'));
    m=read();for(int i=1;i<=m;i++){int x=read();mask|=1<<x;}
    for(int i=0;i<lena;i++){
        for(int s=0;s<(1<<10);s++){
            for(int l=0;l<2;l++)dp[i%2][0][l][s]=dp[i%2][1][l][s]=0;
        }
        for(int j=1;j<=(i==0 ? a[i] : 9);j++){ //前导0情况
            dp[i%2][0][i>0 || j<a[i]][1<<j]++;
            dp[i%2][1][i>0 || j<a[i]][1<<j]+=j;
        }
        for(int l=0;l<2;l++){                   //1是无限制
            for(int s=0;s<(1<<10);s++){
                for(int j=0;j<=(l? 9:a[i]);j++){
                    (dp[i%2][0][l || j<a[i]][s | (1<<j)]+=dp[(i+1)%2][0][l][s])%=MOD;
                    (dp[i%2][1][l || j<a[i]][s | (1<<j)]+=dp[(i+1)%2][1][l][s]*10+dp[(i+1)%2][0][l][s]*j)%=MOD;
                }
            }
        }
    }
    ll ans=0;
    for(int l=0;l<2;l++){
        for(int s=0;s<(1<<10);s++){
            if((s&mask)==mask){
                ans+=dp[(lena-1)%2][1][l][s];
                ans%=MOD;
            }
        }
    }
    printf("%lld",ans);
    return 0;
}


9.G - Gardens
首先考虑没有条件1限制,即不要求每个garden有seedlinng
那么就很简单
\(ans=(\sum_{i=0}^aC(n,i)) \cdot(\sum_{i=0}^b C(n,i)) \cdot (\sum_{i=0}^cC(n,i)\)
对于条件1,考虑容斥原理即可

点击查看代码
 #include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<map>
#define ll long long 
#define pa pair<int,int>
using namespace std;
const int maxn=5e6+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
int read(){
    int x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
ll power(ll x,ll y){
    ll ans=1;
    while(y){
        if(y&1)ans=ans*x%MOD;
        y>>=1;x=x*x%MOD;
    }
    return ans;
}
ll n,a,b,c;
ll fac[maxn],inv[maxn];
ll C(int n,int m){
    if(m>n)return 0;
    return (fac[n]*inv[n-m]%MOD)*inv[m]%MOD;
}
int main(){
    n=read();a=read();b=read();c=read();
    fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%MOD;
    inv[n]=power(fac[n],MOD-2);for(int i=n-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%MOD;
    ll ans=0,sa=1,sb=1,sc=1;
    // C(n,m)=C(n-1,m-1)+C(n-1,m)
    // sum C(n+1,0...m)=sum C(n,0...m) + sum C(n,-1....m-1)=2*sum C(n,0....m)-C(n,m)
    for(int i=0;i<=n;i++){  //容斥,枚举有n-i个garden为空
        (ans+=((n-i)&1 ? -1 : 1)*C(n,i)*sa%MOD*sb%MOD*sc%MOD)%=MOD;
        // sa=sum C(i,0....a),sb=sum C(i,0....b),sc=sum C(i,0....c)
        sa=(2*sa%MOD-C(i,a))%MOD;
        sb=(2*sb%MOD-C(i,b))%MOD;
        sc=(2*sc%MOD-C(i,c))%MOD;
    }
    printf("%lld",(ans%MOD+MOD)%MOD);
    return 0;
}

10.D. Binary Spiders
给定n个数和一个数k,要求在n个数中找出一个集合,使得该集合为原数组的子集,且其中的任意两数的异或和至少为k,问最大子集是什么,给出任意一解即可。
首先要明白,这是位运算,所以我们对每一位分别进行考虑。
我们假设k的最高位1的位置在x(二进制表达中的右边开始数)
在二进制表达式中,我们把每个数的第x及以下的位数抹掉,剩下的数相同的我们叫做同一类数。
首先很显然,不同类的任意两数的异或和一定大于k
其次,同一类的两数中,我们最多可以选2个
假如我们选了两个同一类的数,如果要使得他们的异或和大于等于k,那么必然两数的第x位是不同的
知道了以上后,可以用抽屉原理来解释,如果选了三个的话,那么必定存在任意两个同一类的数,使得第x位相同,从而异或和小于k
那么就简单了,首先对所有数分类,这一步用哈希下即可
然后对同一类中,利用trie找找看是否能有两个数的异或和大于等于k即可,有就选上,没有就随便选一个
分别对每类进行上述操作,然后不同类相加,就是最大解
注意k=0的特判,因为任何数相异或大于等于0

点击查看代码
 #include<functional>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<complex>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#include<deque>
#include<map>
#define ll long long 
#define pa pair<ll,int>
using namespace std;
const int maxn=5e6+101;
const int MOD=998244353;
const int inf=2147483647;
const double pi=acos(-1);
ll read(){
    ll x=0,f=1;char ch=getchar();
    for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    return x*f;
}
ll n,k,a[maxn];
int ch[maxn][2],tot,ed[maxn];
void insert(ll x,int pos){
    int p=0;
    for(int i=30;i>=0;i--){
        int now=(x>>i)&1;
        if(!ch[p][now])ch[p][now]=++tot;
        p=ch[p][now];
    }
    ed[p]=pos;
    return ;
}
int find(ll x){
    int p=0,nn=0;
    for(int i=30;i>=0;i--){
        ll now=(x>>i)&1;
        if(ch[p][now^1])p=ch[p][now^1],nn|=((now^1)<<i);
        else {
            p=ch[p][now];
            nn|=(now<<i);
        }
    }
    if((x^nn)>=k)return ed[p];
    return -1;
}
void init(){
    for(int i=0;i<=tot;i++){
        ch[i][0]=ch[i][1]=ed[i]=0;
    }
    tot=0;return ;
}
int main(){
    n=read();k=read();
    for(int i=1;i<=n;i++)a[i]=read();
    if(k==0){
        printf("%ld\n",n);
        for(int i=1;i<=n;i++)printf("%d ",i);
        return 0;
    }
    ll pos=0;
    for(int i=30;i>=0;i--){
        if((1<<i)&k){pos=i;break;}
    }
    pos=(1<<30)-(1<<(pos+1));
    map<ll, vector<pa> >mm;
    for(int i=1;i<=n;i++){
        ll now=a[i]&pos;
        mm[now].push_back(make_pair(a[i],i));
    }   
    vector<int>ans;
    for(auto i: mm){
        init();
        for(auto j: i.second)insert(j.first,j.second);
        bool fa=false;
        for(auto j: i.second){
            ll x1=j.first;int x2=j.second;
            int now=find(x1);
            if(now==-1)continue;
            ans.push_back(x2);ans.push_back(now);
            fa=true;break;
        }
        if(!fa)ans.push_back(i.second[0].second);
    }
    if(ans.size()<=1)printf("-1\n");
    else {
        printf("%lu\n",ans.size());
        for(auto i: ans)printf("%d ",i);
    }
    return 0;
}


posted @ 2022-01-09 14:09  I_N_V  阅读(42)  评论(0编辑  收藏  举报