2017 ACM/ICPC广西邀请赛

A Math Problem

题目链接
题意:给你一个n,求有多少个k满足
思路:预处理

#include<iostream> 
#include<cstdio>
#include<cstring>
#include<map>
#include<vector> 
#include<set>
#include<queue>
#include<algorithm>
#include<string>
#include<cmath>
#include<stack>
#include<functional>
#include<bitset>
#define ll long long
#define maxn 100002
const int INF = 0x3f3f3f3f;

using namespace std;
ll num[22];
int s;
void init(){
    ll i, j, t;
    s = 1;
    for(i = 1;i < 18;i++){
        t = 1;
        for(j = 0;j < i;j++){
            if(1e18 / i >= t){
                t *= i;
            }
            else{
                return;
            }
        }
        s = i;
        num[i] = t;
    }
}
int main(void){
    init();
    num[s + 1] = 1000000000000000002;
    ll n, i;
    while(scanf("%lld",&n)!=EOF){
        for(i = 0;;i++){
            if(n < num[i]){
                printf("%lld\n",i - 1);
                break;
            }
        }
    }
    return 0;
}

B Color it

题目链接
题意:四种操作。0、初始化,清除操作。1、在坐标(x,y)上画上颜色c。2、计算(1,y1)到(x,y2)有多少种不同颜色。3、结束
思路:由于只有50种颜色,考虑建50棵线段树。采用动态开点,设立root数组表示第i种颜色的根节点编号。线段树上记录最小值,询问是否存在小于等于x。

#include <bits/stdc++.h>

#define maxn 1000005
using namespace std;

struct node{
    int l,r,cnt;
    node(){
        l=r=cnt=0;
    }
}T[maxn<<2];

int rt[55];
int tot=0;
int opr;

void update(int &rt,int l,int r,int pos,int val){
    if(!rt){
        rt=++tot;
        T[rt].l=T[rt].r=0;
        T[rt].cnt=val;
    }

    T[rt].cnt=min(T[rt].cnt,val);

    if(l==r)
        return;

    int mid=(l+r)>>1;

    if(pos<=mid) update(T[rt].l,l,mid,pos,val);
    else update(T[rt].r,mid+1,r,pos,val);
}

bool query(int rt,int l,int r,int L,int R,int x){
    if(!rt)
        return false;

    if(L<=l&&r<=R){
        return T[rt].cnt<=x;
    }

    int mid=(l+r)>>1;
    if(L<=mid&&query(T[rt].l,l,mid,L,R,x))
        return true;
    if(R>mid&&query(T[rt].r,mid+1,r,L,R,x))
        return true;
    return false;
}

void input(){
    int x,y,c,l,r;
    if(opr==0){
        memset(rt,0,sizeof rt);
        tot=0;
    }else if(opr==1){
        scanf("%d%d%d",&x,&y,&c);
        update(rt[c],1,maxn-1,y,x);
    }else if(opr==2){
        scanf("%d%d%d",&x,&l,&r);
        int ans=0;
        for(int i=0;i<=50;i++){
            if(query(rt[i],1,maxn-1,l,r,x))
                ans++;
        }

        printf("%d\n",ans);
    }
}

//void solve(){
//    
//}

int main(){
    while(scanf("%d",&opr)!=EOF){
        if(opr==3)break;
        input();
//        solve();
    }
}

C Counting Stars

题目链接
题意:定义由两个三元环共享一条边的图为星星,求图上有多少子图可以构成星星
思路:对于三元环,有的做法判断。度数大向小连有向边,度数相同则编号小向编号大连有向边,然后对于每条边第第一个顶点的相连点标号,对第二个顶点相连点标号。 若存在一个三元环,将三元环的三边贡献都+1。对于每条边求C(n,2)。
为何是对于每条边求C(n,2),可能存在两个互不相交的子图都是要求的星型。

#include <bits/stdc++.h>

#define maxn 100005
typedef long long ll;
using namespace std;

