• agc003


    题目连接

    AT2001 [AGC003A] Wanna go back home
    AT2002 [AGC003B] Simplified mahjong
    AT2003 [AGC003C] BBuBBBlesort!
    AT2004 [AGC003D] Anticube
    AT2005 [AGC003E] Sequential operations on Sequence
    AT2006 [AGC003F] Fraction of Fractal


    A

    显然:上下方向要么都不存在,要么都存在,左右同理,否则 NO

    const int N = 114514;
    int n, cnt[5];
    char str[N];
    signed main() {
    	scanf("%s", str + 1) ,n = strlen(str + 1);
    	rep (i, 1, n) 
    		if (str[i] == 'E') ++cnt[0];
    		else if (str[i] == 'W') ++cnt[1];
    		else if (str[i] == 'S') ++cnt[2];
    		else ++cnt[3];
    	if ((cnt[0] && !cnt[1]) || (cnt[1] && !cnt[0]) || (cnt[2] && !cnt[3]) || (cnt[3] && !cnt[2])) puts("No");
    	else puts("Yes");
    }
    

    B

    奇数不太好处理,而把它直接消成 (1) 显然是对的 。然后让每一个 (1) 尽量与后面的匹配即可。

    const int N = 100005;
    int n, a[N];
    LL ans;
    signed main() {
    	n = read();
    	rep (i, 1, n) a[i] = read();
    	rep (i, 1, n) if (a[i] & 1) ans += a[i] >> 1, a[i] &= 1;
    	rep (i, 1, n) 
    		if (a[i] & 1) {
    			ans += a[i] >> 1, a[i] &= 1;
    			if (a[i+1]) --a[i+1], --a[i], ++ans;
    		}
    	rep (i, 1, n) ans += a[i] >> 1;
    	printf("%lld
    ", ans);
    	return 0;
    }
    

    C

    reverse相邻三个数相当于隔一个数swap,swap的两个数的下标奇偶性相同。所以如果这个数应该在的位置(排序完的下标)和它的初始下标奇偶性不同那么计数器加一。

    奇下标到偶下标与偶下标到奇下标均被统计了,但是一次swap就能各减少一个,所以答案要除以 (2)

    const int N = 100005;
    int n, a[N], ans, b[N];
    map<int,int>to;
    signed main() {
    	n = read();
    	rep (i, 1, n) a[i] = b[i] = read();
    	sort (b + 1, b + n + 1);
    	rep (i, 1, n) to[b[i]] = i;
    	rep (i, 1, n) if( (i & 1) != (to[a[i]] & 1)) ++ans;
    	printf("%d
    ",ans >> 1);
    	return 0;
    }
    

    D

    开始不那么显然了。一开始净往图论想,怎么连边复杂度都不对qwq。

    (n=p_1^{x_1}p_2^{x_2}cdots p_c^{x_c},n'=p_1^{x_1\%3}p_2^{x_2\%3}cdots p_c^{x_c\%3},iv_{n'}=p_1^{3-x_1\%3}p_2^{3-x_2\%3}cdots p_c^{3-x_c\%3})

    (n) 归到 (n') 这一类里面去。

    可以发现 (n) 可以分解成唯一确定 (n') ,每一个 (n') 对应着唯一确定的 (iv_{n'}) ,而题目的限制就是 (n') 这一类与 (iv_{n'}) 这两类只能取一类。

    所以直接把每一个 (n') 的数量丢到哈系表里,比较 (num_{n'})(num_{iv_{n'}}) 的大小,取较大的即可。

    应当注意到 (10^{10}) 是没法质因数分解的,只能处理 (le sqrt[3]{n}) 的因数。

    发现把这部分质因子去除之后,(n) 一定是 (p,pq,p^2) 三种形式之一((p,q) 均为质数),我们维护出这个 (n') 以及这个 (n') 对应的 (iv_{n'})

    • 如果分解完的 (n>10^5)

      • 如果 (n=p) ,那么 (iv_{n'}) 会乘上 (p^2) ,那么 (iv_{n'}) 必然大于 (10^{10}) 可以直接舍掉,不用维护。

      • 如果 (n=p^2) ,那么 (iv_{n'}) 会乘上 (p) ,直接乘就好了。判断它是不是完全平方数可以直接用 sqrt(x)*sqrt(x)==x 来判断。

      • 如果 (n=pq) ,那么 (iv_{n'}) 会乘上 (p^2q^2) ,必然大于 (10^{10}) ,可以舍掉。

    所以判断 (n) 分解完是否是完全平方数即可。

    • 如果分解完的 (nle 10^5)(iv_{n'}) 直接乘 (n^2) 即可。

    注意特判分解完 (n)(1) 的情况,只能取 (1) 个。

    const int N=100005;
    int n,ans;
    int pct,pri[N];
    bool vis[N];
    LL a[N];
    map<LL,LL>to;
    map<LL,int>num;
    void Sieve(const int&n){
    	for(int i=2;i<=n;++i){
    		if(!vis[i])pri[++pct]=i;
    		for(int j=1;j<=pct&&i*pri[j]<=n;++j){
    			vis[i*pri[j]]=1;
    			if(i%pri[j]==0)break;
    		}
    	}
    }
    void insert(LL x){
    	static int cnt_1=0;
    	LL pre=x,iv=1;
    	for(int i=1;i<=pct;++i){
    		if(pri[i]>2155)break;
    		if(x%pri[i])continue;
    		LL tmp=1ll*pri[i]*pri[i]*pri[i];
    		while(x%tmp==0)x/=tmp,pre/=tmp;
    		if(x%pri[i])continue;
    		while(x%pri[i]==0)x/=pri[i],tmp/=pri[i];
    		iv*=tmp;
    	}
    	if(x>100000){
    		LL tmp=sqrt(x);
    		if(tmp*tmp==x)iv*=tmp;
    		else iv=-1;
    	}else{
    		iv*=x*x;
    	}
    	if(pre>1)to[pre]=iv,++num[pre];
    	else ans+=cnt_1^1,cnt_1=1;
    }
    signed main(){
    	scanf("%d",&n);
    	Sieve(100000);
    	rep(i,1,n)scanf("%lld",&a[i]),insert(a[i]);
    	for(auto i:to){
    		if(num[i.first]>=num[i.second])ans+=num[i.first],num[i.second]=0;
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    E

    发现只需要维护以 (q_{m}) 结尾的一个上升子序列,只有这个子序列的值才会对答案有贡献。注意初始化 (q_0=n)

    过程中维护每一个值的出现次数不是很方便,考虑维护 ([1,i]) 的出现次数,最后求后缀和就是每一个数的出现次数。

    设这个上升子序列为 (a) ,那么 (a_i) 这个操作的效果就是把 (a_{i-1}) 复制了 (lfloor dfrac{a_i}{a_{i-1}} floor) 次然后加入了 (a_i\% a_{i-1}) 这个前缀。

    复制一个前缀可以考虑对每一个操作记录复制的次数,注意应该倒序操作(其实本质是拓扑排序)

    这个前缀不是很好处理,值域太大,卡了我好久。

    发现这个前缀也可以拆成 (lfloor dfrac{len}{a_t} floor) 次复制加上一个前缀,其中 (len=a_i\%a_{i-1})(a_t)(le len) 的最大的 (a),这个前缀可以直接递归。

    不存在 (t) 则说明 (lenle n) ,那么直接修改后缀和即可。

    可以发现,一个数 (x) 不断 (mod) 一个不大于 (x) 的数不超过 (log x) 次可以变成 (0)

    再算上二分的 (log n) ,复杂度上限 (O(nlog nlog |V|)) ,卡不满。

    const int N=100005;
    int n,tot,m;
    LL q[N],a[N],cpy[N],ans[N];
    void add(LL x,LL tim){
    	if(!x)return;
    	int i=upper_bound(a+1,a+tot+1,x)-a-1;
    	if(!i)ans[x]+=tim;
    	else cpy[i]+=x/a[i]*tim,add(x%a[i],tim);
    }
    signed main(){
    	scanf("%d%d",&n,&m);
    	rep(i,1,m)scanf("%lld",&q[i]);
    	q[0]=n,a[tot=1]=q[m];
    	per(i,m-1,0)if(q[i]<a[tot])a[++tot]=q[i];
    	reverse(a+1,a+tot+1);
    	cpy[tot]=1;
    	per(i,tot,2)add(a[i]%a[i-1],cpy[i]+=a[i+1]/a[i]*cpy[i+1]);
    	ans[a[1]]+=cpy[1]+=a[2]/a[1]*cpy[2];
    	per(i,n,1)ans[i]+=ans[i+1];
    	rep(i,1,n)printf("%lld
    ",ans[i]);
    	return 0;
    }
    

    F

    千万注意一开始格子四联通,不然很难做!!!

    做了好几个小时终于搞出来了/ll

    开始大眼观察法,大概发现了如下性质(想象几个例子即得):

    答案与 #的总个数(cnt)横向相邻的#个数(cnt1)纵向相邻的#个数(cnt2)这个矩阵往右复制,分界线上左右相邻的#个数(x)这个矩阵往下复制,分界线上上下相邻的#个数(y) 有关。

    如果 (x>0,y>0) ,答案必然是 (1)

    如果 (x=0,y=0) ,答案为 (cnt^{k-1})

    还剩两种棘手的情况不是很会,接着大眼观察+推公式无果,于是开始打表找规律。

    打表程序

    char ans[5005][5005],e[5005][5005];
    int n,m,k;
    int qpow(int n,int k){int res=1;for(;k;k>>=1,n*=n)if(k&1)res*=n;return res;}
    void solve(int a,int b,int c,int d,int op,int k){
    	if(!op){
    		rep(i,a,c)rep(j,b,d)ans[i][j]='.';
    		return;
    	}
    	if(k==1){
    		rep(i,a,c)rep(j,b,d)ans[i][j]=e[i-a+1][j-b+1];
    		return;
    	}
    	int lx=qpow(n,k-1),ly=qpow(m,k-1),t=(c-a+1)/lx;
    	rep(i,1,t)rep(j,1,t)solve(a+(i-1)*lx,b+(j-1)*ly,a+i*lx-1,b+j*ly-1,e[i][j]=='#',k-1);
    }
    int F[5005*5005];
    int find(int x){return x==F[x]?x:F[x]=find(F[x]);}
    int id(int x,int y,int m){return (x-1)*m+y;}
    int calc(int n,int m){
    	rep(i,1,n*m)F[i]=i;
    	rep(i,1,n)rep(j,1,m){
    		if(ans[i][j]!='#')continue;
    		if(i>1&&ans[i-1][j]=='#')F[find(id(i-1,j,m))]=find(id(i,j,m));
    		if(i<n&&ans[i+1][j]=='#')F[find(id(i+1,j,m))]=find(id(i,j,m));
    		if(j>1&&ans[i][j-1]=='#')F[find(id(i,j-1,m))]=find(id(i,j,m));
    		if(j<m&&ans[i][j+1]=='#')F[find(id(i,j+1,m))]=find(id(i,j,m));
    	}
    	int res=0;
    	rep(i,1,n)rep(j,1,m)if(ans[i][j]=='#'&&find(id(i,j,m))==id(i,j,m))++res;
    	return res;
    }
    signed main(){
    	n=read(),m=read(),k=read();
    	rep(i,1,n)scanf("%s",e[i]+1);
    	solve(1,1,qpow(n,k),qpow(m,k),1,k);
    	rep(i,1,qpow(n,k)){
    		rep(j,1,qpow(m,k))putchar(ans[i][j]);
    		puts("");
    	}
    	cout<<calc(qpow(n,k),qpow(m,k))<<'
    ';
    }
    
    

    手动构造各种情况得到如下的表以及找到的规律(序列是 (k=1,2,...) 时的答案):

    .#.
    ###
    #.#
    

    1,4,20,112,656

    ((((1 imes 6-2) imes 6-4) imes 6-8) imes 6-16)

    ..#
    ###
    #..
    

    1,3,13,63,313

    ((((1 imes 5-2) imes 5-2) imes5-2) imes 5-2)

    #..
    ###
    .##
    

    1,3,15,87,519

    ((((1 imes6-3) imes 6-3) imes 6-3) imes 6-3)

    .##.
    ####
    #..#
    #..#
    

    1,6,48,444

    (((1 imes 10-4) imes 10-12) imes 10-36)

    .##.
    ####
    #..#
    ##.#
    

    1,1,1,1,1

    ..#.
    ####
    ####
    #...
    

    1,4,28,256

    (((1 imes10-6) imes 10-12) imes 24)

    ....
    .##.
    .##.
    ....
    

    1,4,16

    .#.
    .##
    .#.
    

    1,2,6,22

    (((1 imes 4-2) imes 4-2) imes 4-2)

    规律清晰了起来。

    这里为了方便只讲解 (x=0,y>0) 的情况,(x>0,y=0) 同理。

    首先注意到每次乘的数为 (cnt),想了想很有道理。

    然后结合最后一个数据可以发现,每次减的第一个数是 (cnt2)

    结合所有数据,每次减的数是等比数列,并且公比为 (y)

    规律出来了。怎么维护???

    想到了矩阵乘法。

    定义函数 solve(x,y,z,k)(((((1*x-yz^0)*x-yz^1)*x-yz^2)cdots)*x-yz^k)

    直接矩阵转移:

    [egin{bmatrix} ans,y end{bmatrix} * egin{bmatrix} x,0\-1,z end{bmatrix} = egin{bmatrix} ans imes x-y,y imes z end{bmatrix} ]

    矩阵快速幂求转移矩阵的 (k) 次幂即可。

    复杂度 (O(nm+8log k))

    深深的感受到了矩阵的强大!我这种完全不会矩阵的人居然能自己推出矩阵了!感动啊

    int rdc(){
    	char ch=getchar();
    	while(ch!='#'&&ch!='.')ch=getchar();
    	return ch=='#';
    }
    #define mod 1000000007
    void fmod(int&x){x+=x>>31&mod,x-=mod,x+=x>>31&mod;}
    const int N=1005;
    int n,m,a[N][N];
    int cnt,cnt1,cnt2,x,y,ans;
    LL k;
    int qpow(int n,LL k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*res*n%mod;return res;}
    struct Matrix{
    	int a[2][2];
    	Matrix(){a[0][0]=a[0][1]=a[1][0]=a[1][1]=0;}
    	int*operator [] (const int&k){return a[k];}
    	friend Matrix operator * (const Matrix&a,const Matrix&b){
    		Matrix res;
    		rep(i,0,1)rep(j,0,1)rep(k,0,1)fmod(res.a[i][j]+=1ll*a.a[i][k]*b.a[k][j]%mod);
    		return res;
    	}
    	void e(){
    		a[0][0]=1,a[0][1]=0;
    		a[1][0]=0,a[1][1]=1;
    	}
    }sta,tur;
    Matrix Matrix_pow(Matrix a,LL k){
    	Matrix res;res.e();
    	for(;k;k>>=1,a=a*a)if(k&1)res=res*a;
    	return res;
    }
    int solve(int x,int y,int z,LL k){
    	sta[0][0]=1,sta[0][1]=y;
    	tur[0][0]=x,tur[0][1]=0;
    	tur[1][0]=-1,tur[1][1]=z;
    	tur=Matrix_pow(tur,k);
    	sta=sta*tur;
    	return sta[0][0];
    }
    signed main(){
    	scanf("%d%d%lld",&n,&m,&k);
    	rep(i,1,n)rep(j,1,m)a[i][j]=rdc(),cnt+=a[i][j];
    	rep(i,1,n)rep(j,1,m-1)cnt1+=a[i][j]&&a[i][j+1];
    	rep(i,1,n-1)rep(j,1,m)cnt2+=a[i][j]&&a[i+1][j];
    	rep(i,1,n)x+=a[i][1]&&a[i][m];
    	rep(j,1,m)y+=a[n][j]&&a[1][j];
    	if(x&&y)return puts("1"),0;
    	if(!x&&!y)return printf("%d
    ",qpow(cnt,k-1)),0;
    	if(x&&!y)return printf("%d
    ",solve(cnt,cnt1,x,k-1)),0;
    	if(y&&!x)return printf("%d
    ",solve(cnt,cnt2,y,k-1)),0;
    	return 0;
    }
    
  • 相关阅读:
    【Mysql sql inject】【入门篇】sqli-labs使用 part 3【15-17】
    【Mysql sql inject】【入门篇】SQLi-Labs使用 part 2【12-14】
    【Mysql sql inject】【入门篇】SQLi-Labs使用 part 1【01-11】
    【CTF WEB】ISCC 2016 web 2题记录
    【Mysql sql inject】POST方法BASE64编码注入write-up
    【sql server inject】使用动态查询执行sql语句实例
    【跨站关】网络信息安全攻防学习平台跨站过关的彩蛋
    【sql inject】sql盲注技巧
    【php】随缘php企业网站管理系统V2.0 shownews.php注入漏洞
    ASP.NET新建解决方案和网站
  • 原文地址:https://www.cnblogs.com/zzctommy/p/14181521.html
Copyright © 2020-2023  润新知