2017年四川省赛 【(5+2+1)/12】 [待补]

链接: https://post.icpc-camp.org/d/691-2017

A Simple Arithmetic

————————————————————————————————————————————————

签到题
注意-2^63 的情况和2^63不能用64位长度的数表示就好了


#include <bits/stdc++.h>

typedef long long int LL;
using namespace std;

const int N = 200000+7;

inline int read() {
    int x=0,f=1;
    char ch=getchar();
    for(; ch<'0'||'9'<ch; ch=getchar())   if(ch=='-')f=-1;
    for(; '0'<=ch&&ch<='9'; ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}

/*******************************************************/

int main() {
    long double a,b;
    while(cin>>a>>b) {
        if(a == -9223372036854775808.0) {
//            puts("++");
            if(a == b) {
                puts("1");
            } else if(b==-1.0) {
                puts("9223372036854775808");
            } else if(b==1.0) {
                puts("-9223372036854775808");
            } else {
                LL ans = floor(a/b);
                printf("%lld\n",ans);
            }
            continue;
        } else if(b == -9223372036854775808.0) {
            if(a<0) puts("-1");
            else    puts("0");
            continue;
        }
        if(a<0&&b<0) a*=-1.0,b*=-1.0;
//        cout << (LL)a <<" "<<  (LL)b <<endl;
        LL ans = floor(a/b);
        printf("%lld\n",ans);
    }
    return 0;
}

B Broken Counter

————————————————————————————————————————————————


C Determinant

————————————————————————————————————————————————


D Dynamic Graph

————————————————————————————————————————————————
给你一个DAG图,每个节点不是白的就是黑的,有q次操作,每次将点x变换颜色.然后输出当前< u,v>有多少对,
< u,v>定义为; 当存在一条从u到v的路径使得路径上没有黑色的点是存在.


考虑到DAG图,所以进行拓扑排序,
每次操作完,之后将黑色的点去掉,然后进行拓扑序,在过程中记录能到达每个点的个数,然后一路算过去就行了,然后去个重复采用vis标记,复杂度是 O(Tqnm) ,然后妥妥的TLE了,

最后想到用二进制来优化,每一位对应表示每一个节点,然后进行TOP过程中一下就行了.

可以用5个64位整形计算 (这时候注意求1的个数的时候要用lowbit优化)
也可以用bitset来计算

最后复杂度为 O(Tqnm/a) ,其中 a 为bitset优化的常数

#include <bits/stdc++.h>

typedef long long int LL;
using namespace std;

const int N = 1100+7;
const int MOD = 1e9+7;
inline LL read() {
    LL x=0,f=1;
    char ch=getchar();
    for(; ch<'0'||'9'<ch; ch=getchar())   if(ch=='-')f=-1;
    for(; '0'<=ch&&ch<='9'; ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}

/*******************************************************/

int n,m,q,ans;

vector<int>G[303];
int vis[303],c[303],deg[303],temp[303];

void TOP() {
    bitset<303>mmp[303];
    ans = 0;
    queue<int>q;
    for(int i=1; i<=n; i++) {
        if(deg[i]==0) {
            q.push(i);
        }
        temp[i]=deg[i];
    }
    for(int i=1; i<=n; i++) {
        mmp[i][i]=1;
    }
    while(!q.empty()) {
        int u=q.front();
        q.pop();
        for(int i=0; i<G[u].size(); i++) {
            int v=G[u][i];
            if(c[v]==0&&c[u]==0) {
                mmp[v]=mmp[v]|mmp[u];
            }
            temp[v]--;
            if(temp[v]==0)q.push(v);
        }
    }
    for(int i=1; i<=n; i++) {
        if(c[i]==0)
            ans+=mmp[i].count()-1;
        //   printf("%d ---%d\n",c[i],mmp[i].count()-1);
    }
    printf("%d\n",ans);
}

int main() {
    while(~scanf("%d%d%d",&n,&m,&q)) {
        memset(c,0,sizeof(c));
        memset(deg,0,sizeof(deg));
        for(int i=1; i<=n; i++)G[i].clear();
        for(int i=1,u,v; i<=m; i++) {
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            deg[v]++;
        }
        for(int x; q--;) {
            scanf("%d",&x);
            c[x]=1-c[x];
            TOP();
        }
    }
    return 0;
}

E Longest Increasing Subsequence

————————————————————————————————————————————————
给你个序列 问你去除每个数之后剩下的数中的结果
结果定义为 f21\xorf22\xor...\xorf2n
f(i) 为以 a(i) 结尾的lis的长度


最暴力的想法是求 n lis复杂度为 O(n2logn) ,一定TLE, 然后想如何优化为 O(n2)

然后想到删除一个数之后对 f(i) 的影响至多减少1, 所以在我们有了原序列的 f(i) 之后 我们就可以 O(2n) 的求每次新的 f(i)

过程中只要记录 长度为 l 的上升序列当前的最小结尾是谁 ,然后每次看长度为f[i]1的序列的结尾和 a[i] 比谁大 就能判断 f(i) 是否减了1


#include <bits/stdc++.h>

typedef long long int LL;
using namespace std;

const int N = 5000+7;
const int MOD = 1e9+7;
const int INF = (~(1<<31));

inline LL read() {
    LL x=0,f=1;
    char ch=getchar();
    for(; ch<'0'||'9'<ch; ch=getchar())   if(ch=='-')f=-1;
    for(; '0'<=ch&&ch<='9'; ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}

/*******************************************************/
int n;

int a[N],b[N],f[N];

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

        for(int i=1; i<=n; i++) {
            f[i]=1;
            for(int j=1; j<i; j++) if(a[i]>a[j])
                    f[i]=max(f[i],f[j]+1);
        }

        int ans,tmp;
        for(int k=1; k<=n; k++) {
            for(int i=1; i<=n; i++) b[i] = INF;
            ans = 0;
            for(int i=1; i<=n; i++) {
                if(i==k) continue;

                if(b[f[i]-1] < a[i]) tmp = f[i];
                else                 tmp = f[i]-1;

                ans   ^= (tmp*tmp);
                b[tmp] = min(b[tmp],a[i]);
            }

            printf("%d%c",ans,(k==n)?'\n':' ');
        }
    }
    return 0;
}

F Simple Algebra

————————————————————————————————————————————————
给你 f(x,y)=ax2+bxy+cy2 ,问你是否永远非负


考虑到数据范围只有 [10,10] ,于是选择打表,

判断过程是计算 x,y[1000,1000] 过程中有没有负的,然后输出 {0,1}


代码太长了 http://paste.ubuntu.com/25102699/

G 2017

————————————————————————————————————————————————
给你4个数 a,b,c,d .       0ab109,0cd109
问你 i=abj=cd[ij%2017=0]


考虑到2017是素数,满足的情况用坐标系表示的话一定在 x,y=2017k 上 ,所以直接计算就好了


LL cal(LL a,LL b){
    LL ans = (a/2017)*b+(b/2017)*a-(a/2017)*(b/2017);
    return ans;
}

int main(){
    LL a,b,c,d;
    while(~scanf("%lld%lld%lld%lld",&a,&b,&c,&d))
        printf("%lld\n",cal(b,d)-cal(b,c-1)-cal(a-1,d)+cal(a-1,c-1));
    return 0;
}

H Roads

————————————————————————————————————————————————

I Strange Prime

————————————————————————————————————————————————

J Skewness

————————————————————————————————————————————————

给你一个n*n的方阵 求每个子矩阵的元素和的三次方的和


未完待续…

x1=1ny1=1nx2=x1ny2=y1n(i=x1x2j=y1y2ai,j)3mod109x1=1ny1=1nx2=x1ny2=y1n(f(x2,y2)f(x2,y11)f(x11,y2)+f(x11,y11))3mod109

其中
(f(x2,y2)f(x2,y11)f(x11,y2)+f(x11,y11))3=(abc+d)3=(abc+d)×(abc+d)×(abc+d)=(abc+d)×(a2+b2+c2+d22ab2ac+2ad+2bc2bd2cd)=(a3b3c3+d3+6(bcdacdabd+abc)+3(ab2a2ba2c+ac2+a2d+ad2b2cbc2+b2dbd2+c2dcd2))

其中

a(a2+b2+c2+d22ab2ac+2ad+2bc2bd2cd)=a3+ab2+ac2+ad22a2b2a2c+2a2d+2abc2abd2acd b(a2+b2+c2+d22ab2ac+2ad+2bc2bd2cd)=a2bb3bc2bd2+2ab2+2abc2abd2b2c+2b2d+2bcd c(a2+b2+c2+d22ab2ac+2ad+2bc2bd2cd)=a2cb2cc3cd2+2abc+2ac22acd2bc2+2bcd+2c2d d(a2+b2+c2+d22ab2ac+2ad+2bc2bd2cd)=a2d+b2d+c2d+d32abd2acd+2ad2+2bcd2bd22cd2
.

我TM是有多无聊 会来补这个题,,,MD 不补了

然后在对于每种情况的求一下对结果的贡献系数

然后推到一下 ,就需要在预处理出几种前缀和,后缀和,

然后统计即可 总复杂度是 O(n)

代码没写 看dalao的吧

K 2017 Revenge

————————————————————————————————————————————————

对于原根理解的十分不到位,只停留到会计算,,,,

算是强行补题.

大概了解下原根在了解些什么是生成元这题就可以做了

将乘法转化为加法

个人理解其实这部分相当去将a*x%2017和a*y%2017的结果进行了优化,能够方便记录每一位的值

考虑dp[i][j] 表示到第i个点后取模结果为j的数的个数 然后优化下空间到dp[2017];

每次转移就是 dp[i][jx]+=dp[i1][j],dp[i][jx]

然后因为转化为加法后 能够快速找到值, 又有最后的结果要对 2 取模 能够想到二进制的方法做,

这里采用了bitset



int p[N];
int n,r;
int get(int mod) {
    for(int i = 2; ; i++) {
        set<int> s;
        for(int j = 1, x = 1; j < mod; j++) {
            x = (x*i)%mod;
            s.insert(x);
        }
        if(s.size() == mod-1) return i;
    }
}

int main() {
    // cout << get(2017) << endl;
    // 5 为模 2017 的 原根
    for(int i = 0, x = 1; i < 2016; i++) {
        p[x] = i;
        x = (x*5)%2017;
    }
    while(~scanf("%d%d", &n, &r)) {
        bitset<2016> f;
        f[p[1]] = 1;
        for(int i = 0; i < n; i++) {
            int x; scanf("%d", &x); x = p[x];
            f ^= (f<<x) ^ (f>>(2016-x));
        }
        printf("%d\n", (int)f[p[r]]);
    }
    return 0;
}

L Nice Trick

————————————————————————————————————————————————
给你一个序列以及

s3=ii<j<knaiajak=(ni=1ai)33(ni=1a2i)(ni=1ai)+2(ni=1a3i)6

问你 ii<j<k<lnaiajakal


有了s3 , 所以可以先求3个数的情况,然后找第4个数,

然后发现,每次如果第4个数选择 a[x] ,那么结果就多了 a[x]×ii<j<k<xaiajak

所以遍历一遍序列,即固定 a[l] 同时能求出 i=1naii=1na2ii=1na3i 从而能 O(1) 求出 ii<j<k<laiajak


#include <bits/stdc++.h>

typedef long long int LL;
using namespace std;

const int N = 200000+7;
const int MOD = 1e9+7;

inline int read() {
    int x=0,f=1;
    char ch=getchar();
    for(; ch<'0'||'9'<ch; ch=getchar())   if(ch=='-')f=-1;
    for(; '0'<=ch&&ch<='9'; ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
    return x*f;
}

/*******************************************************/

LL qmod(LL a,LL b){
    LL res = 1ll;
    while(b){
        if(b&1) res=res*a%MOD;
        b>>=1,a=a*a%MOD;
    }
    return res;
}
int n ;

LL inv6 = qmod(6,MOD-2);
LL a[N];
LL cal(LL a1,LL a2,LL a3){
    LL ans = 0;
    ans += qmod(a1,3);
    ans -= a1*a2%MOD*3%MOD; ans = (ans % MOD + MOD ) %MOD;
    ans += a3*2%MOD; ans %= MOD;
    return ans*inv6%MOD;
}
int main(){
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);

        LL a1 = 0,a2 = 0,a3 = 0,ans = 0,tmp = 0;
        for(int i=1;i<=n;i++){
            if(i>3){
                tmp  = a[i]*cal(a1,a2,a3)%MOD;
                ans += tmp;
                ans %= MOD;
            }
            a1+=a[i];
            a2+=a[i]*a[i]%MOD;
            a3+=qmod(a[i],3);
            a1%=MOD,a2%=MOD,a3%=MOD;
//            printf(" %lld --->> %lld \n",tmp,ans);
        }
        printf("%lld\n",ans%MOD);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值