int n,m;
int x[maxn<<1],y[maxn<<1];
int deg[maxn];
int vis[maxn];
int ans[maxn<<1];
int pos[maxn];

struct edge{
    int to,index;
};
vector <edge>G[maxn];

void input(){
    int i;
    for(i=1;i<=m;i++){
        scanf("%d%d",&x[i],&y[i]);
        deg[x[i]]++;
        deg[y[i]]++;
    }

}

void solve(){
    int i,j;
    for(i=1;i<=m;i++){
        if(deg[x[i]]>deg[y[i]])
            G[y[i]].push_back((edge){x[i],i});
        else if(deg[x[i]]<deg[y[i]])
            G[x[i]].push_back((edge){y[i],i});
        else
        {
            if(x[i]<y[i])
                G[x[i]].push_back((edge){y[i],i});
            else
                G[y[i]].push_back((edge){x[i],i});    
        }
    }

    memset(vis,0,sizeof vis);
    memset(pos,0,sizeof pos);
    memset(ans,0,sizeof ans);
    for(i=1;i<=m;i++){
        int u=x[i],v=y[i];
        for(j=0;j<G[u].size();j++){
            vis[G[u][j].to]=i;
            pos[G[u][j].to]=G[u][j].index;
        }

        for(j=0;j<G[v].size();j++)
        {
            if(vis[G[v][j].to]==i){
                ans[i]++;
                ans[G[v][j].index]++;
                ans[pos[G[v][j].to]]++;
            }
        }
    }

    ll ANS=0;
    for(i=1;i<=m;i++)
        ANS+=1ll*ans[i]*(ans[i]-1)/2;

    printf("%lld\n",ANS);
}

int main(){
    while(scanf("%d%d",&n,&m)!=EOF){

        for(int i=1;i<=n;i++)
            G[i].clear();
        memset(deg,0,sizeof deg);

        input();
        solve();
    }    
}

/*
8 10
1 2
2 3
3 4
1 4
1 3
5 6
6 7
7 8
5 8
5 7
*/

D Covering

题目链接
题意:给你一个n,求有多少种方式12和21的毯子铺4n的空间
思路:zl递推出规律,建立了16
16的矩阵快速幂,T了。用BM线性递推输入数据过了。。
正解的规律是dp[4]=dp[3]+5*dp[2]+dp[1]-dp[0]

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define mp make_pair
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second
#define SZ(x) ((int)(x).size())
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const ll mod=1000000007;
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
// head

ll _,n;
namespace linear_seq {
    const int N=10010;
    ll res[N],base[N],_c[N],_md[N];

    vector<int> Md;
    void mul(ll *a,ll *b,int k) {
        rep(i,0,k+k) _c[i]=0;
        rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
        for (int i=k+k-1;i>=k;i--) if (_c[i])
            rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
        rep(i,0,k) a[i]=_c[i];
    }
    int solve(ll n,VI a,VI b) { // a 系数 b 初值 b[n+1]=a[0]*b[n]+...
//        printf("%d\n",SZ(b));
        ll ans=0,pnt=0;
        int k=SZ(a);
        assert(SZ(a)==SZ(b));
        rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
        Md.clear();
        rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
        rep(i,0,k) res[i]=base[i]=0;
        res[0]=1;
        while ((1ll<<pnt)<=n) pnt++;
        for (int p=pnt;p>=0;p--) {
            mul(res,res,k);
            if ((n>>p)&1) {
                for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
                rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
            }
        }
        rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
        if (ans<0) ans+=mod;
        return ans;
    }
    VI BM(VI s) {
        VI C(1,1),B(1,1);
        int L=0,m=1,b=1;
        rep(n,0,SZ(s)) {
            ll d=0;
            rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
            if (d==0) ++m;
            else if (2*L<=n) {
                VI T=C;
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                L=n+1-L; B=T; b=d; m=1;
            } else {
                ll c=mod-d*powmod(b,mod-2)%mod;
                while (SZ(C)<SZ(B)+m) C.pb(0);
                rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
                ++m;
            }
        }
        return C;
    }
    int gao(VI a,ll n) {
        VI c=BM(a);
        c.erase(c.begin());
        rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
        return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
    }
};

