好久没撸c,第一场回状态的题(埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛

题目链接:
埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛

A:Wasserstein Distance ##

题意:有2大堆柱状图的土(总体积相同,问从第一堆土移动到第二堆消耗最少的力气,
每堆土所耗的力气为k*|i-j|.
思路:相当于对于第一堆,如果土的数量大于第二堆土,每次把多的土全部都往后移。
如果第一堆土的数量少于第二堆土,那么相当把第二堆土从右边移到了左边。
移动是相对的,那么对于第一堆土少于第二堆土的时候,可以理解为把-数量的土移到了右边,那么此时就可以相当于贪心做法了。每一步多余出来或者缺少的移动k堆土,可正可负
那么对于每次走一个格子累加一个移动量即可,

#include
#include
#include
#include
#include
#include 
#include 
#include 
#include 
using namespace std;
const int maxn=1e5+7;
typedef long long ll;
int q1[maxn],q2[maxn];

int main()
{
    //e满足Note里提到的合法运算式
    //freopen("in.txt","r",stdin);
    int i,j;
    int T,k,t1,t2,t3,t4,f1,f2,f3,f4;
    int r,c;
    int n,m,K;
    scanf("%d",&T);
    ll F1,F2,C1,C2;
    while(T--){
        C1=C2=F1=F2=0;
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        scanf("%d",&q1[i]);
        for(i=1;i<=n;i++){
        scanf("%d",&q2[i]);
        }
        K=0;
        for(i=1;i<=n;i++){
            F1+=q2[i]-q1[i];
        C1+=abs(F1);
        }
        cout <return 0;
}

B:合约数

题意:给一颗以p作为根结点的树,找到这棵树的合约数个数,合约数(指对于结点i来说,i的子树值为结点i的约数且为合数那么称为i的合约数
然后求的结果是累和(i与对数的一个积的)
思路:启发式合并(不会。有时候找的东西如果不是很多,那么可以这么做,对于i结点记录一下到结点i的各个出现过合数且为i约数的个数,然后再回溯回来的时候再记录一次,那么就可以通过第二次-第一次得到这个子树中各个数字出现过的次数了。然后对于这个子树,枚举其约数即可得到其对数。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include
#include
#include
#include
#include
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn=1e5+7;
const int mod =1e9+7;
typedef long long ll;

vector<int>tree[20050];
int value[10050];
bool vis[20050];
int qq[20050];

int head[20500];
struct E{
    int next,v;
}e[105*maxn];  //链式前向星要开双倍;
int tot;
void init(){
    tot=0;
    memset(head,-1,sizeof(head));
}
void add(int u,int v){
    tot++;
    e[tot].v=v;
    e[tot].next=head[u];
    head[u]=tot;
}
vector<int>he[10005];
int get_prime(int n){    //素数打表
    memset(vis,0,sizeof(vis));
    int m=sqrt(n+0.5);
    for(int i=2;i<=m;i++)if(!vis[i])
        for(int j=i*i;j<=n;j+=i)vis[j]=1;
        int k,i;
        for(k=0,i=2;i<=n;i++){
            if(!vis[i]){
                qq[k++]=i;
            }
        }
        return k;
}
ll ans;
int in[maxn];
ll sum[maxn];
int dfs(int x,int fa){
    int i;
    for(i=0;i1;
    for(i=head[x];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(v==fa)continue;
        dfs(v,x);
    }
    for(i=0;ireturn 0;
}

int main()
{
    //e满足Note里提到的合法运算式
    //freopen("in.txt","r",stdin);
    //freopen("out1.txt","w",stdout);
    int i,j;
    int T,k,t1,t2,t3,t4,f1,f2,f3,f4;
    int r,c,t5;
    int n,m,K;
    cin >>T;
    int p;
    get_prime(10002);
    for(i=1;i<=10000;i++){ //这里为预处理出每个数字的约数
        for(j=1;j<=sqrt(i);j++){
            if(j*j==i){
            if(vis[j])
            he[i].push_back(j);
            }else if(i%j==0){
            if(vis[j])
            he[i].push_back(j);
            if(vis[i/j])
            he[i].push_back(i/j);
            }
        }
    }
    while(T--){
    ans=0;
    memset(sum,0,sizeof(sum));
    init();
    cin >> n >>p;
    for(i=1;icin >> t1>> t2;
        add(t1,t2);
        add(t2,t1);
    }
    for(i=1;i<=n;i++)
    cin >> qq[i];
    dfs(p,-1);
    ll kk;
    for(kk=1;kk<=n;kk++)
    ans=(ans+kk*sum[kk])%mod;
    cout << ans << endl;
    }
    return 0;
}

C 序列变换

题意:给两个序列,有3个操作,问最少需要的操作次数能把a变成b序列。
操作一:对第二个序列的i!=j的下标进行一个i与j数据的对调
操作二:如果一开始B[j]小于A[j],那么B[j]只能进行+1或者*2的操作
操作三:如果一开始B[j]大于A[j],那么B[j]只能进行-1或者/2的操作
思路:首先数据范围很小,先暴力一个全排列出来,然后拿起模版抄上计算全排列的一个更改次数(思路:通过对一个数字进行搜索,每次均进行一个标记,搜到其下一个数字,知道全部为标记过的,即为与这个数据有先关的一个对调的次数。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn=1e3+7;
const double eps=1e-6;
struct ttt{
    int id,have,is,len;
};
int L,R;
int N,M;
int qq[maxn];
bool vis[maxn];
int ans[maxn];
int ans2[maxn];
int good[maxn];
int Min;
int Res;
bool vis2[maxn];
int Calc()
{
    int res = 0;
    memset(vis2,0,sizeof(vis2));
    for (int i = 1; i <= N; i ++)
        if (!vis2[i])
        {
            vis[i] = 1;
            for (int j = qq[i]; j != i; j = qq[j])
                res ++, vis2[j] = 1;
        }
    return res;
}
int res(){
    int i;
    int min1,max1;
    int t1;
    int res=Calc();
    for(i=1;i<=N;i++){
    min1=min(ans[i],ans2[qq[i]]);
    max1=max(ans[i],ans2[qq[i]]);
    t1=0;
    while(min1if(max1%2==1){
            max1--;
        }else if(max1/2>=min1){
            max1/=2;
        }else{
        break;
        }
        t1++;
    }
    res+=t1+max1-min1;
    }
    Min=min(Min,res);
    return 0;
}
int dfs(int x){
    if(x>N){
        res();
    }else{
        int i;
        for(i=1;i<=N;i++){
            if(vis[i]==0){
            qq[x]=i;
            vis[i]=1;
            dfs(x+1);
            vis[i]=0;
            }
        }
    }
    return 0;
}
int main(){
    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4,n,m;
    //freopen("in.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--){
    Min=1e9;
    Res=0;
    scanf("%d",&N);
    for(i=1;i<=N;i++)
    scanf("%d",&ans[i]);
    for(i=1;i<=N;i++)
    scanf("%d",&ans2[i]);
    dfs(1);
    cout <return 0;
}

D 数字游戏

题意:
小埃和小森在玩一个数字游戏,小埃先从区间[L1, R1]里选择1个数字n1,小森看到小埃选的数字后,从[L2,R2]里选择1个数字n2, 将n1和n2连接在一起(n1在前, n2在后),形成一个新的数字,若这个数字可以被mod整除,那么小森获胜,否则小埃获胜。若两个人均采取最优策略,试问谁获胜?
思路:猜呗,水过去的。正解是按位置取模,对不同的位数不同的取模得到一个结果!

#include
#include
#include
#include
#include
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn=1e5+7;
typedef long long ll;
char s1[maxn];

int main()
{
    //e满足Note里提到的合法运算式
    //freopen("in.txt","r",stdin);
    //freopen("out1.txt","w",stdout);
    ll i,j;
    ll T,k,t1,t2,t3,t4,f1,f2,f3,f4;
    ll r,c,t5;
    ll n,m,K;
    cin >> n;
    while(n--){
    cin >> t1>> t2 >> t3>> t4>>t5;
    if(t4-t3>=t5-1){
    cout << "LOSE"<else{
    cout <<"WIN"<< endl;
    }

    }
    return 0;
}

E:小Y吃苹果

F:1 + 2 = 3?

题意:问第i个满足1X^2X=3X的数字
思路:因为是异或得到结果为加,那么说明无任何进位。
然后2X必定为1X的0,1位置调换且加一个1在第一位才能使得这么加的结果为3,要求这两个值的0与1刚好错开,那么就要求X在二进制下其1不能有连续的。
那么可以dp字符串DP[i][j]表示长度为i的字符串以j结尾的个数(这里只考虑二进制的0与1
那么得到状态转移方程:

dp[i][0]=dp[i-1][0]+dp[i-1][1];
dp[i][1]=dp[i-1][0];

此时以1开头任意结尾满足没有两个1相连的字符串个数就是以1结尾的那个dp(反转过来想一想。
然后得到了每个长度满足条件的个数,此时因为结果必定每个1与0有间隔,那么对于一个新的串来说,如到100000有k个,要求找到第n个,此时在此基础上找第n-k个即可,由于0与1间隔的特性,相当于重新找第n-k个,如此循环即得最终结果。结果个数为小的数字特判防错即可。

#include
#include
#include
#include
#include
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn=1e5+7;
typedef long long ll;
ll dp[125][2];
ll s1[127];
int main()
{
    //e满足Note里提到的合法运算式
   // freopen("in.txt","r",stdin);
    //freopen("out1.txt","w",stdout);
    ll i,j;
    ll T,k,t1,t2,t3,t4,f1,f2,f3,f4;
    ll r,c;
    ll n,m,K;
    t1=0;
    dp[1][0]=1;
    dp[1][1]=1;
    ll N;
    for(i=2;i<=70;i++){
    dp[i][0]=dp[i-1][0]+dp[i-1][1];
    dp[i][1]=dp[i-1][0];
    }
    for(i=2;i<=70;i++){
    dp[i][1]+=dp[i-1][1];
    }
    cin >> T;
    t1=0;
    while(T--){
       cin >>N;
       memset(s1,0,sizeof(s1));
        ll M=0;
        if(N==1){
        cout<<"1"<< endl;continue;
        }else if(N==2){
        cout<<"2"<< endl;continue;
        }else if(N==3){
        cout<<"4"<< endl;continue;
        }else if(N==4){
        cout<<"5"<< endl;continue;
        }
        while(N>3){
        for(i=1;i<=70;i++){
        if(t1==0&&N>=dp[i][1])continue;
        if(t1!=0&&N>dp[i][1])continue;
        s1[i]=1;
        t1=1;
        N-=dp[i-1][1]+1;
        break;
        }
        }
        if(N==1){
        M=1;
        }else if(N==2){
        M=2;
        }else if(N==3){
        M=4;
        }

    for(i=1;i<=70;i++){
        ll mm=1;
        if(s1[i]==1){
        for(j=1;j<=i-1;j++)
        mm*=2;
        M+=mm;
        }
    }
    cout <return 0;
}

I:二数

题意:
我们把十进制下每一位都是偶数的数字叫做“二数”。
小埃表示自己很聪明,最近他不仅能够从小数到大:2,3,4,5….,也学会了从大数到小:100,99,98…,他想知道从一个数开始数最少的数就得到一个二数。但是聪明的小森已经偷偷在心里算好了小埃会数到哪个二数,请你求出他要数到哪个数吧。
换句话说,给定一个十进制下最多1e5位的数字,请你求出和这个数字的差的绝对值最小的二数,若答案不唯一,输出最小的那个。
也就是说,给定数字n,求出m,使得abs(n-m)最小且m[i] mod 2 = 0
思路:显然可以从最高位开始向后面判断,找到第一个奇数,然后找到第一个大于其的全偶与第一个小于它的全偶进行一个差值判断即可得到结果。
如果对于第一个找到的奇数为9,显然其前面的偶数位,必须要扩大两位才能再次满足为偶,则特判为小于其的第一个二数。然后对于其他偶数,判断其是大于4还是小于4选择合适的二数。
例如204060521,5后面的任意,找到第一个大于其的偶数,然后后面均为0即可。如果是小于4,例如204060321,那么找到小于的第一个二数就是3-1=2,然后后面均为8的结果最大。

#include
#include
#include
#include
#include
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn=1e5+7;
typedef long long ll;
char s1[maxn];

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out1.txt","w",stdout);
    ll i,j;
    ll T,k,t1,t2,t3,t4,f1,f2,f3,f4;
    ll r,c;
    ll n,m,K;
    scanf("%lld",&T);
    while(T--){ //这里个位数特判掉
        cin >> s1;
        ll len1=strlen(s1);
        if(len1==1){
        if(s1[0]=='0'||s1[0]=='1'){
            cout << "0" <else if(s1[0]=='2'||s1[0]=='3'){
        cout << "2" <else if(s1[0]=='4'||s1[0]=='5'){
        cout << "4" <else if(s1[0]=='6'||s1[0]=='7'){
        cout << "6" <else if(s1[0]=='8'||s1[0]=='9'){
        cout << "8" <continue;
        }
        t1=0;
        for(i=0;iif(s1[i]%2!=0){
            t1=1;
            t2=i;
            break;
            }
        }
        if(t1==0){
        cout << s1<< endl;continue;
        }
        ll res=0;
        if(s1[t2]!='9')
        for(i=t2+1;iif(s1[i]!='4'){
            if(s1[i]>'4'){
                res=1;
            }
                break;
            }
        }
        if(res){
        s1[t2]++;
        for(i=t2+1;i'0';
        }
        }else{
        for(i=t2+1;i'8';
        }
        if(s1[t2]=='1'&&t2==0){
        for(i=0;i1;i++)
        s1[i]=s1[i+1];
        s1[len1-1]=0;
        }else{
        s1[t2]--;
        }
        }

        cout <return 0;
}

J 小Y写文章

题意:
小Y写了一篇文章,他对自己的文笔很有自信,尤其是自己总结出了一套计算文章通顺性的公式。
文章共N段,对于文章的每一段小Y对它都能计算出一个估值,而一篇文章的不连贯值定义为,现在小Y想要发布他的文章,但是编辑小Z让他加入一些广告,具体来说就是M段估值分别为的新段落。小Y很头疼,想让修改后的文章依然通顺,也就是要最小化不连贯值,已知小Y加入新段落的时候不需要考虑新段落之间的顺序,但是只可以在原文章的开头段之前、结尾段之后、或两段之间加入一段新段落,每个位置只能加入最多一段。请帮助焦头烂额的小Y求出将这M个新段落全都加入之后的最小不连贯值。
思路:找到一个满足的最小值输出,一开始的思路即为二分判满足即可。对于一个值d与两个相连数字的连贯值,存在例如9,4其连贯值为5,但是如果加个7,连贯值就变成了3。那么对于二分的结果d如果小于2个数字的差值,则其对于当前的d是需要作为一个必选一个值进去降低连贯度的点。
相当与有n+1个点与m个点,m个点,每个点可连n+1个点中的部分点,然后n+1个点分为2种,一种为对于当前d必选,一种为随意,每个点连容量1到2个必选与非必选点,必选与非必选再连总容量为c到汇点,跑二分最大流即可。

#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 2550;
const int Ni = 3550;
const int MAX = 1<<30;
struct Edge{
     int u,v,c;
     int next;
 }edge[40*Ni];
 int n,m;
 int edn;//边数
 int p[Ni];//父亲
 int d[Ni];
 int sp,tp;//原点,汇点

 void addedge(int u,int v,int c)
 {
     edge[edn].u=u; edge[edn].v=v; edge[edn].c=c;
     edge[edn].next=p[u]; p[u]=edn++;

     edge[edn].u=v; edge[edn].v=u; edge[edn].c=0;
     edge[edn].next=p[v]; p[v]=edn++;
 }
 int bfs()
 {
     queue <int> q;
     memset(d,-1,sizeof(d));
     d[sp]=0;
     q.push(sp);
     while(!q.empty())
     {
         int cur=q.front();
         q.pop();//
         for(int i=p[cur];i!=-1;i=edge[i].next)
         {
             int u=edge[i].v;
             if(d[u]==-1 && edge[i].c>0)
             {
                 d[u]=d[cur]+1;
                 q.push(u);
             }
         }
     }
     return d[tp] != -1;
 }
 int dfs(int a,int b)
 {
     int r=0;
     if(a==tp)return b;
     for(int i=p[a];i!=-1 && rint u=edge[i].v;
         if(edge[i].c>0 && d[u]==d[a]+1)
         {
             int x=min(edge[i].c,b-r);
             x=dfs(u,x);
             r+=x;
             edge[i].c-=x;
             edge[i^1].c+=x;
         }
     }
     if(!r)d[a]=-2;
     return r;
 }

 int dinic(int sp,int tp)
 {
     int total=0,t;
     while(bfs())
     {
         while(t=dfs(sp,MAX))
         total+=t;
     }
     return total;
}
struct ttt{
    int l,r;
    int is;
};
ttt q3[maxn];
int q1[maxn],q2[maxn];
int res(int x){
    edn=0;
    sp=0;tp=n+m+4;
    int i,j;
    memset(p,-1,sizeof(p));
    for(i=1;i<=m;i++)
    addedge(sp,i,1);
    int max1,min1,min2,max2;
    int ha1,ha2;
    ha1=ha2=0;
    for(i=1;i1]);
        min1=min(q1[i],q1[i+1]);
        if(max1-min1<=x){
        q3[i].l=max1-x;
        q3[i].r=min1+x;
        q3[i].is=1;
        ha1++;
        }else if(max1-min1>x+x){ //在x的区间中
        return 0;
        q3[i].is=0;//不可以走这条路
        }else if(max1-min1<=x+x){
        q3[i].l=max1-x;
        q3[i].r=min1+x;
        q3[i].is=2;
        ha2++;
        }
    //    cout <<"i= " <
    }
    q3[0].is=1;
    q3[0].l=q1[1]-x;//
    q3[0].r=q1[1]+x;

    q3[n].is=1;
    q3[n].l=q1[n]-x;
    q3[n].r=q1[n]+x;

    for(i=0;i<=n;i++){
        for(j=1;j<=m;j++){
            if(q2[j]>=q3[i].l&&q2[j]<=q3[i].r){
                addedge(j,i+m+1,1);
            }
        }

    }

    for(i=0;i<=n;i++)
    if(q3[i].is==1)
    addedge(i+m+1,n+m+2,1);
    else{
    addedge(i+m+1,n+m+3,1);
    }
    addedge(n+m+2,tp,m-ha2);
    addedge(n+m+3,tp,ha2);

    int res=dinic(sp,tp);

    if(res==m)
    return 1;
    else
    return 0;
}
int main(){

    freopen("in.txt","r",stdin);
    //freopen("out2.txt","w",stdout);
    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
    int T;
    int s,t;
    scanf("%d",&T);
    while(T--){
    scanf("%d",&n);
    scanf("%d",&m);
    for(i=1;i<=n;i++)
    scanf("%d",&q1[i]);
    for(i=1;i<=m;i++)
    scanf("%d",&q2[i]);
    int right1,left1,mid1;
    left1=0;right1=1e9+7;
    t2=1e9+8;
    while(right1>=left1){
        mid1=(left1+right1)/2;
        t1=res(mid1);
        if(t1==1){
        t2=min(t2,mid1);
        right1=mid1-1;
        }else{
        left1=mid1+1;
        }
    }
    printf("%d\n",t2);
    }
    return 0;
}

K 树上最大值

题意:开始刚读题的时候宛如智障,题目完全看不懂呀!!!然后想也没有想明白。
后来才想明白,把树分成A与B两个块,然后对A选一部分异或和最大的结果,再对B选一部分异或和最大的结果,把这两个结果相加,得到的就是想要的答案。
思路:异或和最大的集合,首先想到的是线性基,然后对一个树进行分割的操作想到的是tree dp。
这里有这么一个操作:可以用来求分割后的树,只取某些点的两边的最值。
有一个数组t,用来存这个点往下的全部结果,然后考虑到这个点往上的结果如何进行整合在一起。考虑这么一个方法,从上往下搜,如对于结点A,找到结点A的全部子树的结果,建立前缀pre[i]数组与后缀ed[i]数组,分别储存累加的结果。然后对每个A的子结点建立c[i]数组,c[i]继承A结点的c[i],然后加上pre[i-1]与ed[i+1],c[i]就相当于把全部的除了i与其子树的全部结果。
然后处理完以后再进行每次均对i进行循环取最优即可得到一个最后的答案。
这里用线性基去处理(留坑,想一下其他方法,dfs直接找

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
struct L_B{
    int d[33];//int p[33] 这里的p不用到的时候可删除
    int cnt;
    L_B() //这个函数类似于构造函数,在创建的时候直接先调用一次
    {
        memset(d,0,sizeof(d));
       // memset(p,0,sizeof(p));
        cnt=0;
    }
    bool insert(int val)
    {
        for (int i=31;i>=0;i--)
            if (val&(1LL<if (!d[i])
                {
                    d[i]=val;
                    break;
                }
                val^=d[i];
            }
        return val>0;
    }
    int query_max()
    {
        int ret=0;
        for (int i=31;i>=0;i--)
            if ((ret^d[i])>ret)
                ret^=d[i];
        return ret;
    }
}c[maxn],pre[maxn],ed[maxn],t[maxn];
L_B merge(const L_B &n1,const L_B &n2)
{//把右边的数据合并到左边
    L_B ret=n1;
    for (int i=31;i>=0;i--)
        if (n2.d[i])
            ret.insert(n2.d[i]);
    return ret;
}
struct E{
    int next,to;
}e[maxn*2];
int tot;
int head[maxn];
void init(){
    tot=0;
    memset(head,-1,sizeof(head));
}
void add(int u,int v){
    tot++;
    e[tot].to=v;
    e[tot].next=head[u];
    head[u]=tot;
}
int qq[maxn];
int dfs(int x,int fa){
    t[x].insert(qq[x]);
    int i;
    int v;
    for(i=head[x];i!=-1;i=e[i].next){
        v=e[i].to;
        if(v==fa)continue;
        dfs(v,x);
        t[x]=merge(t[x],t[v]);
    }
    return 0;
}
int res[maxn];
int dfs2(int x,int fa){
    int i,v;
    int num=0;
    for(i=head[x];i!=-1;i=e[i].next){
        v=e[i].to;
        if(v==fa)continue;
        num++;
        pre[num]=ed[num]=t[v];//把这个子结点的结果全部要了,结果为一条孩子链
        res[num]=v;
    }
    for(i=2;i<=num;i++)
        pre[i]=merge(pre[i],pre[i-1]);
    for(i=num-1;i>=1;i--)
        ed[i]=merge(ed[i],ed[i+1]);
    for(i=1;i<=num;i++){
        int to=res[i];
        c[to] = c[x]; //这个c是自上而下,那么其包含全部上面的结点即可,从u拿过来
        c[to].insert(qq[x]);        //但是对于c这个数组中,其是不存在自己结点的值
        if(i-1>=1)
        c[to]=merge(c[to],pre[i-1]);
        if(i+1<=num)
        c[to]=merge(c[to],ed[i+1]);
    }
    for(i=head[x];i!=-1;i=e[i].next){
        v=e[i].to;
        if(v==fa)continue;
        dfs2(v,x);
    }
    return 0;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("in.txt","w",stdout);
    int i,j,k,f1,f2,f3,f4,t1,t2,t3,t4;
    int n,m;
    scanf("%d",&n);
    for(i=1;i<=n;i++){
        scanf("%d",&qq[i]);
    }
    int u,v;
    init();
    for(i=1;iscanf("%d %d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1,-1);
    dfs2(1,-1);
    int res=0;
    for(i=1;i<=n;i++)
    res=max(res,c[i].query_max()+t[i].query_max());
    printf("%d\n",res);
    return 0;
}

L K序列

题意:
给一个数组 a,长度为 n,若某个子序列中的和为 K 的倍数,那么这个序列被称为“K 序列”。现在要你 对数组 a 求出最长的子序列的长度,满足这个序列是 K 序列。 、
思路:注意(子串要求连续,而子序列不要求连续。因为n*k<=1e7.所以如果n大,那么k小,
如果n小,则k大。通过对每个数字与上一次%k的最大长度进行一个再判断,更新每个%k的最大长度维护一下即可。而自己的复杂度为n*k,刚好满足

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include
#include
#include
#include
#include
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int maxn=1e5+7;
const int mod =1e9+7;
typedef long long ll;
int dp[2][10000500];
int is[10000500];
int list[100050];
int qq[maxn];
int main()
{
    //e满足Note里提到的合法运算式
    //freopen("in.txt","r",stdin);
    //freopen("out1.txt","w",stdout);
    int i,j;
    int T,k,t1,t2,t3,t4,f1,f2,f3,f4;
    int r,c,t5;
    int n,m,K,p;
    cin >> n>> m;
    int s1,e1;
    for(i=1;i<=n;i++){
    cin >> qq[i];
    }
    int Max=0;
    s1=0;
    for(i=1;i<=n;i++){
        t2=s1;
        for(j=1;j<=s1;j++){
        t1=(list[j]+qq[i])%m;
        if(is[t1]!=i){
        if(is[t1]==0){
        list[++t2]=t1;
        }
        dp[i%2][t1]=dp[(i+1)%2][list[j]]+1;
        is[t1]=i;
        }else{
        dp[i%2][t1]=max(dp[i%2][t1],dp[(i+1)%2][list[j]]+1);
        }
        if(t1==0)
        Max=max(Max,dp[i%2][0]);
        }
        s1=t2;
        t1=qq[i]%m;
        if(is[t1]!=i){
        if(is[t1]==0){
        list[++s1]=t1;
        }
        dp[i%2][t1]=dp[(i+1)%2][list[j]]+1;
        is[t1]=i;
        }else{
        dp[i%2][t1]=max(dp[i%2][t1],dp[(i+1)%2][list[j]]+1);
        }
    }
    cout << Max << endl;
    return 0;
}

你可能感兴趣的:(----网络流,线性基,dp)