codeforces每日一题1

目录:

1.1093D. Beautiful Graph(DFS染色)

2.514C - Watto and Mechanism(Tire)

3.69E.Subsegments(STL)

4.25C. Roads in Berland(最短路松弛操作)

5.128B. String Problem (floyd预处理)

6.33C.Wonderful Randomized Sum(思维)

7.1292B.Aroma's Search (暴力+思维)

8.1286 A.Garland(DP)

1093D. Beautiful Graph(DFS染色)

题意:

给你一个一个n个顶点,m条边的无向图,你可以将顶点赋值为1,2,3,现在问你有多少种赋值方案,使得任意一条边的两个顶点权值和为奇数

思路:

由于权值和要为奇数,所有一条边的两个顶点要一个为奇数,一个为偶数,所以就变成了一个染色问题

要注意图并不一定联通,所以答案为每个连通块内(2^(奇数)+2^(偶数))的乘积和-可以由组合数进行推导

如果有任意一个块无法染色,则答案为0

#include<iostream>
#include<algorithm>
#include<vector>
 using namespace std;
 typedef long long ll;
 const ll mod=998244353;
 const int maxn=3e5+10;
 vector<int> a[maxn];
 int num1,num2,color[maxn],n=0,flag,two[maxn];
 void pre_process()
 {
     two[0]=1;
     for(int i=1;i<maxn;i++)
         two[i]=(2*two[i-1])%mod;
 }
 void dfs(int x,int fa,int co)
 {
     if(!flag) return;
     color[x]=co;
     if(co==1) num1++;
     else num2++;
     for(int i=0;i<a[x].size();i++){
        int to=a[x][i];
        if(color[to]==color[x])
        {
            flag=0;return ;
        }
        if(to!=fa&&color[to]==0)
        {
            if(co==1) dfs(to,x,2);
            else dfs(to,x,1);
        }
     }
 }
 int main()
 {
     int t,m,u,v;
     ll ans=0;
     scanf("%d",&t);
     pre_process();
     while(t--){
         for(int i=1;i<=n;i++) a[i].clear(),color[i]=0;
         num1,num2,ans=1;
         flag=1;
         scanf("%d%d",&n,&m);
         for(int i=1;i<=m;i++){
             scanf("%d%d",&u,&v);
             a[u].push_back(v);
             a[v].push_back(u);
         }
        for(int i=1;i<=n;i++){
            if(!flag) break;
            if(!color[i]){
                num1=num2=0;
                dfs(i,-1,1);
                ans*=(two[num1]+two[num2])%mod;
                ans%=mod;
            }
        }
        if(!flag) cout<<0<<endl;
        else cout<<ans%mod<<endl;
     }
     return 0;
 }
View Code

514C - Watto and Mechanism(Tire)

题意:

给n个模式串,m个匹配串,问是否有只与匹配串相差一个字符的模式串

思路:

直接上Tire,正常插入模式串

匹配时往该节点的儿子节点进行正常匹配,若能匹配成功,就输出YES

