CF 题目选做

  写省选的题目对noip没什么大用 关键是 细节题或者是思考题比较重要 练思维自然是CF比较好了 把我见到的比较好的CF题放上来刷一刷。

LINK:Complete the projects

就是说一个人 初始值为R 有n项工作每项工作有两种属性 a和b 当且仅当 R>=a时可以完成这项任务 且R+=b; 每项任务只能完成一次 问能否把所有工作完成。注:b可能为负。

怎么做?显然对于b>=0的工作我们按照a由小到大的顺序直接做如果有不能完成的任务的话 那便一定不能完成。考虑如何做负值的工作?

想了一下我们不能仅仅按照a的值从大到小做 因为可能出现顺序上的错误 导致答案错误 按照 ai+bi从大到小的顺序么? 经过我的一番操作 发现wa掉了。再次考虑这个顺序?

苦苦思索好多天 发现没认真读题 题目中说道对于完成每一个任务都R都得非负,最后我没判断R的正负... 所以就是按照ai+bi从大到小的顺序的。

当时考的时候我直接想到了 但是没想证明这里来利用微扰法来证明一下。设当前任务排序为 a1+b1,a2+b2...an+bn;

如果对于 ai+bi >aj+bj 来说j排在i前面那么 则有R>=aj 且R-=bj 之后可得 R>=ai; 我们看另一种情况 R>=ai 且 R-=bi 之后又 R>=aj;

好像不太好证明  因为看起来非常的显然 也容易想到,那么不证自明好了.../cy

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime> 
#include<cstring>
#include<string>
#include<ctime>
#include<cctype>
#include<cstdio>
#include<utility>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<bitset>
#include<vector>
#include<algorithm>
#include<cstdlib>
#define ll long long
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const ll MAXN=210;
ll n,r,top,top1,flag;
struct wy
{
    ll x,y;
}t[MAXN],w[MAXN];
inline ll cmp(wy a,wy b){return a.x<b.x;}
inline ll cmp1(wy a,wy b){return a.x+a.y>b.x+b.y;}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();r=read();
    for(ll i=1;i<=n;++i)
    {
        ll x,y;
        x=read();y=read();
        if(y>=0)t[++top]=(wy){x,y};
        if(y<0)w[++top1]=(wy){x,y};
    }
    sort(t+1,t+1+top,cmp);
    for(ll i=1;i<=top;++i)
    {
        if(t[i].x>r){puts("NO");return 0;}
        r+=t[i].y;
    }
    sort(w+1,w+1+top1,cmp1);
    for(ll i=1;i<=top1;++i)
    {
        if(w[i].x>r){puts("NO");return 0;}
        r+=w[i].y;
    }
    if(r>=0)puts("YES");
    else puts("NO");
    return 0;
}
View Code

LINK:加强版

现在是求 我们最多能完成的任务数 这就很有区别了首先对于b>=0我们不一定能全部做完但是还是可以贪心的去做 因为拿不到的不管采取什么方法都拿不到。

考虑如何取 a<0的部分这次我们不能再贪心的选取了 因为 可能当前是能选导致R大大减小 以至于后面选的任务更少了。

但是我们仍可以按照上一道题的排序方式来选取,这样仍可以使得利益最大化...固定了顺序接下来的操作其实就是对于每个任务选或不选了,直接背包的拿取即可。dp一下...

当然和背包不太一样因为 有ai的限制我们状态设为 到第i个任务此时R值为j所做的最多的任务数,这样进行转移即可。只不过这个需要刷表法更容易写。

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime> 
#include<cstring>
#include<string>
#include<ctime>
#include<cctype>
#include<cstdio>
#include<utility>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<bitset>
#include<vector>
#include<algorithm>
#include<cstdlib>
#define ll long long
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=110,maxn=30010<<1;
int n,r,top,top1,ans,maxx;
int f[MAXN][maxn];//f[i][j]表示到了第i个任务此时还有j所能完成的最大任务数
struct wy
{
    int x,y;
}t[MAXN],w[MAXN];
inline int cmp(wy a,wy b){return a.x<b.x;}
inline int cmp1(wy a,wy b){return a.x+a.y>b.x+b.y;}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();r=read();
    for(int i=1;i<=n;++i)
    {
        int x,y;
        x=read();y=read();
        if(y>=0)t[++top]=(wy){x,y};
        if(y<0)w[++top1]=(wy){x,y};
    }
    sort(t+1,t+1+top,cmp);
    for(int i=1;i<=top;++i)
    {
        if(t[i].x>r)break;
        r+=t[i].y;++ans;
    }
    sort(w+1,w+1+top1,cmp1);
    for(int i=1;i<=top1;++i)
    {
        for(int j=0;j<=r;++j)
        {
            f[i+1][j]=max(f[i+1][j],f[i][j]);
            if(j>=w[i].x&&j>=w[i].y)f[i+1][j+w[i].y]=max(f[i+1][j+w[i].y],f[i][j]+1);
        }
    }
    for(int j=0;j<=r;++j)maxx=max(maxx,f[top1+1][j]);
    printf("%d\n",maxx+ans);
    return 0;
}
View Code

LINK:Equalizing by division

每次 都寻找到任意一个数除以二问 得到k个相同的数字最小的除法次数。我们显然的一个想法是答案是不具单调性的不能二分 不如枚举 当前答案。

那么我们只需要的到 所有元素到这个数字的前k小的和即可,每次都这样做事n^2logn的 不妨做一次直接存起来然后调用预处理nlogn 对于每个值开vector存就好了。

然后取前 k个排序即可,累和统计答案即可。因为 数字的个数最多nlogn 所以 复杂度n(logn)^2 但是 我开的是堆每次插入删除都是logn的所以总复杂度有两倍的常数。

//#include<bits/stdc++.h>
#include<iostream>
#include<iomanip>
#include<ctime> 
#include<cstring>
#include<string>
#include<ctime>
#include<cctype>
#include<cstdio>
#include<utility>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<set>
#include<bitset>
#include<vector>
#include<algorithm>
#include<cstdlib>
#define ll long long
#define INF 1000000000
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=200010;
int n,k;
int maxx,w=INF;
priority_queue<int>q[MAXN];
int main()
{
    //freopen("1.in","r",stdin);
    n=read();k=read();
    for(int i=1;i<=n;++i)
    {
        int x=read(),cnt=0;
        maxx=max(maxx,x);
        while(x)
        {
            q[x].push(-cnt);
            x=x>>1;++cnt;
        }
        q[0].push(-cnt);
    }
    for(int i=maxx;i>=0;--i)
    {
        if(q[i].size()<k)continue;
        int cnt=k,ans=0;
        while(cnt)
        {
            ans+=-q[i].top();
            q[i].pop();--cnt;
        }
        w=min(w,ans);
    }
    printf("%d\n",w);
    return 0;
}
View Code

 LINK:RBG的子串 这个题还不错吧,CF主要考的就是思维了..

但是这里明确指明了是连续的子串 就是说给定一个串让你改变其中若干个字符 使得长度为k的字符串既是RBGRBGRBG...的连续子串也是给定的字符串的连续子串。

求要改变的最小数量。初看觉得不太可做..还在思考中。上了趟厕所想起来了 $\sum{n}$<=2000 

那么这样的话这不是一道**题么?发现每个字母都后面紧跟的字母是一定的我们分析答案的来源 源于某个点开始进行的匹配我们直接枚举然后暴力匹配即可.

复杂度3*n*k;