int main() {
    while (~scanf("%lld",&n)) {
        vector<int>v;
        v.push_back(1);//前几项 
        v.push_back(5);
        v.push_back(11);
        v.push_back(36);
        v.push_back(95);
        v.push_back(281);
        v.push_back(781);
        v.push_back(2245);
        v.push_back(6336);
        v.push_back(18061);
        v.push_back(51205);
        v.push_back(145601);
        v.push_back(413351);
        v.push_back(1174500);
        v.push_back(3335651);
        v.push_back(9475901);

        //输入n ,输出第n项的值 
        printf("%d\n",linear_seq::gao(v,n-1));
    }
}

E CS Course

题目链接
题意:给出n个数字,q次查询,查询除了第ai个数剩余所有数的与值、或值、异或值。
思路:前缀+后缀

#include <bits/stdc++.h>

#define maxn 100005
using namespace std;

int n,q;
int a[maxn];
int preand[maxn],sufand[maxn];
int prexor[maxn],sufxor[maxn];
int preor[maxn],sufor[maxn];


void input(){
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);

    preand[1]=prexor[1]=preor[1]=a[1];
    for(int i=2;i<=n;i++){
        preand[i]=preand[i-1]&a[i];
        prexor[i]=prexor[i-1]^a[i];
        preor[i]=preor[i-1]|a[i];
    }

    sufand[n]=sufxor[n]=sufor[n]=a[n];
    for(int i=n-1;i>1;i--){
        sufand[i]=sufand[i+1]&a[i];
        sufxor[i]=sufxor[i+1]^a[i];
        sufor[i]=sufor[i+1]|a[i];
    }
}

void solve(){
    int i,index;
    for(i=1;i<=q;i++){
        scanf("%d",&index);
        int a=index-1;
        int b=index+1;
        if(a<1)
            printf("%d %d %d\n",sufand[index+1],sufor[index+1],sufxor[index+1]);
        else if(b>n)
            printf("%d %d %d\n",preand[index-1],preor[index-1],prexor[index-1]);
        else
            printf("%d %d %d\n",preand[index-1]&sufand[index+1],preor[index-1]|sufor[index+1],prexor[index-1]^sufxor[index+1]);
    }
}

int main(){
    while(scanf("%d%d",&n,&q)!=EOF){
        input();
        solve();
    }
}

F Destroy Walls

题目链接
题意:给出n个城市,存在m座墙,第i座墙连接着ui城市和vi城市,求国王希望到达城市每一个地方所需要的拆墙的最少费用,以及拆多少座
思路:求最大生成树

#include <bits/stdc++.h>

#define maxn 100005
typedef long long ll;
using namespace std;

int n,m;
struct edge{
    int from,to,cost;
};

bool cmp(edge a,edge b){
    return a.cost>b.cost;
}

edge G[maxn<<1];
int fa[maxn];
ll sum;

void init(int n){
    for(int i=1;i<=n;i++)
        fa[i]=i;
}

int find(int x){
    return x==fa[x]?x:fa[x]=find(fa[x]);
}

void mix(int x,int y){
    int fx=find(x);
    int fy=find(y);
    fa[fx]=fy;
}

void input(){
    init(n);
    int i,x,y;
    for(i=1;i<=n;i++){
        scanf("%d%d",&x,&y);
    }

    int u,v,w;
    sum=0;
    for(i=1;i<=m;i++){
        scanf("%d%d%d",&u,&v,&w);
        G[i].from=u;
        G[i].to=v;
        G[i].cost=w;
        sum+=G[i].cost;
    }
}

void solve(){
    sort(G+1,G+1+m,cmp);
    ll ans=0;
    int tot=0;
    for(int i=1;i<=m;i++){
        if(find(G[i].from)!=find(G[i].to)){
            ans+=G[i].cost;
            tot++;
            mix(G[i].from,G[i].to);
        }
    }

    printf("%lld %lld\n",m-tot,sum-ans);
}