#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
 using namespace std;
 const int maxn=1e6+10;
 int n,m;
 struct Tire{
     int ch[maxn][3],val[maxn],cnt;
     void init()
     {
         cnt=1;
         memset(ch,0,sizeof(ch));
         val[0]=0;
     }
     void insert(string s){
        int u=0,n=s.length();
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            if(!ch[u][c]) ch[u][c]=cnt++;
            u=ch[u][c];
        }
        val[u]=cnt;
    }
    bool query(string s){
        int u=0,n=s.length(),flag,k;
        for(int i=0;i<n;i++){
            int c=s[i]-'a';
            for(int j=0;j<=2;j++){
                if(c==j||ch[u][j]==0) continue;
                int x=ch[u][j];
                flag=1;
                for(int k=i+1;k<n;k++){
                    int y=s[k]-'a';
                    if(!ch[x][y]){
                        flag=0;
                        break;
                    }
                    x=ch[x][y];
                }
                if(flag&&val[x]) return true;
            }
            if(!ch[u][c]) return false;
            u=ch[u][c];
        }
        return false;
    }
 }T;
 int main()
 {
     string temp;
    scanf("%d%d",&n,&m);
    T.init();
     for(int i=1;i<=n;i++){
         cin>>temp;
         T.insert(temp);
     }
    for(int i=1;i<=m;i++){
        cin>>temp;
        if(T.query(temp)) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
    return 0;
 }
View Code

69E.Subsegments(STL)

题意:

输出每一个长度为K的区间中出现次数不为1数的最大值

思路:

维护一个map与set

set用于维护当前所指向的区间(可以O(logn)直接取到最大值与删除容器内元素)

map用于维护区间内元素的出现个数

先将前k个元素加入set内,每个元素加入之后,通过map查询其出现次数,如果不为1则抹去,最后判断set是否为空,如果不为空则输出set内的最后一个元素(set容器内元素有序)

之后移动区间,需要删去一个元素这里要注意如果元素删去后其出现次数为1的话,要将其重新加入区间内

#include<iostream>
#include<algorithm>
#include<map>
#include<set>
 using namespace std;
 const int maxn=1e5+10;
 map<int,int> cnt;
 set<int> s;
 int a[maxn];
 int main()
 {
     int n,k;
     scanf("%d%d",&n,&k);
     for(int i=1;i<=n;i++)    scanf("%d",&a[i]);
     for(int i=1;i<=k;i++){
         cnt[a[i]]++;
         if(cnt[a[i]]!=1) s.erase(a[i]);
         else s.insert(a[i]);
     }
     if(s.empty())    cout<<"Nothing"<<endl;
     else    cout<<*(--s.end())<<endl;
     for(int i=k+1;i<=n;i++){
         cnt[a[i]]++;
         cnt[a[i-k]]--;
         if(cnt[a[i]]!=1)    s.erase(a[i]);
         else    s.insert(a[i]);
         if(cnt[a[i-k]]==1)    s.insert(a[i-k]);
         else    s.erase(a[i-k]);
         if(s.empty())    cout<<"Nothing"<<endl;
         else    cout<<*(--s.end())<<endl;
     }
    return 0;
 }
View Code

25C. Roads in Berland(最短路松弛操作)

题意:

给你一个dis[i][j]矩阵,代表从i顶点到j顶点的最短路,再给你k条路,每次将其加入地图中,问加入后各个顶点之间最短路的和

思路:

如果加入的道路距离比原来的距离长,则不需要修改答案

如果小于原来的距离,则先将原来的距离更新,枚举起点与重点判断能否进行松弛操作

松弛操作:dis[i][j]>dis[i][v]+w+dis[u][j]

     ②dis[i][j]>dis[i][u]+w+dis[v][j]

#include<iostream>
#include<algorithm>
#include<cstring>
 using namespace std;
 typedef long long ll;
 const int maxn=305;
 ll dis[maxn][maxn];
 ll n,ans=0,m,u,v,w;
 void solve()
 {
     for(int i=1;i<=n;i++){
         for(int j=1;j<=n;j++){
            if(dis[i][j]>dis[i][v]+w+dis[u][j]){
                ans-=dis[i][j]-(dis[i][v]+w+dis[u][j]);
                dis[i][j]=dis[j][i]=dis[i][v]+w+dis[u][j];
            }
            if(dis[i][j]>dis[i][u]+w+dis[v][j]){
                ans-=dis[i][j]-(dis[i][u]+w+dis[v][j]);
                dis[i][j]=dis[j][i]=dis[i][u]+w+dis[v][j];
             }
     }
 }
}
 int main()
 {
     scanf("%lld",&n);
     for(int i=1;i<=n;i++)
         for(int j=1;j<=n;j++){
             scanf("%lld",&dis[i][j]);
             ans+=dis[i][j];
         }
    ans/=2;
    scanf("%lld",&m);
    for(int i=1;i<=m;i++){
        scanf("%lld%lld%lld",&u,&v,&w);
        if(dis[u][v]<=w){
            cout<<ans<<" ";
            continue;
        }
        else{
            ans-=dis[u][v]-w;
            dis[u][v]=dis[v][u]=w;
            solve();
            cout<<ans<<" ";
        }
    }
    return 0;
 }
View Code

128B. String Problem (floyd预处理)

题意:

给两个字符串,以及m个从字符a变换到b的代价,问把两个字符串变得相同的最小代价

思路:

将字符串变换的代价当做一条有向边建图,之后跑floyd,得到从字符i变成j的最小代价dis[i][j]

之后遍历两个字符串的每一位,不相同的话枚举变成什么字符所需要的代价最小,如果找不到这样一个中间点使得s1[i]=ch,s2[i]=ch,则直接输出-1,最后将最小代价累加就好了

#include<iostream>
#include<algorithm>
#include<cstring>
#define inf 0x3f3f3f3f
 using namespace std;
 const int maxn=1e5+10;
 int dis[27][27],mp[27][27];
 char s1[maxn],s2[maxn],a1,a2,s3[maxn];
 void floyd()
 {
     for(int i=0;i<26;i++)
         for(int j=0;j<26;j++)    dis[i][j]=mp[i][j];
     for(int k=0;k<26;k++)
         for(int i=0;i<26;i++)
             for(int j=0;j<26;j++)
                 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
 }
 int main()
 {    
     int n,w;
     cin>>s1>>s2>>n;
     for(int i=0;i<26;i++)
         for(int j=0;j<26;j++){
             if(i==j)    mp[i][j]=0;
             else mp[i][j]=inf;
         }
     for(int i=1;i<=n;i++){
         cin>>a1>>a2>>w;
         int u=a1-'a',v=a2-'a';
         mp[u][v]=min(mp[u][v],w);
     }
    floyd();
    int len1=strlen(s1),len2=strlen(s2);
    if(len1!=len2){
        cout<<-1<<endl;
        return 0;
    }
    int ans=0;
    for(int i=0;i<len1;i++){
        if(s1[i]==s2[i]){s3[i]=s1[i];continue;}
        else{
            int mini=inf,x=-1,u=s1[i]-'a',v=s2[i]-'a';
            for(int j=0;j<26;j++){
                if(dis[u][j]+dis[v][j]<mini) mini=dis[u][j]+dis[v][j],x=j;
            }
            if(x==-1){
                cout<<"-1"<<endl;
                return 0;
            }
            else{
                s3[i]=x+'a';
                ans+=mini;
            }
        }
    }
    cout<<ans<<endl<<s3<<endl;
    return 0;
 }
View Code

 33C.Wonderful Randomized Sum

题意:

给一个数组你可以把一段前缀和乘上-1,再把一个后缀和乘上-1,前缀跟后缀之间可以有重叠部分,问操作后序列的最大和是多少

思路:

我们先考虑有重叠的部分,由于乘了两次-1,所有重合的部分不变,因此答案的方案肯定不会有重叠部分

我们假设所有元素的和为sum,从反面思考假设中间的那段和为s,那么操作后数组的和为-(sum - s) + s = 2 * s - sum

所以问题就转化为找一个最大子序列问题了

#include<iostream>
#include<algorithm>
 using namespace std;
 const int maxn=1e5+10;
 int a[maxn];
 int main()
 {
     int n,sum=0;
     scanf("%d",&n);
     for(int i=1;i<=n;i++){
         scanf("%d",&a[i]);
         sum+=a[i];
     }
     int mx,cnt=0;
     for(int i=1;i<=n;i++){
         cnt+=a[i];
         if(cnt<0) cnt=0;
         mx=max(mx,cnt);
     }
    cout<<2*mx-sum<<endl;
    return 0;
 }
View Code

 1292B.Aroma's Search (暴力+思维)

题意:

告诉一个点的位置,之后的点按照x[i]=x[i-1]*ax+bx;,y[i]=y[i-1]*ay+by的规律分布,初始时你站在一个位置,每秒可以往四个方向移动,问你在t内最多能走过多少个点

思路:

通过数据我们可以发现,在264就已经超过了1e16也就是t的范围了,因此地图内最多只有64个点

所以我们只需要暴力求出每一个点的坐标,再枚举你所走的点的起点与终点(只往一个方向走连续的点肯定是最优)

判断距离(从初始位置走到起点的距离再加上起点到终点的距离)是否小于t即可,求出一个最大值就是答案啊

#include<iostream>
#include<algorithm>
#include<cmath>
 using namespace std;
 typedef long long ll;
 ll x[70],y[70];
 ll dis(ll x1,ll y1,ll x2,ll y2)
 {
     return abs(x1-x2)+abs(y1-y2);
 }
 int main()
 {
     ll ax,ay,bx,by,xs,ys,t;
     int cnt=0;
     cin>>x[0]>>y[0]>>ax>>ay>>bx>>by;
     cin>>xs>>ys>>t;
     while(x[cnt]-xs<t&&y[cnt]-ys<t){
         cnt++;
         x[cnt]=x[cnt-1]*ax+bx;
         y[cnt]=y[cnt-1]*ay+by;
     }
    int ans=0;
    for(int i=0;i<=cnt;i++){
        for(int j=i;j<=cnt;j++){
            if(j-i+1<=ans) continue;
            ll temp=min(dis(x[i],y[i],xs,ys),dis(x[j],y[j],xs,ys));
            temp+=dis(x[i],y[i],x[j],y[j]);
            if(temp<=t) ans=j-i+1;
        }
    }
    cout<<ans<<endl;
    return 0;
 }
View Code

1286 A.Garland(DP)

题意:

有一个数组,包含乱序1-n的元素,并且有些数被从数组中拿出,现在问怎样将这些数放回才能使,相邻两数为奇偶的对数最小

思路:

定义dp[i][j][k]为到n这个位置放了j个偶数最后一位为奇数或偶数

那么转移方程为:

令t=dp[i-1][j][k]

i.奇数:dp[i][j][1]=max(dp[i][j][1],t+(k!=1))

ii.偶数:dp[i][j+1][0]=max(dp[i][j][0],t+(k!=0))

#include<iostream>
#include<algorithm>
#include<cstring>
#define inf 0x3f3f3f3f
 using namespace std;
 const int maxn=110;
 int dp[maxn][maxn][3],a[maxn],n;
 int main()
 {
     int n,num=0;
     scanf("%d",&n);
     for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    num=n/2;
    memset(dp,inf,sizeof(dp));
    dp[0][0][0]=dp[0][0][1]=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<i;j++){
            for(int k=0;k<2;k++){
                int t=dp[i-1][j][k];
                if(a[i]){
                    if(a[i]%2) dp[i][j][1]=min(dp[i][j][1],t+(k!=1));
                    else dp[i][j+1][0]=min(dp[i][j+1][0],t+(k!=0));
                }
                else{
                    for(int l=0;l<2;l++)
                        dp[i][j+1-l][l]=min(dp[i][j+1-l][l],t+(l!=k));
                }
            }
        }
    }
    printf("%d\n", min(dp[n][num][0], dp[n][num][1]));
 }
View Code

猜你喜欢

转载自www.cnblogs.com/overrate-wsj/p/12373149.html