//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 1000000000
#define ll long long
#define db double
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=2010;
int n,k,T,ans;
char a[MAXN];
int b[MAXN];
int main()
{
    //freopen("1.in","r",stdin);
    T=read();
    while(T--)
    {
        n=read();k=read();ans=INF;
        scanf("%s",a+1);
        for(int i=1;i<=n;++i)
        {
            if(a[i]=='G')b[i]=2;
            if(a[i]=='B')b[i]=0;
            if(a[i]=='R')b[i]=1;
        }
        for(int i=1;i<=n-k+1;++i)//枚举起点
        {
            for(int j=0;j<=2;++j)
            {
                int w=j,cnt=0;
                for(int l=i;l<=i+k-1;++l)
                {
                    if(w==3)w=0;
                    if(b[l]!=w)++cnt;
                    ++w;
                }
                ans=min(ans,cnt);
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

当然 现在看起来是很水的..但是当然还是有加强版的Y。

LINK:RBG substring hard version n,k<=200000 当然还是T组数据 所以 $\sum{n}$<=200000

暴力显然T,考虑一下正解吧,其实我们发现可以优化上述的过程,一个状态被重复计算了多次我们优化即可。

不妨设f[i][j]表示以第i个点为起点它是j所形成长度为k的子串的最小代价。还是比较容易求的吧复杂度这样就变成了O(n*3);

//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 1000000000
#define ll long long
#define db double
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=200010;
int n,k,T,ans;
char a[MAXN];
int b[MAXN];
int f[MAXN][3];
int main()
{
    //freopen("1.in","r",stdin);
    T=read();
    while(T--)
    {
        n=read();k=read();ans=INF;
        scanf("%s",a+1);
        for(int i=1;i<=n;++i)
        {
            if(a[i]=='G')b[i]=2;
            if(a[i]=='B')b[i]=0;
            if(a[i]=='R')b[i]=1;
        }
        for(int i=n;i>=n-k+1;--i)
            for(int j=0;j<=2;++j)
            {
                f[i][j]=b[i]==j?0:1;
                int w=j+1;if(w==3)w=0;
                f[i][j]+=f[i+1][w];
            }
        ans=min(ans,f[n-k+1][0]);
        ans=min(ans,f[n-k+1][1]);
        ans=min(ans,f[n-k+1][2]);
        for(int i=n-k;i>=1;--i)
        {
            //cout<<b[i]<<' '<<i<<endl;
            for(int j=0;j<=2;++j)
            {
                int w=j+1;if(w==3)w=0;
                f[i][j]=b[i]==j?0:1;
                f[i][j]+=f[i+1][w];
                f[i][j]-=(b[i+k]!=(j+k)%3)?1:0;
                ans=min(ans,f[i][j]);
            }
            //if(ans==0)cout<<i<<endl;
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

 发现做的都是一些D题 应该多做一些F和E题才对..

LINK:Connected Component on a Chessboard 这里的Component意思是组成的组成部分 chessboard意思是棋牌 题目的意思是再棋盘上链接组成。

从题目中我们可以得知这是一个网格图 题目中cell被翻译乘单元格而并非细胞。就是说构造一个b+w的联通块使得白块个数为w 黑块个数为b 显然 分类讨论一波即可。

为了防止越界 我从500000这个点开始构造。

#//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 1000000000
#define ll long long
#define db double
#define pb push_back
#define un unsigned
using namespace std;
char *ft,*fs,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=200010;
int x[MAXN],y[MAXN];
int Q,b,w;
int main()
{
    //freopen("1.in","r",stdin);
    Q=read();
    while(Q--)
    {
        b=read();w=read();
        if(b>w)
        {
            int cnt=3*w+1;
            if(b>cnt){puts("NO");continue;}
            puts("YES");
            x[1]=500000,y[1]=500000;
            cout<<x[1]<<' '<<y[1]<<endl;
            for(int i=2;i<=w;++i)
            {
                x[i]=x[i-1]-2;
                y[i]=y[i-1];
                cout<<x[i]<<' '<<y[i]<<endl;
                cout<<x[i]+1<<' '<<y[i]<<endl;
                --b;
            }
            for(int i=1;i<=w;++i)
            {
                if(!b)break;
                cout<<x[i]<<' '<<y[i]-1<<endl;
                --b;
                if(!b)break;
                cout<<x[i]<<' '<<y[i]+1<<endl;
                --b;
            }
            if(b)cout<<x[1]+1<<' '<<y[1]<<endl,--b;
            if(b)cout<<x[w]-1<<' '<<y[w]<<endl,--b;
        }
        else 
        {
            int cnt=3*b+1;
            //cout<<cnt<<endl;
            if(w>cnt){puts("NO");continue;}
            puts("YES");
            x[1]=500000,y[1]=500001;
            cout<<x[1]<<' '<<y[1]<<endl;
            for(int i=2;i<=b;++i)
            {
                x[i]=x[i-1]-2;
                y[i]=y[i-1];
                cout<<x[i]<<' '<<y[i]<<endl;
                cout<<x[i]+1<<' '<<y[i]<<endl;
                --w;
            }
            for(int i=1;i<=b;++i)
            {
                if(!w)break;
                cout<<x[i]<<' '<<y[i]-1<<endl;
                --w;
                if(!w)break;
                cout<<x[i]<<' '<<y[i]+1<<endl;
                --w;
            }
            if(w)cout<<x[1]+1<<' '<<y[1]<<endl,--w;
            if(w)cout<<x[b]-1<<' '<<y[b]<<endl,--w;
        }
    }
}
View Code

LINK:最短路相关去掉路径问题 具体题面都是可以打开的这里不再赘述。考虑怎么做?一个比较容易得到的错误的思路是 先不跑铁路 先跑道路然后判断某个铁路是否能够去掉 然后考虑得到 一条铁路也会对整张图有贡献考虑用堆 从小到大 判断 再如果不去掉那么加到图中 类似于动态加边的spfa 可能复杂度不太对qaq. 正确性?很遗憾是错误的 因为边权可能是相同的 所以你有可能根本不知道到底哪一条边先出堆可能消除的边会更多。发现自己有点蠢怎么可能出现这种情况啊。边权不为负权是不可能出现我刚才说的东西的。

所以做法是错误的 当然这个动态加边的spfa 写的dij复杂度最坏可能会达到nmlogn 不推荐写还不如直接spfa 所以理所当然的T。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cctype>
#include<cstdlib>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#define INF 1000000000000000ll
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define F first
#define S second
#define mk make_pair
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=100010,maxn=400010;
int n,m,k,len;
int ans;
int v[MAXN],vis[MAXN];
int lin[MAXN],nex[maxn<<1],ver[maxn<<1],e[maxn<<1];
ll dis[MAXN];
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
priority_queue<pair<ll,int> > q,s;
inline void dij(int x)
{
    q.push(mk(-dis[x],x));vis[x]=1;
    while(q.size())
    {
        int x=q.top().S;vis[x]=0;
        q.pop();
        for(int i=lin[x];i;i=nex[i])
        {
            int tn=ver[i];
            if(dis[tn]>dis[x]+e[i])
            {
                dis[tn]=dis[x]+e[i];
                if(!vis[tn])q.push(mk(-dis[tn],tn)),vis[tn]=1;
            }
        }
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();k=read();
    for(int i=1;i<=m;++i)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    for(int i=1;i<=k;++i)
    {
        int x,y;
        x=read();y=read();
        if(!v[x])v[x]=y;
        else v[x]=min(v[x],y);
    }
    for(int i=1;i<=n;++i)
    {
        dis[i]=INF;
        if(v[i])s.push(mk(-v[i],i));
    }
    dis[1]=0;
    dij(1);
    while(s.size())
    {
        int x=s.top().S;
        s.pop();
        if(v[x]>=dis[x])continue;
        ++ans;dis[x]=v[x];
        dij(x);
    }
    printf("%d\n",k-ans);
    return 0;
}
spfa版本的dij

T了意料之中这可能是比spfa 更差的算法了...

可以直接spfa 当然由于spfa 已经死了。。所以直接就死了。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cctype>
#include<cstdlib>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#define INF 1000000000000000ll
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define F first
#define S second
#define mk make_pair
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=100010,maxn=400010;
int n,m,k,len;
int ans;
int v[MAXN],vis[MAXN];
int lin[MAXN],nex[maxn<<1],ver[maxn<<1],e[maxn<<1];
ll dis[MAXN];
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
priority_queue<pair<ll,int> > s;
queue<int>q;
inline void dij(int x)
{
    q.push(x);vis[x]=1;
    while(q.size())
    {
        int x=q.front();vis[x]=0;
        q.pop();
        for(int i=lin[x];i;i=nex[i])
        {
            int tn=ver[i];
            if(dis[tn]>dis[x]+e[i])
            {
                dis[tn]=dis[x]+e[i];
                if(!vis[tn])q.push(tn),vis[tn]=1;
            }
        }
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();k=read();
    for(int i=1;i<=m;++i)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    for(int i=1;i<=k;++i)
    {
        int x,y;
        x=read();y=read();
        if(!v[x])v[x]=y;
        else v[x]=min(v[x],y);
    }
    for(int i=1;i<=n;++i)
    {
        dis[i]=INF;
        if(v[i])s.push(mk(-v[i],i));
    }
    dis[1]=0;
    dij(1);
    while(s.size())
    {
        int x=s.top().S;
        s.pop();
        if(v[x]>=dis[x])continue;
        ++ans;dis[x]=v[x];
        dij(x);
    }
    printf("%d\n",k-ans);
    return 0;
}
View Code

考虑 一下正解 仔细观察题目 有一个特殊的性质 铁路都是和1 相连的边 考虑全部都一起跑最短路 那么对于更新每个点的dis之前我们尽量让到i这个点的路径尽量长那么 此时如果更新时大于2 那么显然 连向其的铁路是没有用的。

obviously 这种关系显然不存在环 当然还有1条边的情况需要特判一下什么的。这点小细节还是很容易处理的。注意是双向边 坑了我很久很久。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cctype>
#include<cstdlib>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#define INF 1000000000000000ll
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=100010,maxn=400010;
int n,m,k,len;
int ans;
int v[MAXN],vis[MAXN],pos[MAXN],pre[MAXN],sum[MAXN];
int lin[MAXN],nex[maxn<<1],ver[maxn<<1],e[maxn<<1];
ll dis[MAXN];
struct wy
{
    int x;
    ll v;
    int friend operator <(wy a,wy b)
    {
        return a.v>b.v;
    }
};
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
priority_queue<wy> q;
inline void dij()
{
    while(q.size())
    {
        int x=q.top().x;
        q.pop();
        if(vis[x])continue;
        vis[x]=1;
        for(int i=lin[x];i;i=nex[i])
        {
            int tn=ver[i];
            if(dis[tn]>dis[x]+e[i])
            {
                dis[tn]=dis[x]+e[i];
                pre[tn]=i;sum[tn]=sum[x]+1;
                q.push((wy){tn,dis[tn]});
            }
            else if(dis[tn]==dis[x]+e[i])
            {
                pre[tn]=min(pre[tn],i);
                sum[tn]=max(sum[tn],sum[x]+1);
            }
        }
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();k=read();
    for(int i=1;i<=m;++i)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    for(int i=1;i<=k;++i)
    {
        int x,y;
        x=read();y=read();
        if(!v[x])v[x]=y;
        else v[x]=min(v[x],y);
    }
    for(int i=1;i<=n;++i)
    {
        dis[i]=INF;
        if(v[i])
        {
            add(1,i,v[i]);
            pos[i]=len;
        }
    }
    dis[1]=0;
    q.push((wy){1,0});
    dij();
    for(int i=1;i<=n;++i)
        if(v[i])
        {
            if(sum[i]>1)continue;
            if(pre[i]==pos[i])++ans;
        }
    printf("%d\n",k-ans);
    return 0;
}
View Code

 LINK:比较难的最短路问题 对于每个点都需要求出那个等式的值一个比较显然的思路发现是多源最短路floyd 然后n^2暴力枚举统计答案即可。

考虑一下正解我们 把i放到终点其实就是找j到i的最短路 把j放到队列里发现这道题就做完了qwq。。。我当时就没想出来/kk

对于多源最短路 必然使用floyd更好一点 但是特殊的是本题中自己也可以是终点,所以不难想到把终点放到队列中找起点,刚好自己也可以是终点所以直接跑最短路即可找到起点 那么$dis_i$存的就是某个终点到起点的最短路辣。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cctype>
#include<cstdlib>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#define INF 1000000000000000ll
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define mk make_pair
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=200010,maxn=200010;
int n,m,k,len;
int ans;
int vis[MAXN];
int lin[MAXN],nex[maxn<<1],ver[maxn<<1];
ll dis[MAXN],e[maxn<<1];
priority_queue<pair<ll,int> > q;
inline void add(int x,int y,ll z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline void dij()
{
    while(q.size())
    {
        int x=q.top().second;
        q.pop();
        if(vis[x])continue;
        vis[x]=1;
        for(int i=lin[x];i;i=nex[i])
        {
            int tn=ver[i];
            if(dis[tn]>dis[x]+e[i])
            {
                dis[tn]=dis[x]+e[i];
                q.push(mk(-dis[tn],tn));
            }
        }
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=m;++i)
    {
        int x,y;ll z;
        x=read();y=read();z=read();
        add(x,y,z<<1);add(y,x,z<<1);
    }
    for(int i=1;i<=n;++i)
    {
        dis[i]=read();
        q.push(mk(-dis[i],i));
    }
    dij();
    for(int i=1;i<=n;++i)printf("%lld ",dis[i]);
    return 0;
}
View Code

 LINK:最小生成树性质题目 这我拿头都不一定能想出来 可持久化并茶几一定能写,但看一下能否有更优的解法。

一个定理 设当前要加入的边权为z 那么对于<=z-1的边权 存在树的 形态一定是固定的。该联通的一定都联通了 那么对于边权为z的边首要是判断 在z-1的时候两个端点x y是否联通。

如果不连通呢?如果存在边权和其相同的 呢?可能存在当前边于前面加入的那些边构成了环 这里发现一条边可能直接连通了两个集合,另一条边联通的 也是两个集合 但是这两个集合是相同的 如果我们单单使用并茶几连接单点 并不能及时的判环,这里 想了一下是连通块直接相连 然后判断即可 但是连过后还需要断边 所以 瞄了一眼题解 发现大多都是 按秩合并并茶几...(有路径压缩的 大雾) 值得一提的是 边权不同的是不受影响的..

所以主要的性质是 就是上述定理辣。

//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 1000000000
#define ll long long
#define db double
#define mod 1000000007
#define pii pair<int,int>
#define mk make_pair
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=500010;
int n,m,maxx,Q,cnt;
int f[MAXN],flag[MAXN],ans[MAXN],sz[MAXN],pos[MAXN];
struct wy
{
    int x,y,z,id;
}t[MAXN];
struct data
{
    int x,y,id;
    int fa,son;
};
vector<data>g[MAXN];
inline int cmp(wy a,wy b){return a.z<b.z;}
inline int getfather(int x)
{
    if(f[x]==x)return x;
    return getfather(f[x]);
}
inline void cut(int x,int y)
{
    sz[x]-=sz[y];
    f[y]=y;return;
}
int main()
{
    freopen("1.in","r",stdin);
    n=read();m=read();cnt=1;
    for(int i=1;i<=m;++i)
    {
        int x,y,z;
        x=read();y=read();z=read();
        t[i]=(wy){x,y,z,i};
        maxx=max(maxx,z);
    }
    sort(t+1,t+1+m,cmp);
    for(int i=1;i<=m;++i)pos[t[i].id]=i;
    for(int i=1;i<=n;++i)f[i]=i,sz[i]=1;
    Q=read();
    for(int i=1;i<=Q;++i)
    {
        int k,x;
        k=read();
        for(int j=1;j<=k;++j)
        {
            x=read();
            g[t[pos[x]].z].push_back((data){t[pos[x]].x,t[pos[x]].y,i});
        }
    }
    for(int i=1;i<=maxx;++i)//处理 前面的然后再加边
    {
        int last=-1;
        if(g[i].size())
        {
            int ww=(int)g[i].size();
            for(int j=0;j<ww;++j)
            {
                if(ans[g[i][j].id])continue;
                data w=g[i][j];
                if(last==-1)last=j;
                int xx=getfather(w.x);
                int yy=getfather(w.y);
                if(xx==yy)
                {
                    ans[w.id]=1;
                    int p=j-1;
                    while(p>=last)
                    {
                        cut(g[i][p].fa,g[i][p].son);
                        --p;
                    }
                    last=-1;
                    continue;
                }
                else
                {
                    if(sz[xx]>sz[yy])swap(xx,yy);
                    g[i][j].fa=yy;g[i][j].son=xx;
                    sz[yy]+=sz[xx];f[xx]=yy;
                }
                if(j+1>ww||g[i][j+1].id!=w.id)
                {
                    int p=j;
                    while(p>=last)
                    {
                        cut(g[i][p].fa,g[i][p].son);
                        --p;
                    }
                    last=-1;
                }
            }
        }
        while(cnt<=m&&t[cnt].z==i)
        {
            int xx=getfather(t[cnt].x);
            int yy=getfather(t[cnt].y);
            if(xx==yy){++cnt;continue;}
            if(sz[xx]>sz[yy])swap(xx,yy);
            f[xx]=yy;sz[yy]+=sz[xx];
            ++cnt;
        }
    }
    for(int i=1;i<=Q;++i)puts(ans[i]?"NO":"YES");
    return 0;
}
View Code

 LINK:不太会的dp题目1249Fmaximum weight subset 考试的时候写到这道题的时候 就只剩下40分钟了 还是在外面下雨的 深夜 很不想写 也没有什么思路 所以就咕了。

选出一些点集使这个点集之中任意两点之间的距离不超过k 且 求点集的最大权。n 是200 显然无法状压和爆搜。还是不会 咕咕咕...

 看完题解丢脸的 爬过来填坑...我有一个状态但是不太对头 这道题中显然是树形dp 但是考虑到一些问题 关于深度我们必然要记录 不然很难搞这个限制.

还有一点值得一提的是 可以设状态了 f[i][j] 表示 以i为根的子树此时选择点集深度任意一个点的深度满足>=j 且 该集合合法 时的最大值 。

那么显然有转移 f[i][0]+=f[tn][k]...什么的 还是挺显然的。但是存在漏洞 每次转移我们都是定点转移也就是说==j的转移 >j的那部分我们不知道 但是 由于单调性的缘故如果>j比==j更优那么显然可以利用>j来更新。

让f数组具有单调性即可。不需要换根 因为我们没有强制选根所以只要答案最优都是可以任意一个点来得到答案的。

//#include<bits/stdc++.h>
#include<iostream>
#include<queue>
#include<iomanip>
#include<cctype>
#include<cstdio>
#include<deque>
#include<utility>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdlib>
#include<vector>
#include<algorithm>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#define INF 1000000000
#define inf 1000000000
#define ll long long
#define db double
#define pb push_back
#define un unsigned
#define mod 1000000007
using namespace std;
inline ll read()
{
    ll    x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const ll MAXN=210;
int n,k,len;
int f[MAXN][MAXN];//f[i][j] 表示 以i为根此时选的点集的选的最浅的点的深度为j 的最大价值
//f数组显然具有单调性 即 f[x][j]>=f[x][j+1];
int v[MAXN];
int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1];
inline void add(int x,int y)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
}
inline void dfs(int x,int father)
{
    f[x][0]=v[x];
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        dfs(tn,x);
        f[x][0]+=f[tn][k];
    }
    for(int i=1;i<n;++i)//枚举状态
    {
        for(int w=lin[x];w;w=nex[w])//寻找转移
        {
            int tn=ver[w];
            if(tn==father)continue;
            int cnt=f[tn][i-1];
            for(int j=lin[x];j;j=nex[j])
            {
                if(j==w)continue;
                int te=ver[j];
                if(te==father)continue;
                cnt+=f[te][max(i-1,k-i)];
            }
            f[x][i]=max(f[x][i],cnt);
        }
    }
    for(int i=n-1;i>=1;--i)f[x][i-1]=max(f[x][i-1],f[x][i]);
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();k=read();
    for(int i=1;i<=n;++i)v[i]=read();
    for(int i=1;i<n;++i)
    {
        int x,y;
        x=read();y=read();
        add(x,y);add(y,x);
    }
    dfs(1,0);
    printf("%d\n",f[1][0]);
    return 0;
}
View Code

根据状态的最优性 所以这是正确的 但是我觉得 这一类题还是理解浅薄 qwq难受。

复杂度n^3 注意 这里的确是n^3的 外层dfs 不计算到复杂度之中 时间复杂度最大n^3;

这里以后将会记录我每次CF比赛的 一些没有做出来的题目的坑 和已经做出来的简洁做法. 发现代码简洁 短才能在比赛中拿到一个非常好的名次.

先来一道有趣的dp 或者 说是贪心 

当我看到这道题目的时候惊 到我了 因为我好像见过 还不会写的那种 也没多少时间了 关键是D题写写调调大约用了40分钟 所以这道题也就成功GG.

现在 重新审视一下这道题目。解锁人数一旦多了起来就可以解锁其他人了 但是也可以用钱来 解锁求最少花费。

对于第一个人我们必然是要用 钱来买的 但是我们不一定要买 最小的那个 也可能是适中的一个什么的这关乎后续的选择。

好像很难dp的样子 贪心策略也没有那么的清晰 但是我们可以慢慢分析性质.

考虑一下 我们把需要相同人数的 的放在一起 我们解锁的时候一定是解锁 这若干个队列中某个的最小值(显然 

还有一个 性质我们解锁的时候除了花钱的那种 剩下的必然是 从人数从小到大的有序解锁。

还有一个 能不花钱就不花钱 把能解锁的都解锁完了再考虑花钱。

还有一个性质 不一定花费最少的是必买的因为买了其可能还需要买下一个 但是花费高的却可以完成这样的操作.

分析到这里 我们还是没有解决 第一个人要选择谁的问题...但是不要放弃 我还能继续挖掘性质.

还有一个性质 对于需求人数>n-1的我们是必买的 且先买其不会使答案变得更差。但是题目中mi都是小于n的很遗憾...由此我们得出了一个重要的结论 一个人要么被买要么被解锁。

我们能够推出的是要买人一定在解锁之前进行这样一定不会使得答案变得更差,,,当然我们要跟据序列中的元素大小 来 进行一些决策..

考虑了一下dp 显然我们 做不了 因为还需要得到人的集合 只能考虑贪心了qwq 烦躁 想不出来。

于是选择看题解 这个贪心真的是精辟qwq 我们不知道第一次要选哪个人 也不能动态的解锁 翻转一个角度来看问题。

我们尽可能的解锁 一些人 但是要解锁一个 人 那么必须保证其后面还有mi 个人我们如果让mi 从小到大的来选择的话会出现一些小问题可能前面有的人已经选了 此时会对后面有影响。

在前面发生的冲突会影响到后面所以我们倒着解决一些冲突 如何发现冲突?即每个人我们都想象成可以免费 然后需要后面的人的人数为 mi 但是设前面已经有 sum个人免费了。

那么当前点的前面最多有n-mi个人免费 当sum>n-mi的时候此时必然起冲突且和后面的人无关此时我们在前面取最小值即可。

可以发现这样满足贪心的最优性 真是神仙题目qwq.

//#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<vector>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<map>
#define INF 1000000000000000ll
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define RI register ll
#define db double
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const ll MAXN=200010;
vector<ll>g[MAXN];
ll T,n,ans;
priority_queue<ll,vector<ll>,greater<ll> > q;
signed main()
{
    //freopen("1.in","r",stdin);
    T=read();
    while(T--)
    {
        n=read();ans=0;
        for(ll i=1;i<=n;++i)
        {
            ll x,y;
            x=read();y=read();
            g[x].push_back(y);
        }
        //q.clear();
        while(q.size())q.pop();
        for(ll i=n-1;i>=0;--i)
        {
            for(unsigned j=0;j<g[i].size();++j)
            {
                q.push(g[i][j]);
                //cout<<g[i][j]<<endl;
            }
            while((ll)q.size()>n-i)
            {
                ans+=q.top();
                q.pop();
            }
            g[i].clear();
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 啊啊啊 每次都是最后一道题不会写 都给我留下阴影了qwq。

哦不这次连C都不会了当然再考试结束的2min之后我才珊珊打完...灵光灵光 灵光。

这次是一个字符串翻转问题。LINK:Equalizing Two Strings

对于两个字符串我们可以随便的进行翻转 每次翻转的大小必须相同询问 经过若干次翻转能否使两个字符串等价。

T组数据。我不会 完全没有思路,好像知道了什么套路 这个时候 必须要干一件事情 再次读题。
问了一些dalao 原来是这样惊了!使两个序列翻转过后能变成相同的。
有一个必要条件 两个序列中存在的字母类型相同且类型相同的个数相同。
有了这个条件 我们可以设想一下 翻转区间必然翻转1~n长度的区间,此时 如果区间大小是1的话那么就没有什么用了。
我们每一次翻转任意大小的区间改变的可以抽象成逆序对的问题。显然如果序列存在两个相同的字母的话
我们可以无条件的 将这对字母先放在一起 然后开始无限的冒泡排序每次搞都 翻转当前两个相同的字母 和 另外一个区间。
这样另外一个区间进行冒泡 然后另一个区间也存在这样的两个字母直接 再反过来搞即可。
考虑如果不存在这样的两个字母的话 我们继续考虑两个数字的逆序对的奇偶性 如果奇偶性相同的话我们也可以构造出来一个成功的结果。
果断考虑一下我们如果每次仅翻转两个字符 逆序对变化1 另一个也是如此 也会变化1 .
每次都变话2这样我们对两个序列都同时进行冒泡 就都可以变得有序了。如果有一个逆序对较多 我们对 先排好序的序列交换任意两个且下一次再交换回来这样若干次即可 都可以是有序的。且相同。
考虑奇偶不同 我们无论翻转 多大的序列奇偶性还是不同的我想了好长时间终于可以证明的这个东西了。
考虑 对于 s1中我们要翻转的某个长度为len的区间 中的逆序对数a1 和 s2中翻转的逆序对的个数a2 奇偶性相同 设len这个长度的区间内的逆序对个数为sum。
那么显然 sum-a1==sum-a2 s1 和 s2的奇偶性不变 都同时减去一个同奇或同偶数同时加上一个同奇数偶数 。
考虑 a1 a2奇偶不同 sum-a1 sum-a2 奇偶也不同 经过分类讨论可以发现 奇偶性依然没有改变qwq。
所以这道题就可以这样做了。真妙....
 //#include<bits/stdc++.h>
#include<iomanip>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<stack>
#include<algorithm>
#include<vector>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<map>
#define INF 1000000000
#define ll long long
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define RI register ll
#define db double
#define pii pair<ll,ll>
#define mk make_pair
#define mod 1000000007
#define ull unsigned long long
using namespace std;
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=200010,maxn=30;
int T;
int n,flag,mark;
char a[MAXN];
int b[MAXN],c[MAXN],vis[maxn];
int vis1[maxn],s[MAXN];
int main()
{
    //freopen("1.in","r",stdin);
    T=read();
    while(T--)
    {
        n=read();mark=flag=0;
        scanf("%s",a+1);
        memset(vis,0,sizeof(vis));
        memset(vis1,0,sizeof(vis1));
        for(int i=1;i<=n;++i)
        {
            int w=a[i]-'a';
            ++vis[w];
            b[i]=w+1;
        }
        scanf("%s",a+1);
        for(int i=1;i<=n;++i)
        {
            int w=a[i]-'a';
            ++vis1[w];
            s[i]=w+1;
        }
        for(int i=0;i<=25;++i)if(vis[i]!=vis1[i])flag=1;
        if(flag){puts("NO");continue;}
        for(int i=0;i<=25;++i)if(vis[i]>1)mark=1;
        if(mark){puts("YES");continue;}
        int cnt=0,cnt1=0;
        for(int i=1;i<=n;++i)
            for(int j=1;j<i;++j)
                if(b[j]>b[i])++cnt;
        for(int i=1;i<=n;++i)
            for(int j=1;j<i;++j)
                if(s[j]>s[i])++cnt1;
        cnt=cnt&1;cnt1=cnt1&1;
        if(cnt==cnt1){puts("YES");continue;}
        puts("NO");
    }
    return 0;
}
View Code

 CF1156D 0 - 1 tree  

这可真的是秒啊 我想了半天的状态转移从基础的状态 到 统计答案 很不好做。

最后是自己手残打错了 惩罚自己把手指咬破了。以后不能再脑残了。脑残是要退役的。

关于这道题很有意思 口胡一下大体思路 求点对 显然是树形dp 发现点的状态有4种 列出来 然后转移。

考虑统计答案 有一个小坑点是姿势要正确 关于儿子这个点对答案的贡献一定要分析准确不然很容易歇菜的。

还有一点就是 对于一个白边 黑点也是有贡献的 以后还是画一张清晰的图分析更快一点。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=200010;
int n,len;
ll ans;
int lin[MAXN],e[MAXN<<1],ver[MAXN<<1],nex[MAXN<<1];
ll f[MAXN][5];
//f[i][j]表示以i为根节点的子树内0 只经过边权为0的边的点的个数
//1 只经过1的点的个数 2先经过0再经过1的点的个数 3 先经过1再经过0的个数
inline void add(int x,int y,int z)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
    e[len]=z;
}
inline void dp(int x,int father)
{
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        dp(tn,x);
        //考虑如何统计答案
        if(!e[i])
        {
            //先统计答案再进行转移
            ans+=(f[tn][0]+1)*(2+2*f[x][0]+f[x][1]+f[x][3]);
            ans+=f[tn][3]*(1+f[x][0]);
            ans+=f[tn][1]*(f[x][0]+1);
            f[x][0]+=f[tn][0]+1;
            //f[x][1] 无法转移
            //f[x][2] 无法转移
            f[x][3]+=f[tn][3]+f[tn][1];
        }
        else
        {
            //f[x][0] 无法转移
            ans+=(f[tn][1]+1)*(2+f[x][0]+2*f[x][1]+f[x][2]);
            ans+=f[tn][2]*(1+f[x][1]);
            ans+=f[tn][0]*(f[x][1]+1);
            f[x][1]+=f[tn][1]+1;
            f[x][2]+=f[tn][2]+f[tn][0];
            //f[x][3]无法转移
        }
        
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<n;++i)
    {
        int x,y,z;
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    dp(1,0);
    printf("%lld\n",ans);
    return 0;
}
View Code

值得一提的是 这道题有并查集的做法 当然 我当然也是会看完题解口胡的。

显然我们直接的去分析答案 答案的来源三种 全是1 全是0 一段0 一段1 然后 对于前两者都是直接统计联通分量即可。

后者 使用枚举法 因为一段0一段1 一定是由某个点连接的 然后其实我们只需要求出连接某个点的所有连续0连续1 的个数相乘即可。

维护整个联通分量显然可以使用并查集维护。简单方便。好写。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=200010;
int n,len;
ll ans,sz[MAXN][2];
int f[MAXN][2];
inline int getfather(int x,int y)
{
    return f[x][y]==x?x:f[x][y]=getfather(f[x][y],y);
}
inline void merge(int x,int y,int z)
{
    int xx=getfather(x,z);
    int yy=getfather(y,z);
    sz[yy][z]+=sz[xx][z];
    f[xx][z]=yy;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;++i)f[i][0]=f[i][1]=i,sz[i][0]=sz[i][1]=1;
    for(int i=1;i<n;++i)
    {
        int x,y;
        x=read();y=read();
        merge(x,y,read());
    }
    for(int i=1;i<=n;++i)
    {
        int w1=getfather(i,0);
        int w2=getfather(i,1);
        if(w1==i)ans+=sz[w1][0]*(sz[w1][0]-1);
        if(w2==i)ans+=sz[w2][1]*(sz[w2][1]-1);
        ans+=(sz[w1][0]-1)*(sz[w2][1]-1);
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

经过这道题我们 可以发现从答案的角度进行分析和处理可以使问题变得简单化。
CF1287B  map这道题主要考map的使用当时关键没读懂题目 所以自闭了。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define ld long double
#define INF 1000000000
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define EPS 1e-7
using namespace std;
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=1510;
int n,m;
ll ans;
char a[MAXN][35];
ll c[MAXN];
map<ll,int>H;
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n;++i)
    {
        scanf("%s",a[i]+1);
        for(int j=1;j<=m;++j)
        {
            int w;
            if(a[i][j]=='S')w=0;
            if(a[i][j]=='E')w=1;
            if(a[i][j]=='T')w=2;
            c[i]=c[i]*3+w;
        }
        ++H[c[i]];
    }
    for(int i=1;i<=n;++i)
    {
        for(int j=i+1;j<=n;++j)
        {
            ll v=0;
            for(int k=1;k<=m;++k)
            {
                int w,w1,w2;
                if(a[i][k]=='S')w=0;
                if(a[i][k]=='E')w=1;
                if(a[i][k]=='T')w=2;
                if(a[j][k]=='S')w1=0;
                if(a[j][k]=='E')w1=1;
                if(a[j][k]=='T')w1=2;
                if(w==w1)w2=w;
                else w2=3-w-w1;
                v=v*3+w2;
            }
            ans+=H[v];
            if(c[i]==c[j])ans-=2;
        }
    }
    printf("%lld\n",ans/3);
    return 0;
}
View Code

CF1286A 贪心题 (反正我写了一个贪心 好长好长 。

细节有点ex了。真的 注意判断n==1的情况 坑了我半个小时多 导致掉分了 下次一定要注意边界。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000000000000ll
#define ld long double
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=1010;
int n,cnt,flag;
int a[MAXN];
struct wy
{
    int l,r;
    int p;
    inline int friend operator <(wy a,wy b)
    {
        return a.p<b.p;
    }
}t[MAXN];
int w,w1,ans;
int k1,k2,p1,p2,h1,h2;
inline void check(int x)
{
    if(!h1&&p1)
    {
        if(k1==x)
        {
            if(!x)
            {
                if(w>=p1)w-=p1,h1=1;
                else 
                {
                    h1=1;
                    ++ans;
                }
            }
            else
            {
                if(w1>=p1)w1-=p1,h1=1;
                else
                {
                    h1=1;
                    ++ans;
                }
            }
        }
    }
    if(!h2&&p2)
    {
        if(k2==x)
        {
            if(!x)
            {
                if(w>=p2)w-=p2,h2=1;
                else 
                {
                    h2=1;
                    ++ans;
                }
            }
            else
            {
                if(w1>=p2)w1-=p2,h2=1;
                else
                {
                    h2=1;
                    ++ans;
                }
            }
        }
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;++i)
    {
        a[i]=read();
        if(a[i])flag=1;
        if(a[i]&&a[i-1])if(a[i]%2!=a[i-1]%2)++ans;
        if(a[i]&1)++w1;
        if(a[i]%2==0&&a[i])++w;
    }
    if(n==1){puts("0");return 0;}
    if(!flag){puts("1");return 0;}
    for(int i=1;i<=n;++i)
    {
        if(!a[i])
        {
            int j=i+1;
            while(!a[j]&&j<=n)++j;
            if(i-1>=1&&j<=n&&a[i-1]%2==a[j]%2)
            {
                t[++cnt].l=a[i-1];
                t[cnt].r=a[j];
                t[cnt].p=j-i;
            }
            else 
            {
                if(i-1==0)
                {
                    k1=a[j]&1;
                    p1=j-i;
                    i=j-1;
                    continue;
                }
                if(j==n+1)
                {
                    k2=a[i-1]&1;
                    p2=j-i;
                    i=j-1;
                    continue;
                }
                ++ans;
            }
            i=j-1;
        }
    }
    w=n/2-w;
    w1=n/2-w1;
    if(n&1)++w1;
    sort(t+1,t+1+cnt);
    for(int i=1;i<=cnt;++i)
    {
        if(t[i].l%2==0&&t[i].r%2==0)
        {
            if(w>=t[i].p)w-=t[i].p;
            else
            {
                check(0);
                ans+=2;
            }
            continue;
        }
        else
        {
            if(t[i].l%2==1&&t[i].r%2==1)
            {
                if(w1>=t[i].p)w1-=t[i].p;
                else
                {
                    check(1);
                    ans+=2;
                }
            }
            continue;
        }
    }
    check(1);check(0);
    if(!h1&&p1)++ans;
    if(!h2&&p2)++ans;
    printf("%d\n",ans);
    return 0;
}
View Code

其实不是贪心 我想成贪心了 不过贪心的复杂度是nlogn的 但是面对n等于100的情况我们难免会想到n^3的dp...

dp好写的多其实.也没有那么多ex的细节 超级简单.

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define ld long double
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define EPS 1e-8
#define ui unsigned int
#define pb push_back
#define RI register int
#define INF 1000000000
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=110;
int n;
int w,w1;
int a[MAXN];
int f[MAXN][MAXN][MAXN][2];
int main()
{
    //freopen("1.in","r",stdin);
    memset(f,0x3f,sizeof(f));
    n=read();
    for(int i=1;i<=n;++i)
    {
        a[i]=read();
        if(a[i]&1)++w1;
        else if(a[i])++w;
    }
    w1=n/2-w1;if(n&1)++w1;
    w=n/2-w;
    f[0][w][w1][0]=f[0][w][w1][1]=0;
    for(int i=1;i<=n;++i)
    {
        if(a[i])
        {
            int g=a[i]&1;
            for(int j=0;j<=w;++j)
                for(int k=0;k<=w1;++k)
                {
                    if(g)f[i][j][k][1]=min(f[i-1][j][k][0]+1,f[i-1][j][k][1]);
                    else f[i][j][k][0]=min(f[i-1][j][k][0],f[i-1][j][k][1]+1);
                }
            continue;
        }
        for(int j=w;j>=0;--j)
            for(int k=w1;k>=0;--k)
            {
                f[i][j][k][1]=min(f[i-1][j][k+1][0]+1,f[i-1][j][k+1][1]);
                f[i][j][k][0]=min(f[i-1][j+1][k][0],f[i-1][j+1][k][1]+1);
            }
    }
    printf("%d\n",min(f[n][0][0][0],f[n][0][0][1]));
    return 0;
}
View Code

CF 1287D 构造题(用的是我一个同学的思路 我又把问题搞复杂了 其实 这道题就是考构造。首先一个性质显然是子数和子树之间是不影响的 所以说对于一个点x我们暴力找到$c[i]$个点即可 子数内之中还有一个相对的顺序我们保证这个顺序。

然后倒着找点就行了 子树和子树之间暴力拼起来 然后最后赋值即可。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define ld long double
#define INF 1000000000
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define EPS 1e-8
#define mod 1000000007
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=2010;
int n,m,rt;
int c[MAXN],flag;
int lin[MAXN],ver[MAXN],nex[MAXN];
int sz[MAXN];
int a[MAXN],cnt,len;
vector<int>g[MAXN];
inline void add(int x,int y)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
}
inline void dfs(int x)
{
    if(flag)return;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        dfs(tn);
        sz[x]+=sz[tn];
        for(unsigned int j=0;j<g[tn].size();++j)g[x].push_back(g[tn][j]);
    }
    if(sz[x]<c[x]||flag){flag=1;return;}
    ++sz[x];
    if(!g[x].size())g[x].push_back(x);
    else
    {
        int w=g[x].size();cnt=-1;
        for(int i=0;i<w;++i)a[++cnt]=g[x][i];
        g[x].clear();w-=c[x];
        for(int i=0;i<=w-1;++i)g[x].push_back(a[i]);
        g[x].push_back(x);
        for(int i=w;i<=cnt;++i)g[x].push_back(a[i]);
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;++i)
    {
        int x=read();
        if(!x)rt=i;
        else add(x,i);
        c[i]=read();
    }
    dfs(rt);cnt=0;
    if(flag)puts("NO");
    else
    {
        puts("YES");
        for(int i=g[rt].size()-1;i>=0;--i)a[g[rt][i]]=++cnt;
        for(int i=1;i<=n;++i)printf("%d ",a[i]);
    }
    return 0;
}
View Code
CF 1286C1 这题可太秒了给我半个小时也不一定能想出来 还是思维具有局限性 这是我做的第一道交互的题目。
我们能询问三次 然后总询问后输出的子串个数不能超过$(n+1)^2$个 然后最后输出整个序列的字符串是什么。
很nb的题目说实话... 正解是询问1~n 再询问2~n 可以证明是不超过$(n+1)^2$个的 也可以通过这么多的个数来判断出来的。
首先我们知道了1~n的单个子串 之后我们又知道了2~n的单个子串所以可以得到 1 再通过2个的判断出3 就这样一直下去即可判断出来所有的东西.
有一些小trick 要是我来写这道题的话我写的方法可能非常的复杂 map的其他用法我一直都没用过 遍历map也很少使用。
这道题中 可以先把所有的东西放到map里注意size分一下sort一下因为可能是乱序的 然后 遍历map map1-map2得到少的那一个 然后结合以前的答案 快速算出。
总复杂度 $n^2$(平衡树遍历我记得好像是O(n)来着...
//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define ld long double
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define EPS 1e-8
#define ui unsigned int
#define pb push_back
#define RI register int
#define INF 1000000000
using namespace std;
const int MAXN=110;
int n;
string a,w;
string ans;
int c[MAXN<<1];
map<string,int>H1[MAXN],H2[MAXN];
map<string,int>::iterator it;
inline void get()
{
    for(int i=0;i<w.size();++i)--c[w[i]];
    for(int i='a';i<='z';++i)if(c[i])ans+=(char)i,c[i]=0;
    for(int i=0;i<ans.size();++i)++c[ans[i]];
}
int main()
{
    //freopen("1.in","r",stdin);
    ios::sync_with_stdio(false);
    cin>>n;
    if(n==1)
    {
        cout<<"? 1 1"<<endl;
        cin>>a;
        cout<<"! "<<a[0]<<endl;
        return 0;
    }
    cout<<"? 1 "<<n<<endl;
    for(int i=1;i<=n*(n+1)/2;++i)
    {
        cin>>a;
        sort(a.begin(),a.end());
        //cout<<a<<endl;
        ++H1[a.size()][a];
    }
    cout<<"? 2 "<<n<<endl;
    for(int i=1;i<=n*(n-1)/2;++i)
    {
        cin>>a;
        sort(a.begin(),a.end());
        ++H2[a.size()][a];
        //cout<<a<<endl;
    }
    for(int i=1;i<=n;++i)
    {
        w="";
        it=H1[i].begin();
        while(it!=H1[i].end())
        {
            H1[i][(*it).first]-=H2[i][(*it).first];
            ++it;
        }
        it=H1[i].begin();
        while(it!=H1[i].end())
        {
            if((*it).second>0)
            {
                w+=(*it).first;
                //cout<<w<<endl;
                break;
            }
            ++it;
        }
        get();
    }
    cout<<"! ";
    for(int i=0;i<ans.size();++i)cout<<ans[i];
    return 0;
}
View Code

当然要注意迭代器的使用 map的两个键位通过first 和second来判断。

 CF 128C2 这道题目和刚才唯一的区别就是让我们询问的子串不能超过$[0.777(n+1)^2]$ 刚才取数的方法是用不了的,因为这样我们取数会取得更多考虑另外一种取法.

遇到这类题目只能不断的去尝试 但是我们已知的是 知道 $[1,n-1] [1.n]$的区间便可以知道整个区间的答案 我们依照这个来判断。

首先我们肯定不能选择上面那道题那么大的区间我们先选一个[1,n] 判断一下超出限制了没有 可以发现我们接下来还能再选择一个$[1,\frac{n}{2}]$选出这个后我们还能选择什么呢?显然我们再选择一个$[1,\frac{n}{2}+1]$就可以得到后者区间的所有信息。

可以发现接下来靠乱搞 通过计算我们发现并没有超过限制 且我们只需要把答案判断出来即可。可以发现我们利用第一个选择的东西 用[1,n]的所有1字符的两倍减去所有长度为2的字符串可以得到最后一个字符。

然后用1的三倍减去3等等一直下去就可以得到所有的答案了。就是这样...

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define ld long double
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define EPS 1e-8
#define ui unsigned int
#define pb push_back
#define RI register int
#define INF 1000000000
using namespace std;
const int MAXN=110;
int n,l,r;
string a,w;
string ans,ans1;
int c[150];
map<string,int>H1[MAXN],H2[MAXN],H3[MAXN];
map<string,int>::iterator it;
inline void get()
{
    for(int i=0;i<w.size();++i)--c[w[i]];
    for(int i='a';i<='z';++i)if(c[i])ans+=(char)i,c[i]=0;
    for(int i=0;i<ans.size();++i)++c[ans[i]];
}
inline void get_c()
{
    for(int i='a';i<='z';++i)if(c[i])ans1+=(char)i,c[i]=0;
}
int main()
{
    //freopen("1.in","r",stdin);
    ios::sync_with_stdio(false);
    cin>>n;
    if(n==1)
    {
        cout<<"? 1 1"<<endl;
        cin>>a;
        cout<<"! "<<a[0]<<endl;
        return 0;
    }
    if(n==2)
    {
        cout<<"? 1 1"<<endl;
        cin>>a;
        cout<<"? 2 2"<<endl;
        cin>>w;
        cout<<"! "<<a[0]<<w[0]<<endl;
        return 0;
    }
    l=n>>1;r=l+1;
    cout<<"? 1 "<<n<<endl;
    for(int i=1;i<=n*(n+1)/2;++i)
    {
        cin>>a;
        sort(a.begin(),a.end());
        //cout<<a<<endl;
        ++H1[a.size()][a];
    }
    cout<<"? 1 "<<l<<endl;
    for(int i=1;i<=l*(l+1)/2;++i)
    {
        cin>>a;
        sort(a.begin(),a.end());
        ++H2[a.size()][a];
        //cout<<a<<endl;
    }
    cout<<"? 1 "<<r<<endl;
    for(int i=1;i<=r*(r+1)/2;++i)
    {
        cin>>a;
        sort(a.begin(),a.end());
        ++H3[a.size()][a];
        //cout<<a<<endl;
    }
    for(int i=1;i<=r;++i)
    {
        w="";
        it=H3[i].begin();
        while(it!=H3[i].end())
        {
            H3[i][(*it).first]-=H2[i][(*it).first];
            ++it;
        }
        it=H3[i].begin();
        while(it!=H3[i].end())
        {
            if((*it).second>0)
            {
                w+=(*it).first;
                //cout<<w<<endl;
                break;
            }
            ++it;
        }
        get();
    }
    cout<<"! ";
    for(int i=0;i<ans.size()/2;++i)swap(ans[i],ans[ans.size()-i-1]);
    ans+='1';for(int i=ans.size()-1;i>=1;--i)swap(ans[i],ans[i-1]);
    //for(int i=1;i<=ans.size();++i)cout<<ans[i];
    //此时已知1~r之间的字母;
    //cout<<ans1.size()<<endl;
    for(int i=2;i<=n;++i)
    {
        if(ans.size()-1+ans1.size()==n)break;
        w="";
        memset(c,0,sizeof(c));
        it=H1[1].begin();
        while(it!=H1[1].end())
        {
            for(int j=0;j<(*it).first.size();++j)c[(*it).first[j]]+=i*(*it).second;
            ++it;
        }
        it=H1[i].begin();
        while(it!=H1[i].end())
        {
            for(int j=0;j<(*it).first.size();++j)c[(*it).first[j]]-=(*it).second;
            ++it;
        }
        for(int j=1;j<=i-1;++j)
        {
            c[ans[j]]-=(i-j);
            if(j!=i-1)c[ans1[j-1]]-=(i-j);
        }
        get_c();
    }
    for(int i=ans1.size()-1;i>=0;--i)ans+=ans1[i];
    for(int i=1;i<ans.size();++i)cout<<ans[i];
    return 0;
}
View Code

CF1288C 这道题组合数的做法我先咕了不是很想自己研究了 简单说一下dp的做法 :设一个状态 然后是$mn^4$的转移 前缀和优化然后简单容斥一下就完成了具体细节见code。

不过看起来这个容斥还是有点难度的。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>    
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define ld long double
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define EPS 1e-12
#define INF 1000000000000000ll
#define mod 1000000007
#define ull unsigned long long
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const ll MAXN=1010;
ll n,m;
ll sum,ans,cnt;
ll w[MAXN];
ll f[11][MAXN][MAXN];
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    f[0][1][n]=1;
    for(ll i=1;i<=m;++i)
    {
        sum=0;cnt=0;
        for(int j=1;j<=n;++j)w[j]=0;
        for(ll j=1;j<=n;++j)
        {
            cnt=0;
            for(ll k=1;k<=n;++k)
            {
                cnt=(cnt+f[i-1][j-1][k])%mod;
                w[k]=(w[k]+cnt)%mod;
            }
            for(ll k=n;k>=j;--k)
            {
                if(j<=k)sum=(sum+f[i-1][j][k])%mod;
                if(j<=k)f[i][j][k]=(f[i][j][k]+sum-w[k-1])%mod;
            }
            //for(ll k=n-1;k>=j;--k)sum=(sum-f[i-1][j][k])%mod;
        }
    }
    for(ll j=1;j<=n;++j)
    {
        for(ll k=1;k<=n;++k)
        {
            if(j<=k)ans=(ans+f[m][j][k])%mod;
        }
    }
    printf("%lld\n",(ans+mod)%mod);
    return 0;
}
View Code

我知道组合数的做法 我没有认真分析题目中的条件导致写了一个极其麻烦的dp 我觉得组合数才是正解..

具体的 题目中提到$a_i$这个序列是递增的$b_i$这个序列是递减的 且对于任意的i来说都有$a_i \queiv b_i$ 那么说明了 $a_m \queiv b_m$又有b数组递减可知b整体大于a数组 也就是形成了我们选2m个数字按大小分配给a数组和b数组即可。

显然值域为$[1,n]$问题的模型变成了 有n个数字每个数字有无限个分给2m个数字 隔板法的基本应用。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>    
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define ld long double
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define EPS 1e-12
#define INF 1000000000
#define ull unsigned long long
#define mod 1000000007
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=1100;
int n,m;
int maxx=1030;
ll fac[MAXN],in[MAXN];
inline int fast_pow(ll b,ll p)
{
    ll ans=1;
    while(p)
    {
        if(p&1)ans=ans*b%mod;
        b=b*b%mod;
        p=p>>1;
    }
    return ans;
}
inline void prepare()
{
    fac[0]=1;
    for(int i=1;i<=maxx;++i)fac[i]=fac[i-1]*i%mod;
    in[maxx]=fast_pow(fac[maxx],mod-2);
    for(int i=maxx-1;i>=0;--i)in[i]=in[i+1]*(i+1)%mod;
}
inline ll C(int a,int b)
{
    return fac[a]*in[b]%mod*in[a-b]%mod;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    prepare();
    printf("%lld\n",C(n+2*m-1,n-1));
    return 0;
}
View Code

CF1288D 这道题主要讲我偶然间想到一个枚举子集的妙用 这道题先二分显然把每一行当成一个二进制数问题就转换成两个二进制数或起来等于全集,trie树无法解决 具体的trie树只能解决异或的问题 按位与与或的问题其很难解决。

a^b=maxx 强行把b移到右边 a=maxx^b 显然有maxx^b包含a即可 准确来说是 &a==a这个时候我对每一个b都枚举子集然后标记一下至少有这样的b出现即可。复杂度2^8*logn单论这一步的复杂度。我觉得挺秒的。。

CF1288E 这道题就很有意思了 我不知道题面怎么回事看半天每看懂 看样例秒懂 估计我忽略了某个英语单词导致全盘崩溃?

大致意思是每次指定一个数字将这个数字提前剩下的数字依次放到后面最后求出每个数字最前的位置和最后的位置 这个 由于我是直接听别人讲了没有认真思考得到的解法果然空虚 我应该自己知道题目意思的时候多想想而不是直接听别人的做法。

这道题 特异性也很明显最前的位置 如果不修改的话那么一定是其最初的位置 相反一旦修改那必然是1 所以针对于修改和不修改我们已经可以光从操作上判断了。考虑末位的位置一个数字如果不修改的话那么其一定不会提前这是必然的只会往后走 我们可以在最后再查找它前面到底有多少个数字 对于要修改的数字来说 其最小位置固定最晚的位置我们要在修改的时候就要查询了 因为最后是查不到的 我们两种情况都讨论完了 那么这道题也就做完了。

至于模拟操作的话 其实我们可以在线段树上预留几个位置然后直接插入位置单点移动转变为区间移动即可。懒省事我直接树状数组了qwq.

这里我使用了O(n)初始化树状数组早就想学了发现很简单只要明白树状数组的原理即可 我们知道数状数组$c[i]$表示的i的最低二进制的和不过下标是整体的意思 维护一个前缀和 $c[i]=sum[i]-sum[i-lowbit(i)]$我们按照其真实的含义做即可。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>    
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define ld long double
#define put(x) printf("%d\n",x)
#define EPS 1e-12
#define INF 1000000000
#define ull unsigned long long
#define mod 1000000007
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=300010;
int n,m,fir;
int c[MAXN<<1],sum[MAXN<<1],pos[MAXN];
int mx[MAXN],mn[MAXN];
inline int ask(int x)
{
    int cnt=0;
    while(x)
    {
        cnt+=c[x];
        x-=x&(-x);
    }
    return cnt;
}
inline void add(int x,int y)
{
    while(x<=n+m)
    {
        c[x]+=y;
        x+=x&(-x);
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();fir=m=read();
    for(int i=1;i<=n;++i)
    {
        mn[i]=mx[i]=i,pos[i]=m+i;
        sum[m+i]=1+sum[m+i-1];
    }
    for(int i=1;i<=n;++i)
        c[m+i]=sum[m+i]-sum[(m+i)-((m+i)&(-m-i))];
    for(int i=1;i<=m;++i)
    {
        int x=read();
        mn[x]=1;
        int w=ask(pos[x]);
        mx[x]=max(mx[x],w);
        add(pos[x],-1);
        pos[x]=fir;--fir;
        add(pos[x],1);
    }
    for(int i=1;i<=n;++i)
    {
        mx[i]=max(mx[i],ask(pos[i]));
        printf("%d %d\n",mn[i],mx[i]);
    }
    return 0;
}
View Code

 (CF终于上蓝了 尽管非常的丢人...不行下次我一定要仔细认真做题!!! 争取在省选前上红.

CF1295D 这道题是一个容斥.题目大意:存在等式gcd(a,b)==gcd(a+x,b) 0<=x<m 求这样的x有多少个。

看起来很难做 但是要先化简这个式子 设 d=gcd(a,b) 那么显然 d|a+x d|b d|a d|x 我们发现了d是x的因数 暴力枚举x 判断blogn

观察一下 gcd(a+x,b)==d 我们把b/d (a+x)/d 那么这两个数gcd==1 说明了 (a+x)/d b/d 是互质的 我们考虑一下a+x的范围 [a,a+b-1] 这个范围我们直接让这个范围除以d

现在我们得到了一个范围 [x,y] 问题转换成了这个区间和 b/d这个数字互质的数字个数。

显然的 我们求1~y这个范围内和b/d互质的数字和1~x-1这个范围内和b/d互质的个数即可。

还是很难求 这不是欧拉函数,当然我们欧拉函数普通筛也是容斥得来的... 考虑容斥我们设 b/d有 k个质因子 此时考虑换一个问题求1~y中有多少数字和b/d的gcd>=2

我们用这k个质因数的一些组合去筛 奇数的加上偶数的减去即可得到答案。

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<utility>
#include<cctype>
#include<string>
#include<bitset>
#include<vector>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<list>
#include<set>
#include<map>
#define INF 1000000000
#define ll long long
#define mod 10007
#define pb push_back
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ll T;
ll x,y;
vector<ll>p;
inline ll gcd(ll a,ll b)
{
    return b?gcd(b,a%b):a;
}
inline ll calc(ll x,ll w)
{
    p.clear();
    for(ll i=2;i*i<=w;++i)
        if(w%i==0)
        {
            while(w%i==0)w/=i;
            p.pb(i);
        }
    if(w>1)p.pb(w);
    ll ans=0;
    for(ll i=1;i<(1<<p.size());++i)
    {
        ll cnt=0,s=1;
        for(ll j=0;j<p.size();++j)
        {
            if(i&(1<<j))
            {
                s=s*p[j];
                ++cnt;
            }
        }
        if(cnt&1)ans+=x/s;
        else ans-=x/s;
    }
    return x-ans;
}
int main()
{
    //freopen("1.in","r",stdin);
    T=read();
    while(T--)
    {
        x=read();y=read();
        ll g=gcd(x,y);
        ll R=x+y-1;
        y=y/g;
        ll L=x/g;
        R=R/g;
        printf("%lld\n",calc(R,y)-calc(L-1,y));
    }
    return 0;
}
View Code

CF1295E 这题我应该想了20min还是太菜了 不能及快速想出来。我发现题目并不算特别难 但是限制我的可能是英语。

这道题目的意思是给出一个排列 让你人为的从n-1个空隙中选出一个空隙分为两个集合 然后你可以将一个数字从一个集合移动到另一个集合花费的代价是题目给出是这个数字的v[i] 最终使得左边集合的max<右边集合的min 求形成这种局面的最小代价 值得一提的是如果一个集合为空那么也被认为是合法的。

求最小代价 二分?发现没用考虑暴力 枚举从哪个地方分开 仔细分析题目中的那个性质其实是想让我们判断左边集合最终会变成多少个数字右边集合也是同理知道了这个东西我们就可以求代价了但是枚举这个也是O(n)的总复杂度n^2考虑优化 我们发现是否可以二分左边最终会有多少个数字 这类似于一个单峰函数 很遗憾的是不是单峰函数 得想出另外一个方法 我想了很久最终发现了 是否可以先枚举左边划分多少个右边划分多少个然后指针向右移动也就是把刚才的过程翻转 于是用线段树维护这个划分数字的多少的集合 指针向右移动的时候 利用线段树进行区间修改 复杂度就变成了logn 很妙对不对从这道题目中告诉我有的时候可以考虑将暴力翻转再进行优化...

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<utility>
#include<cctype>
#include<string>
#include<bitset>
#include<vector>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<list>
#include<set>
#include<map>
#define INF 10000000000000000ll
#define ll long long
#define mod 10007
#define pb push_back
#define l(x) s[x].l
#define r(x) s[x].r
#define sum(x) s[x].sum
#define tag(x) s[x].tag
#define zz p<<1
#define yy p<<1|1
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const ll MAXN=200010;
ll n,id;
ll c[MAXN],ans;
ll a[MAXN],v[MAXN],root[MAXN],sum[MAXN];
struct wy
{
    ll l,r;
    ll sum;
    ll tag;
}s[MAXN<<2];// s为线段树
inline void build(ll p,ll l,ll r)
{
    l(p)=l;r(p)=r;
    if(l==r){sum(p)+=c[l];return;}
    ll mid=(l+r)>>1;
    build(zz,l,mid);
    build(yy,mid+1,r);
    sum(p)=min(sum(zz),sum(yy));
}
inline void pushdown(ll p)
{
    sum(zz)+=tag(p);
    sum(yy)+=tag(p);
    tag(zz)+=tag(p);
    tag(yy)+=tag(p);
    tag(p)=0;
}
inline void change(ll p,ll l,ll r,ll w)
{
    if(l>r)return;
    if(l<=l(p)&&r>=r(p))
    {
        sum(p)+=w;
        tag(p)+=w;
        return;
    }
    ll mid=(l(p)+r(p))>>1;
    if(tag(p))pushdown(p);
    if(l<=mid)change(zz,l,r,w);
    if(r>mid)change(yy,l,r,w);
    sum(p)=min(sum(zz),sum(yy));
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(ll i=1;i<=n;++i)a[i]=read();
    for(ll i=1;i<=n;++i)v[i]=read();
    for(ll i=1;i<=n;++i)sum[a[i]]+=v[i];
    for(ll i=1;i<=n;++i)sum[i]+=sum[i-1];
    //先计算出以第一个空隙为间隔的所有答案
    for(ll i=0;i<=n;++i)c[i]=sum[i]-(a[1]<=i?v[1]:-v[1]);
    build(1,0,n);ans=sum(1);
    for(ll i=2;i<=n-1;++i)
    {
        change(1,a[i],n,-v[i]);
        change(1,0,a[i]-1,v[i]);
        ans=min(ans,sum(1));
    }
    printf("%lld\n",ans);
    return 0;
}
View Code

CF1291D 这道题的题目意思很难懂 大体上就是给定一个字符串 Q次询问 每次询问这个字符串的子串问能否找到给当前子串进行重新排列形成一个irreducible anagram。鬼的很... 其实是 题目先定义了一个串是另一个串的anagram 当且仅当能被分成k段(k>=2) 没一段都要非空 且每一段对应起来都应该是相同字母异序词 求是否存在一个irreducible anagram 就是不能这样划分起来对应的串 回答Yes 或No 即可。$n\leq 200000$

这道题就是手玩 考虑n==1 一定是Yes 显然。 考虑什么时候还会是Yes 我们发现当出现三种字符及以上的时候一定是Yes.

手玩啊 当然也有证明:我们一定能够在这个子串中找到中间两个相邻最近的且不同的字符把他们和开头分别结尾交换 当然是不能分别和开头和结尾相等的那种。

那么我们发现一种字母是No 两种呢?手玩一组就能看出门道 当首尾两个字符相同时一定是No 自证不难。那么不同的时候一定是Yes 自证也不难。

综上这道题解决方法是 一种字母一种字母判断即可。 当然要认认真真的读题 把题意理解清楚了.

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<utility>
#include<cctype>
#include<string>
#include<bitset>
#include<vector>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<list>
#include<set>
#include<map>
#define INF 2147483647
#define ll long long
#define pb push_back
#define M 998244353
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=200010;
char a[MAXN];
int n;
int t[MAXN][26];
int main()
{
    //freopen("1.in","r",stdin);
    scanf("%s",a+1);
    n=strlen(a+1);
    for(int i=1;i<=n;++i)
    {
        for(int w=0;w<=25;++w)t[i][w]+=t[i-1][w];
        ++t[i][a[i]-'a'];
    }
    n=read();
    for(int i=1;i<=n;++i)
    {
        int ans=0;
        int l,r;
        l=read();r=read();
        if(r==l){puts("Yes");continue;}
        if(a[l]!=a[r]){puts("Yes");continue;}
        for(int w=0;w<=25;++w)if(t[r][w]-t[l-1][w])++ans;
        if(ans>2){printf("Yes\n");continue;}
        printf("No\n");
    }
    return 0;
}
View Code

CF708C 给定一颗树可以加一条边删一条边 求第i个点能否成为重心。

好题 可以具体分析一下重心的性质来做 我们知道一个点如果是重心答案必然为Yes 反之一定存在一颗子树大小大于$\frac{n}{2}$

我们只能删一条边加一条边 让其成为重心 那么显然删掉的那条边一定是儿子最多的那棵子树之中 设$d=\frac{n}{2}$ 儿子最多的子树为x

我们要在 x中找到一个儿子tn使得$sz[tn] \gep sz[x]-d$ 显然这个sz[tn]越小越好 然后在除x其他儿子中找到一颗子树w使得 $sz[w]+sz[tn]\leq d$即可。

这里是一个误区这个w可以为空 所以显然sz[w]=0,所以最终我们只需要判断一下自己的重儿子的子树内是否存在一棵子树大小在 d~sz[x]-d 之中即可.

显然主席树是完全能做这个事情的 但是我们可以不使用主席树+换根的方法 可以采用up and down 类型的树形dp 我们只需记录一下小于d的最大儿子数是多少即可.

要注意观察 询问的形式采用简而有效的方法解决问题而不是无脑采用麻烦的数据结构硬搞.

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<utility>
#include<cctype>
#include<string>
#include<bitset>
#include<vector>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<queue>
#include<deque>
#include<stack>
#include<list>
#include<set>
#include<map>
#define INF 1000000000
#define ll long long
#define pb push_back
#define mod 20101009
using namespace std;
char *fs,*ft,buf[1<<15];
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline ll read()
{
    ll x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=400010;
int n,len,d;
int sz[MAXN],son[MAXN],mx[MAXN],mxx[MAXN],w[MAXN];
int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1],ans[MAXN];
inline void add(int x,int y)
{
    ver[++len]=y;
    nex[len]=lin[x];
    lin[x]=len;
}
inline void dfs(int x,int father)
{
    sz[x]=1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        dfs(tn,x);
        sz[x]+=sz[tn];
        if(sz[tn]>sz[son[x]])son[x]=tn;
        //mx 某个子树中的最大的子树
        if(mx[tn]>mx[x])mx[x]=mx[tn],w[x]=tn;
        if(sz[tn]>mx[x]&&sz[tn]<=d)mx[x]=sz[tn],w[x]=tn;
    }
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        if(mx[tn]>mxx[x]&&tn!=w[x])mxx[x]=mx[tn];
        if(sz[tn]>mxx[x]&&sz[tn]<=d&&w[x]!=tn)mxx[x]=sz[tn];
    }
}
inline void dp(int x,int father,int v)//up and down
{
    if(sz[son[x]]>d||n-sz[x]>d)
    {
        if(sz[son[x]]>d){if(mx[son[x]]>=sz[son[x]]-d)ans[x]=1;}
        else if(v>=n-sz[x]-d)ans[x]=1;
    }
    else ans[x]=1;
    for(int i=lin[x];i;i=nex[i])
    {
        int tn=ver[i];
        if(tn==father)continue;
        int ww=v;
        ww=max(ww,n-sz[tn]<=d?n-sz[tn]:0);
        if(w[x]==tn)ww=max(ww,mxx[x]);
        else ww=max(ww,mx[x]);
        dp(tn,x,ww);//注意v的更新
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<n;++i)
    {
        int x,y;
        x=read();y=read();
        add(x,y);add(y,x);
    }
    d=n>>1;
    dfs(1,0);
    dp(1,0,0);
    for(int i=1;i<=n;++i)printf("%d ",ans[i]);
    return 0;
}
View Code

 CF1291F 这道题是交互题 我没想出来 好难想orz 是这样交互的 你可以询问 $\frac{2\cdot n^2}{k}$次 有一个集合S 当|S|>k时把最前面的pop掉。

每次你可以询问 x 是否和这个集合中的某个元素是相同的然后把x 插入到S集合中问n个数本质不同的个数.

看起来很难做的样子,认真分析一下这道题的性质 首先当k==1时我们能够询问$2\cdot n^2$次。

所以说 我们暴力枚举 两两询问即可。怎么判定答案?最蠢的方法使用并查集缩点 当然还有更快的做法我们规定一旦x 和y 是相同的 那么y的权值就让其等于0.

每个权值的消失都有前面第一个权值的出现 所以这样做是正确的。

我们把k扩展到 $k<=n$的情况 显然我们暴力的像上面询问的话显然不行,但是我们仍然考虑让每一个j都和前面的比对过 这样就能知道j的权值消失与否。

于是我们发现一个j最多和前面k个元素比对剩下的都消失了所以我们应该按照顺序来我们发现如果只让一个j比对的话那么对于j+1来说只能和k-1个元素比对了 这严重影响我们的有序性所以考虑每次都让一部分元素同时比对一部分元素,这启示我们 每次可以比对$\frac{k}{2}$种元素 这样我们每部分也就有序了 每组也是k/2个 考虑一下这样做我们要询问多少次。计算一下 发现能过 但是显然还可以优化 我们比对1,2  1,3  1,4 再比对 2,3 2,4 这显然我们在比对完1 2的时候直接往里面插3 这样询问次数会更少,也就是说这样的话比较次数会降到更低。我们发现显然次数可以通过hard version 这里只放后者做法。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>  
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
#define INF 1000000000
#define ll long long
#define max(x,y) (x>y?x:y)
#define min(x,y) (x>y?y:x) 
#define db double
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=1100;
int sz[MAXN],ans;
int n,k,id,op;
char a[2];
int b[MAXN];
vector<int>g[MAXN<<1];
inline void put(){cout<<"R"<<endl;}
inline void put(int x){cout<<"?"<<' '<<x<<endl;scanf("%s",a+1);op=a[1]=='Y'?1:0;}
inline void judge(int x){if(op)if(sz[x])--sz[x],--ans;}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();k=read();
    int w=k/2+(k&1);ans=n;
    int sum=0;id=1;
    for(int i=1;i<=n;++i)
    {
        ++sum;
        sz[i]=1;
        g[id].push_back(i);
        if(sum==w){++id;sum=0;}
    }--id;
    for(int len=1;len<=id;++len)
    {
        for(int i=1;i<=len;++i)
        {
            if(i+len>id)break;put();
            for(int j=0;j<g[i].size();++j)
            {
                put(g[i][j]);
                judge(g[i][j]);
            }
            for(int j=i+len;j<=id;j+=len)
            {
                for(int k=0;k<g[j].size();++k)put(g[j][k]),judge(g[j][k]);
            }
        }
    }
    cout<<"! "<<ans<<endl;
    return 0;
}
View Code

 CF1300D 这题我真的不想看太长了 下次一定要耐心看完 不会写再放弃(其实我是想先写E的 但是写E写到自闭...

 给出一个凸多边形 边数为n 让你把各个定点移到(0,0)求形成的图形的并集 一个新的多边形是否和原图形相似。

我们大概可以联想到一个东西 闵可夫斯基和 这个东西是两个欧几里得空间点集的和,也称这两个空间的膨胀集。

具体是什么东西两个图形A和B的闵可夫斯基和是$C={a+b|a\in A,b\in B}$ 我也不是很懂更形象一点的是 从原点向图形A内部的每一个点做向量,将图形B沿每个向量移动,所有的最终位置的并便是闵可夫斯基和(具有交换律)。

这道题目已经给了一个点集了 叫做S吧 对于$(x,y)\in S$ 我们要将(x,y)平移到(0,0) 相当于走了一个(-x,-y) 那么整体的S 形成一个新的点集合 或者说向量集合-S

这道题原来是让我们求S和-S的闵可夫斯基形成的图形是否和S是相似的。 按理来说我们求闵可夫斯基和形成的凸包一定有2n条边但是是相似的说明有边是共线的。

既然有边是共线的我们发现共线的时候一定是同一条边造成的 出现这样的情况是因为自己后面的那个边造成的 共线说明对边与其平行 不重合说明对边和其相等。

这说明了这个图形是一个 中心对称图形 感性理解也可以的出来的吧...

如何判断一个凸多边形是一个中心对称图形?观察样例我们发对称的点的坐标加起来都一样 然后自己随便证明一下就出来了。

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>  
#include<iomanip>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<utility>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
#include<deque>
#include<map>
#include<vector>
#include<ctime>
#define INF 1000000000000000000ll
#define ll long long
#define max(x,y) (x>y?x:y)
#define min(x,y) (x>y?y:x) 
#define db double
#define EPS 1e-6
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    register int x=0,f=1;register char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=100010;
int n;
int a[MAXN],b[MAXN];
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    if(n&1){puts("NO");return 0;}
    for(int i=1;i<=n;++i)a[i]=read(),b[i]=read();
    int xx=a[1]+a[n/2+1];int yy=b[1]+b[n/2+1];
    for(int i=1;i<=n/2;++i)
        if(a[i]+a[i+n/2]!=xx||b[i]+b[n/2+i]!=yy){puts("NO");return 0;}
    puts("YES");
    return 0;
}
View Code

 CF1301E 我发现我最讨厌写这种 二维矩阵的问题了...很烦...无奈 越是害怕什么就越要写什么。

这道题问的是一个矩阵中存在的最大logo是多少Q 是3e5 所以 这种题一般都是Qlogn的复杂度过的。。

显然要二分吧 二分完之后我们要判断的 的是某个区域内的极值 显然ST表 但是我却不太会这种写法 这次要好好尝试一番,,,考虑预处理

我们可以搞4个数组预处理 但是我觉得悬线法可以做这个东西那就悬线法吧 最后我们ST表数组这样开 f[i][j][k][l]表示 i j 这个点向右延伸2^i 向下延伸2^j的矩阵中的极大值

我们存的当然也是以某个点开始的最大值啦...很好就这样搞。

感觉悬线法 不是很好做 那就暴力一点采用4个数组 dp~! 学到了二维ST表的初始化 和查询 和我以前想的不太一样。。

先初始化一维 再大力初始化第二维+矩形 查询也是那个样子。。又犯sb错误 查半天错误 我还是太蠢了啊...

作为Punish 今天晚上不看番了 我是真的蠢。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000000000000ll
#define ld long double
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define mod 1000000007
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=510;
int n,m,ans,Q;
int Log[MAXN];
char a[MAXN][MAXN];
int f1[MAXN][MAXN];
int f2[MAXN][MAXN];
int f3[MAXN][MAXN];
int f4[MAXN][MAXN];
int f[MAXN][MAXN][10][10];//f[i][j][k][l] 表示 这个矩阵中的最大值
int g[MAXN][MAXN];//表示以i,j为起点的最大值
inline int check(int w,int x,int y,int xx,int yy)
{
    xx=xx-w;y=y+w-1;
    yy=yy-w;x=x+w-1;
    if(xx<x||yy<y)return 0;
    // 查询 x y xx yy这个矩阵内的最大值
    int w1=Log[xx-x+1],w2=Log[yy-y+1];
    xx=xx-(1<<w1)+1;yy=yy-(1<<w2)+1;
    //cout<<f[x][y][w1][w2]<<endl<<endl;
    int ans=max(f[x][y][w1][w2],f[xx][yy][w1][w2]);
    ans=max(ans,f[x][yy][w1][w2]);
    ans=max(ans,f[xx][y][w1][w2]);
    return ans>=w;
}
int main()
{
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    n=read();m=read();Q=read();
    for(int i=1;i<=n;++i)scanf("%s",a[i]+1);
    for(int i=2;i<=max(n,m);++i)Log[i]=Log[i>>1]+1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
        {
            if(a[i][j]!='R')continue;
            f1[i][j]=min(f1[i-1][j],min(f1[i][j-1],f1[i-1][j-1]))+1;
        }
    for(int i=1;i<=n;++i)
        for(int j=m;j>=1;--j)
        {
            if(a[i][j]!='G')continue;
            f2[i][j]=min(f2[i-1][j],min(f2[i][j+1],f2[i-1][j+1]))+1;
        }
    for(int i=n;i>=1;--i)
        for(int j=1;j<=m;++j)
        {
            if(a[i][j]!='Y')continue;
            f3[i][j]=min(f3[i+1][j],min(f3[i][j-1],f3[i+1][j-1]))+1;
        }
    for(int i=n;i>=1;--i)
        for(int j=m;j>=1;--j)
        {
            if(a[i][j]!='B')continue;
            f4[i][j]=min(f4[i+1][j],min(f4[i][j+1],f4[i+1][j+1]))+1;
        }
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            int w=min(f1[i][j],f2[i][j+1]);
            w=min(w,min(f3[i+1][j],f4[i+1][j+1]));
            if(!w)continue;
            f[i][j][0][0]=w;
            //cout<<f1[i][j]<<' '<<a[i][j]<<' '<<w<<endl;
            //cout<<i<<' '<<j<<' '<<w<<endl;
            ans=max(ans,w);
        }
    }
    for(int k=1;k<=Log[n];++k)
        for(int i=1;i<=n-(1<<k)+1;++i)
            for(int j=1;j<=m;++j)
                f[i][j][k][0]=max(f[i][j][k-1][0],f[i+(1<<(k-1))][j][k-1][0]);
    for(int k=0;k<=Log[n];++k)
        for(int l=1;l<=Log[m];++l)
            for(int i=1;i<=n-(1<<k)+1;++i)
                for(int j=1;j<=m-(1<<l)+1;++j)
                {
                    f[i][j][k][l]=max(f[i][j][k][l-1],f[i][j+(1<<(l-1))][k][l-1]);
                    //cout<<i<<' '<<j<<' '<<k<<' '<<l<<' '<<f[i][j][k][l]<<endl;
                }
    //cout<<f[3][3][0][1]<<endl;
    for(int i=1;i<=Q;++i)
    {
        int x,y,xx,yy;
        x=read();y=read();
        xx=read();yy=read();
        int l=0,r=min(n,m);
        while(l+1<r)
        {
            int mid=(l+r)>>1;
            if(check(mid,x,y,xx,yy))l=mid;
            else r=mid;
        }
        if(check(r,x,y,xx,yy))printf("%d\n",(r<<1)*(r<<1));
        else printf("%d\n",(l<<1)*(l<<1));
    }
    return 0;
}
View Code

CF1301F 好久没写F题了今天来水一题,题目是 一个网格 每个点都有一个颜色 每次可以传送到和自己颜色相同的一个格子 或者向四个方向走一格 Q次询问 查询两点之间的最短路。题目的意思算是很清晰 但是我没想出来怎么办才好...觉得思路有些漏洞。

但是为了对的起其题目 Super Jaber 笑死我了.....(此处笑容逐渐变得猥琐.....

我觉得还是将其A掉为好。

自己的想法:每次要么传送要么 跑路 那么显然 我们可以暴力的bfs 这样单次询问的复杂度为$n^2$

考虑有没有什么地方可以优化从算样例中我们也可以发现 答案是 节点-颜色-节点这样的形式的。

于是我们可以处理一下某个节点到某种颜色的最短路这样我们就能求出上述的值了。

其实我们发现了 要么直走 曼哈顿距离 要么 传送一次 于是我们就强制性求出其传送的是哪一种颜色 然后暴力计算答案即可。

问题的关键在于我们如何求出某个节点到某种颜色的最短距离 节点个数1e6 颜色个数40 显然有机会。

我们求某个节点到某种颜色的距离 不难想出bfs的时候复杂度$n^2$ 因为进队的点过多了。

但是 我们求颜色到某个节点的最短距离呢?发现是40*n的复杂度这样就过了。

还是思想 不够成熟 似乎被什么影响了 bfs时也要更新同颜色的这点我没在意。。当然我们再加入的显然也是满足bfs的队列的单调性的。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db long double
#define INF 1000000000
#define ld long double
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define mk make_pair
#define S second
#define F first
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=1010,maxn=41;
int c[MAXN][MAXN];
int n,m,k,Q,t,h;
int dis[MAXN][MAXN],mark[maxn];
int b[MAXN][MAXN][maxn];
int x[MAXN*MAXN],y[MAXN*MAXN];
int dx[5]={0,0,0,1,-1};
int dy[5]={0,1,-1,0,0};
vector<pair<int,int> >g[maxn];
inline void bfs()
{
    for(int w=1;w<=k;++w)
    {
        for(int i=1;i<=k;++i)mark[i]=0;
        t=h=0;mark[w]=1;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j)
            {
                if(w!=1)b[i][j][w-1]=dis[i][j];
                dis[i][j]=INF;
                if(c[i][j]==w)
                {
                    x[++t]=i;y[t]=j;
                    dis[i][j]=0;
                }
            }
        while(h++<t)
        {
            int tx=x[h],ty=y[h];
            if(!mark[c[tx][ty]])
            {
                int ww=c[tx][ty];
                mark[ww]=1;
                for(unsigned int j=0;j<g[ww].size();++j)
                {
                    if(dis[g[ww][j].F][g[ww][j].S]>dis[tx][ty]+1)
                    {
                        dis[g[ww][j].F][g[ww][j].S]=dis[tx][ty]+1;
                        x[++t]=g[ww][j].F;y[t]=g[ww][j].S;
                    }
                }
            }
            for(int i=1;i<=4;++i)
            {
                int xx=dx[i]+tx;
                int yy=dy[i]+ty;
                if(xx<1||yy<1||xx>n||yy>m)continue;
                if(dis[xx][yy]>dis[tx][ty]+1)
                {
                    dis[xx][yy]=dis[tx][ty]+1;
                    x[++t]=xx;y[t]=yy;
                }
            }
        }
    }
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            b[i][j][k]=dis[i][j];
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();k=read();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)c[i][j]=read(),g[c[i][j]].push_back(mk(i,j));
    bfs();
    Q=read();
    for(int i=1;i<=Q;++i)
    {
        int x,y,xx,yy;
        x=read();y=read();xx=read();yy=read();
        int ans=abs(xx-x)+abs(yy-y);
        for(int w=1;w<=k;++w)ans=min(ans,b[x][y][w]+b[xx][yy][w]+1);
        printf("%d\n",ans);
    }
    return 0;
}
View Code

每次打CF都出现各种各样的意外 要不是题目不会写 要不就是会写调不出来 我也很绝望啊。

还是智商不够高啊 要冷静分析题目啊啊。

LINK:CF1320D div1的D题 我果然不会写。

想了几天了 最后觉得不太好做这道题。题目中就给出了一个01串并且给出了一个变换110到011的变换。每次询问两个子串能否经过上述变换变换成相同的子串。

遇到这种问题先想暴力 我们如何变换可以使得两个串变成相同的?先判不合法情况即0的个数或1的个数要相等。

然后我们发现我们人为手玩都不太好计算这个东西不妨分析一波性质0每次移动两个位置那么我们可以分析0因为1有两个 0有一个。

0既然每次只能移动两个位置我们发现其位置的奇偶性一定是不变的。然后我们发现两个0交换前后的位置的所以每个0都有自己的活动范围。

所以我们可以发现既然后面的0不能到前面来 也就是说两个序列中0都是相对的 第一个0对应第一个0什么的。

我们得到0对应后可以发现位置的奇偶性需要对应如果奇偶性对应的话我们显然可以通过一个一个调整从而调整出来。

0都对应了1 必然也已经对应了 综上 其实就是判断0的个数是否相同0的奇偶性是否相同我们可以采用字符串hash解决这个问题。

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define db double
#define INF 1000000000
#define ld long double
#define pb push_back
#define put(x) printf("%d\n",x)
#define min(x,y) ((x)>(y)?(y):(x))
#define max(x,y) ((x)>(y)?(x):(y))
#define rep(p,n,i) for(ll i=p;i<=n;++i)
#define pii pair<ll,ll> 
#define F first
#define S second
#define mk make_pair
#define EPS 1e-7
#define P 13331ll
#define ll long long
#define mod 998244353
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=200010;
int n;
char a[MAXN];
ll ha[MAXN][2],w[MAXN],p[MAXN];
inline int get(int l,int r)
{
    return ((ha[r][l&1]-ha[l-1][l&1]*p[w[r]-w[l-1]])%mod+mod)%mod;
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();
    scanf("%s",a+1);p[0]=1;
    for(int i=1;i<=n;++i)
    {
        w[i]=w[i-1];p[i]=p[i-1]*P%mod;
        ha[i][0]=ha[i-1][0];
        ha[i][1]=ha[i-1][1];
        if(a[i]!='0')continue;
        ha[i][0]=(ha[i][0]*P+1)%mod;
        ha[i][1]=(ha[i][1]*P+1)%mod;
        if(i&1)ha[i][1]=ha[i][1]+1;
        else ha[i][0]=ha[i][0]+1;
        ++w[i];
    }
    n=read();
    for(int i=1;i<=n;++i)
    {
        int l,r,L,R,len;
        l=read();L=read();len=read();
        r=l+len-1;R=L+len-1;
        if(get(l,r)==get(L,R))puts("Yes");
        else puts("No");
        //cout<<get(l,r)<<endl;
        //cout<<get(L,R)<<endl;
    }
    return 0;
}
View Code

LINK:[CF R84 div2 D]

我想了10min 3 1 2这个形式不就无解了么 没有认真的看题目$p^k$的定义 在原基础上的 导致我后面写的代码也是现基础上的。所以会wa掉。

既然是原基础上的 那么3 1 2就有解了答案为3. 设环大小为s 可以发现当答案为K的时候 这个环会变成gcd(s,K)个小环 我们发现一旦有某个小环成立那么对答案造成贡献

所以做法就是枚举约数判断一下即可。

const ll MAXN=200010;
int n,T,top,ans;
int a[MAXN],c[MAXN],d[MAXN];
int b[MAXN],vis[MAXN],f[MAXN],w[MAXN];
inline int getfather(int x){return x==f[x]?x:f[x]=getfather(f[x]);}
inline void merge(int x,int y)
{
    int xx=getfather(x);
    int yy=getfather(y);
    if(xx==yy)return;
    f[xx]=yy;
}
inline void solve()
{
    for(int i=1;i<=top;++i)
    {
        if(i>=ans)return;
        if(top%i==0)
        {
            int w=top/i;
            for(int j=1;j<=i;++j)
            {
                int flag=0,fa=getfather(b[j]);
                for(int k=j+i,cc=2;cc<=w;++cc,k+=i)
                {
                    int ww=getfather(b[k%n==0?n:k%n]);
                    if(ww!=fa){flag=1;break;}
                }
                if(!flag){ans=i;return;}
            }
        }
    }
}
int main()
{
    //freopen("1.in","r",stdin);
    get(T);
    while(T--)
    {
        get(n);
        rep(1,n,i)f[i]=i,get(a[i]),vis[i]=0,w[i]=0;
        ans=INF;
        rep(1,n,i)
        {
            get(c[i]);
            if(w[c[i]])merge(w[c[i]],i);
            w[c[i]]=i;
        }
        rep(1,n,i)
        {
            if(vis[i])continue;
            vis[i]=1;int x=i;b[top=1]=i;
            while(!vis[a[x]])
            {
                b[++top]=a[x];
                vis[a[x]]=1;
                x=a[x];
            }
            solve();
        }
        put(ans);
    }
    return 0;
}
View Code

LINK:CFR84 div2 C

这题FST 了原因是瞎搞了一个算法上去 然后GG了没有认真思考。

操作不超过2nm次 不难想到我们让任何一个点移动到任何位置 次数不超过nm次 如果所有的点是一个点就好了 发现我们把所有的点先移到一个位置 然后就变成一个点要到任意位置了 第一步次数为n-1+m-1次 两部加起来刚好满足要求。

(也就是K个关键点没有用...

//#include<bits\stdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define ld long double
#define pb push_back
#define get(x) x=read()
#define gt(x) scanf("%d",&x)
#define put(x) printf("%d\n",x)
#define putl(x) printf("%lld\n",x)
#define gc(a) scanf("%s",a+1);
#define rep(p,n,i) for(RE int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int> 
#define F first
#define S second
#define mk make_pair
#define RE register
using namespace std;
char buf[1<<15],*fs,*ft;
inline char getc()
{
    return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
}
inline int read()
{
    RE int x=0,f=1;char ch=getc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    return x*f;
}
const int MAXN=210;
int T,n,m,k;
struct wy
{
    int x,y;
}t[MAXN],w[MAXN];
int vis[MAXN],minn,st,top,cnt;
char ans[MAXN*MAXN<<1];
int main()
{
    freopen("1.in","r",stdin);
    get(n);get(m);get(k);
    rep(1,k,i)
    {
        int x,y;
        get(x);get(y);
        t[i]=(wy){x,y};
    }
    rep(1,k,i)
    {
        int x,y;
        get(x);get(y);
        w[i]=(wy){x,y};
    }
    rep(1,n-1,i)ans[++top]='U';
    rep(1,m-1,i)ans[++top]='L';
    rep(1,n,i)
    {
        rep(1,m-1,j)
        if(i&1)ans[++top]='R';
        else ans[++top]='L';
        if(i<n)ans[++top]='D';    
    }
    put(top);printf("%s",ans+1);
    return 0;
}
View Code

 

posted @ 2019-09-08 21:02  chdy  阅读(681)  评论(0编辑  收藏  举报