int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        input();
        solve();
    }
}

G Duizi and Shunzi

题目链接
题意:给出n张牌,求能组成最多的对子或者顺子个数。
思路:对子优于顺子,特判顺子的一种情况

#include <bits/stdc++.h>

#define maxn 1000005
using namespace std;

int n;
int t[maxn];
void input(){
    int i,a;
    for(i=1;i<=n;i++){
        scanf("%d",&a);
        t[a]++;
    }
}

void solve(){
    int ans=0;
    int i;
    for(i=1;i<=1000000;i++){
        if(t[i]>=2){
            ans+=t[i]/2;
            t[i]=t[i]&1;
        }

        while(i+2<=1000000&&t[i]&&t[i+1]%2&&t[i+2]){
            t[i]--;
            t[i+1]--;
            t[i+2]--;
            ans++;
        }
    }
    printf("%d\n",ans);
}

int main(){
    while(scanf("%d",&n)!=EOF){
        memset(t,0,sizeof t);
        input();
        solve();
    }    
}

J Query on A Tree

[题目链接](http://acm.hdu.edu.cn/showproblem.php?pid=6191) 题意:q次查询,给出一棵树,求结点u子树下值与x最大异或值 思路:通过DFS序建立可持久化字典树,在可持久化字典树中查询
#include <bits/stdc++.h>

#define maxn 100005
using namespace std;

int n,q;
int v[maxn];
vector <int>G[maxn];

int rt[maxn];
struct Persistent_Trie {
    int tot;
    int ch[maxn * 32][2];//开logn+2倍
    int sum[maxn * 32];

    void init() {
        tot = 0;
        memset(ch, 0, sizeof(ch));
        memset(sum, 0, sizeof(sum));
    }
    void insert(int &x, int y, int val) {
        int sta;
        int t;
        x = sta = ++tot;
        for (int i = 30; i >= 0; i--) {
            ch[sta][0] = ch[y][0];
            ch[sta][1] = ch[y][1];
            sum[sta] = sum[y] + 1;
            t = val & (1 << i); t >>= i;
            y = ch[y][t];
            ch[sta][t] = ++tot;
            sta = ch[sta][t];
        }
        sum[sta] = sum[y] + 1;
    }

    int query(int x, int y, int val) {
        int res = 0;
        for (int i = 30; i >= 0; i--) {
            int t = val & (1 << i); t >>= i;
            if (sum[ch[y][t ^ 1]] - sum[ch[x][t ^ 1]]) {
                res += 1 << i;
                y = ch[y][t ^ 1];
                x = ch[x][t ^ 1];
            }
            else {
                y = ch[y][t];
                x = ch[x][t];
            }
        }
        return res;
    }
}pt;

int dfs_num[maxn];
int L[maxn],R[maxn];
int cnt;

void dfs(int x,int fa){
    L[x]=++cnt;
    dfs_num[cnt]=x;
    for(int i=0;i<G[x].size();i++){
        if(G[x][i]==fa)continue;
        dfs(G[x][i],x);
    }
    R[x]=cnt;
}

void input(){
    pt.init();
    cnt=0;
    int i,u,x;
    for(i=1;i<=n;i++){
        scanf("%d",&v[i]);
        G[i].clear();
    }

    for(i=2;i<=n;i++){
        scanf("%d",&x);
        G[x].push_back(i);
    }

    dfs(1,-1);

    for(i=1;i<=cnt;i++){
        pt.insert(rt[i],rt[i-1],v[dfs_num[i]]);
    }

    for(i=1;i<=q;i++){
        scanf("%d%d",&u,&x);
        printf("%d\n",pt.query(rt[L[u]-1],rt[R[u]],x));
    }
}

//void solve(){
//    
//}

int main(){
    while(scanf("%d%d",&n,&q)!=EOF){
        input();
//        solve();
    }
}