2017国家集训队作业Atcoder题目试做

虽然远没有达到这个水平,但是据说Atcoder思维难度大,代码难度小,适合我这种不会打字的选手,所以试着做一做

不知道能做几题啊

在完全自己做出来的题前面打"√“

计数器菌:11/104

agc001_d

如果两个字符确定相等就在中间连一条边,那么所有字符相同就等价于使整个图联通

然后发现至少要\(n-1\)条边,而事实上一个序列贡献的边数最大为\(\frac n 2\)条,而且一旦序列里有一个奇数贡献的边数就会减去\(\frac 1 2\),所以如果原始序列出现\(\gt 2\)个奇数,那么就不可行

一个偶数序列,整体向左平移一个之后,正好全部连起来了

如果有奇数怎么办?因为至多两个奇数,我们把奇数放到两边,中间全是偶数,那么可以像刚才那样做,两边的奇数这样做也符合题意。

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--) ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
} const int maxn=200;
int n,m;
int a[maxn]; int main(){
#ifdef LZT
// freopen("in","r",stdin);
#endif
int num=0;
n=read();m=read();
rep(i,1,m){
a[i]=read();
if(a[i]&1) num++;
}
if(num>2){
puts("Impossible");
return 0;
}
for(int i=1;i<=m;i++)
if(a[i]&1){
if(a[1]&1) swap(a[i],a[m]);
else swap(a[i],a[1]);
}
rep(i,1,m) cout<<a[i]<<' ';
cout<<endl;
a[1]++;a[m]--;
if(a[m]==0) m--;
if(m>=2){
cout<<m<<endl;
rep(i,1,m) cout<<a[i]<<' ';
cout<<endl;
}
else{
if(n<=2){
cout<<1<<endl;
cout<<n<<endl;
}
else{
cout<<2<<endl;
cout<<n-1<<' '<<1<<endl;
}
}
return 0;
}

agc001_e

我们发现答案其实就是要求\(\sum_{i=1}^{n-1}\sum_{j=i+1}^nC_{a_i+a_j+b_i+b_j}^{a_i+a_j}\)

然后知道\(C_{a_i+a_j+b_i+b_j}^{a_i+a_j}\)实际上就是点\((-a_i,-b_i)\)走到\((a_j,b_j)\)的方案数

那么原式等价于求点集\((-a_i,-b_i)\)到点集\((a_i,b_i)\)两两的方案数的和减去所有点走到他对应的对称点的方案数(即\(i=j\)的方案数)除以2(每个方案被算了两次)

所以dp就可以了,可以想象中建立一个超级源点连向所有的\((-a_i,-b_i)\)和超级汇点连向所有的\((a_i,b_i)\),就可以求出方案数

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--) ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
} const int mod=1000000007;
const int maxn=200200;
const int maxm=2200;
int n;
int a[maxn],b[maxn];
int dp[maxm*2][maxm*2];
int flag[maxm*2][maxm*2];
int ans; void pl(int &a,ll b){
a=(a+b%mod)%mod;
}
void mi(int &a,ll b){
a=a-b%mod;
while(a<0) a+=mod;
a=a%mod;
} int main(){
#ifdef LZT
freopen("in","r",stdin);
#endif
n=read();
rep(i,1,n) a[i]=read(),b[i]=read();
rep(i,1,n){
flag[2100-a[i]][2100-b[i]]++;
flag[a[i]+2100][b[i]+2100]++;
}
rep(i,1,4200){
rep(j,1,4200){
pl(dp[i][j],dp[i-1][j]);
pl(dp[i][j],dp[i][j-1]);
if(flag[i][j] && i<=2100 && j<=2100) pl(dp[i][j],flag[i][j]);
if(flag[i][j] && i>=2100 && j>=2100) pl(ans,dp[i][j]*1ll*flag[i][j]);
}
}
memset(dp,0,sizeof(dp));
dp[0][0]=1;
rep(i,0,4200){
rep(j,0,4200){
if(i==0 && j==0) continue;
if(i) pl(dp[i][j],dp[i-1][j]);
if(j) pl(dp[i][j],dp[i][j-1]);
}
}
rep(i,1,n)
mi(ans,dp[a[i]+a[i]][b[i]+b[i]]);
ans=ans*500000004ll%mod;
cout<<ans<<endl;
return 0;
}

agc002_d

先考虑暴力做法,对于一组询问\((x,y,z)\),我们暴力将边从小到大加入图里,当\(x\)所在的连通块点数加\(y\)所在连通块点数(当\(x\)和\(y\)在不同连通块时才加)第一次\(\geq z\)时,当前边的序号就是答案

所以答案是有单调性的,可以二分

然后每一组都二分肯定不行,所以整体二分

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--) ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
} const int maxn=100100;
int n,m,Q;
pii edge[maxn];
struct query
{
int ind;
int a,b,num;
void re(int x){
a=read(),b=read(),num=read();
ind=x;
}
} q[maxn],tmp[maxn];
int ans[maxn],fa[maxn],sz[maxn];
bool ok[maxn];
pii sta[maxn];int cnt; inline int fp(int x){if(x==fa[x]) return x;return fp(fa[x]);} void solve(int l,int r,int le,int ri){
//cout<<l<<' '<<r<<' '<<le<<' '<<ri<<endl;
if(l==r){
rep(i,le,ri) ans[q[i].ind]=l;
int x=edge[l].fi,y=edge[l].se;
x=fp(x);y=fp(y);
if(x!=y){
if(sz[x]>sz[y])swap(x,y);
fa[x]=y;sz[y]+=sz[x];
}
return;
}
int md=(l+r)>>1;cnt=0;
rep(i,l,md){
int x=edge[i].fi,y=edge[i].se;
x=fp(x);y=fp(y);
if(x!=y){
if(sz[x]>sz[y]) swap(x,y);
fa[x]=y;sz[y]+=sz[x];
sta[++cnt]=mp(x,y);
}
}
rep(i,le,ri){
query &nw=q[i];
int a=nw.a,b=nw.b;
//cout<<a<<' '<<b<<endl;
a=fp(a);b=fp(b);
//cout<<i<<' '<<a<<' '<<b<<' ';
int nww=0;
if(a==b) nww=sz[a];else nww=sz[a]+sz[b];
if(nww>=nw.num) ok[i]=1;else ok[i]=0;
//cout<<ok[i]<<endl;
}
int pos=le-1;
rep(i,le,ri)
if(ok[i]) tmp[++pos]=q[i];
pos=ri+1;
rrep(i,ri,le)
if(!ok[i]) tmp[--pos]=q[i];
//cout<<le<<' '<<ri<<' '<<pos<<endl;
rep(i,le,ri) q[i]=tmp[i];
while(cnt){
int x=sta[cnt].fi,y=sta[cnt].se;
fa[x]=x;sz[y]-=sz[x];cnt--;
}
solve(l,md,le,pos-1);solve(md+1,r,pos,ri);
} int main(){
n=read(),m=read();
rep(i,1,m) edge[i].fi=read(),edge[i].se=read();
Q=read();
rep(i,1,Q) q[i].re(i);
rep(i,1,n) sz[i]=1,fa[i]=i;
solve(1,m,1,Q);
rep(i,1,Q) printf("%d\n",ans[i]);
return 0;
} /*
5 6
2 3
4 5
1 2
1 3
1 4
1 5
6
2 4 3
2 4 4
2 4 5
1 3 3
1 3 4
1 3 5
*/

agc002_e

真难想的博弈题

首先先想状态

不知怎么想到把他表示成图形

就是我们先排序 然后把一堆石子想象成一个石子个数*1的矩形。 把矩形从高到低排列变成一个图形。

然后操作就变成了删掉最左边一列或者最下面一行

假设有一个点当前在\((1,1)\),那么每次操作他向右或者向上移动一个,不能移动者输

那么给每个点标记上\(o\)或者\(x\),分别表示必胜和必败

所有最外层的角上(意会)一定都是\(x\)

然后发现当\((x+1,y+1)\)不是最外层的点的时候,\((x,y)\)和\((x+1,y+1)\)的标记相同

所以算法就是先把\((1,1)\)向右上方移动直到边界为止,然后要么向上要么向右,如果都是必败那么就是必败,否则必胜

向上向右因为只有一个方向所以只奇偶性有关

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--) ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
} const int maxn=100100;
int n;
int a[maxn]; int main(){
n=read();
rep(i,1,n) a[i]=read();
sort(a+1,a+n+1);
reverse(a+1,a+n+1);
rep(i,1,n+1){
if(a[i]<i){
i--;
int num=0;
for(int j=i+1;j<=n;j++)
if(a[j]==i) num++;
if(num&1){
puts("First");
return 0;
}
if((a[i]-i)&1) puts("First");
else puts("Second");
return 0;
}
}
}

agc002_f

直接\(\text{dp}\)

把“把每种颜色的最左边的球变为0”理解成“任意前缀的颜色数<0的数量”

然后放球的时候一次把一种颜色的球放完,把“放在后面”变成“插入这么多个新的球”,放完一种颜色的 \(k-1\) 个球之后才能放 \(0\)

然后 \(f[i][j]\) 表示当前放前 \(i\) 种颜色(我们固定先放颜色1,再放颜色2,最后乘上 \(\text{fac[i]}\) 就可以了),还没放的 \(\text{0}\) 的个数是 \(j\) 的方案数

那么首先可以在最前面加一个 \(0\) ,即 \(f[i][j]=f[i][j+1]\)

然后可以新增一种颜色,从 \(f[i-1][j-1]\) 转移过来,我们知道当前的球的个数是 \((i-1)*k-(j-1)\) 个,要放入 \(k-1\) 个新的球,运用插板法,就是 \(C_{(i-1)*k-(j-1)+k-1-1}^{(i-1)*k-j}=C_{i*k-j-1}^{k-2}\) 种方案

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--) ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
} const int mod=1e9+7;
const int maxn=3050;
int n,k;
ll f[maxn][maxn];
ll fac[maxn*maxn],inv[maxn*maxn]; inline int C(int x,int y){
if(x<y) return 0;
return fac[x]*inv[y]%mod*inv[x-y]%mod;
} int main(){
n=read(),k=read();
if(k==1){
puts("1");
return 0;
}
fac[0]=1;
rep(i,1,3000*3000) fac[i]=fac[i-1]*i%mod;
inv[0]=inv[1]=1;
rep(i,2,3000*3000) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
rep(i,1,3000*3000) inv[i]=inv[i]*inv[i-1]%mod;
f[0][0]=1;
rep(i,1,n){
rrep(j,i,0){
f[i][j]=f[i][j+1]%mod;
if(j){
f[i][j]+=f[i-1][j-1]*C(i*k-j-1,k-2)%mod;
f[i][j]%=mod;
}
}
}
cout<<f[n][0]*fac[n]%mod;
return 0;
}

agc003_d

首先把每个数分解质因数,然后把指数模3,称变换之后的数为原来的数的最简数

对于一个最简数,存在一个补数,定义为与最简数相乘是立方数的最小数

对于一个最简数代表的数集和其补数代表的数集,我们选取较大的一个

如果一个数和其补数相同,那么只能选1个

关键在于分解质因数,我们先筛出来3000以内的质数,然后如果一个数把3000以下的数除完之后不为1,那么肯定是完全平方数或者质数,分别判断一下即可(如果是两个3000以上的质数相乘,那么不可能对答案产生贡献,所以可以忽略)

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register ll i=(ll)(j);i<=(ll)(k);i++)
#define rrep(i,j,k) for(register ll i=(ll)(j);i>=(ll)(k);i--) ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
} const ll maxn=100100;
int n;
ll a[maxn];
bool isp[maxn];
int pr[maxn],cnt;
int num[maxn][500];
set<ll> s;
map<ll,int> M;
map<ll,int> ind;
map<ll,int> used; void init(){
memset(isp,1,sizeof(isp));
isp[0]=isp[1]=0;
rep(i,2,3000){
if(isp[i]){
pr[++cnt]=i;
}
for(ll j=1;j<=cnt && i*pr[j]<=3000;j++){
isp[i*pr[j]]=0;
if(i%pr[j]==0) break;
}
}
} bool pd(ll x){
for(ll i=2;i*i<=x;i++){
if(x%i==0) return 0;
}
return 1;
} int main(){
#ifdef LZT
//freopen("in","r",stdin);
#endif
ll ans=0;
n=read();
rep(i,1,n) a[i]=read();
init();
rep(i,1,n){
ll t=a[i];
rep(j,1,cnt){
while(t%pr[j]==0){
t=t/pr[j];
num[i][j]++;
}
num[i][j]%=3;
}
if(t>pr[cnt]){
ll nw=sqrt(t);
if(nw*nw==t){
num[i][cnt+1]=nw;
num[i][cnt+2]=2;
t=1;
}
else{
if(t<=100000 && pd(t)){
num[i][cnt+1]=t;
num[i][cnt+2]=1;
t=1;
}
}
}
ll tt=1;
rep(j,1,cnt){
if(num[i][j]==1) tt=tt*pr[j];
else if(num[i][j]==2) tt=tt*pr[j]*pr[j];
}
if(num[i][cnt+1]){
tt=tt*num[i][cnt+1];
if(num[i][cnt+2]==2) tt=tt*num[i][cnt+1];
}
if(t==1){
s.insert(tt);
M[tt]++;
ind[tt]=i;
}
else ans++;
}
for(set<ll>::iterator it=s.begin();it!=s.end();it++){
ll nw=*it;
if(used[nw]) continue;
used[nw]=1;
ll i=ind[nw];
ll t=1;
for(ll j=1;j<=cnt;j++){
if(num[i][j]==1) t=t*pr[j]*pr[j];
else if(num[i][j]==2) t=t*pr[j];
}
if(num[i][cnt+1]){
if(num[i][cnt+2]==1) t=t*num[i][cnt+1]*num[i][cnt+1];
else t=t*num[i][cnt+1];
}
if(nw==t) ans++;
else ans+=max(M[nw],M[t]);
used[t]=1;
}
cout<<ans<<endl;
return 0;
}

agc003_e

借鉴了\(\text{fizzydavid}\)的代码,写的很精巧

题目就是说有一个数字串S,初始长度为n,是1 2 3 4 …… n,有m次操作,每次操作给你一个正整数a[i],你先把S无穷重复,然后把前a[i]截取出来成为新的S。

求m次操作后,每个数字在S中出现的次数。

我们倒着考虑

首先如果 \(a[i] \ge a[i+1]\) 那么 \(a[i]\) 可以直接删掉,显然没用

所以我们先用单调栈把 \(a\) 变成单调递增的

然后每一次操作等价于把前一次的序列复制几次然后加上一个前缀

我们倒着考虑,用一个 \(\text{pair}\) 表示我们当前给长度为 \(\text{pair.first}\) 的前缀重复加了 \(\text{pair.second}\) 次

那么一开始,考虑最后一次操作,等价于插入 \(\text{pair<a[cnt],1>}\) 就是整个加了1次

然后考虑当前操作的长度为 \(cur\) ,对于每一个存在的 \(\text{pair}\) ,我们会将其更新,因为实际上这个 \(pair\) 可以由当前的 \(\text{cur}\) 得到,所以我们计算当前的 \(cur\) 这个前缀在每一个存在的 \(\text{pair}\) 中一共出现几次,也就是

sum+=nw.se*(nw.fi/cur);

当然如果 \(nw.fi\) 不是\(cur\) 的倍数,那么会出现一个多余的小于 \(cur\) 的前缀,这个前缀出现了 \(nw.se\) 次,长度就是 \(\text{nw.fi%cur}\)

然后我们使用partial sum的方法,最后倒着加起来就好了

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--) ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
} priority_queue<pair<ll,ll> > pq;
ll n,q;
ll st[100100],cnt;
ll ans[100100];
int main(){
#ifdef LZT
// freopen("in","r",stdin);
#endif
n=read(),q=read();
st[++cnt]=n;
for(int i=1;i<=q;i++){
ll x=read();
while(st[cnt]>=x) cnt--;
st[++cnt]=x;
}
pq.push(mp(st[cnt],1));
for(int i=cnt;i>=1;i--){
ll sum=0,cur=st[i];
while(!pq.empty() && pq.top().fi>cur){
pair<ll,ll> nw=pq.top();pq.pop();
sum+=nw.se*(nw.fi/cur);
if(nw.fi%cur) pq.push(mp(nw.fi%cur,nw.se));
}
pq.push(mp(cur,sum));
}
while(!pq.empty()) ans[pq.top().fi]+=pq.top().se,pq.pop();
rrep(i,n,1) ans[i]+=ans[i+1];
rep(i,1,n) printf("%lld \n",ans[i]);
return 0;
}

agc003_f

\(\text{Atcoder}\) 的题果然都是神仙

\(\left\{
\begin{matrix}
b & c \\
0 & d
\end{matrix}
\right\} ^ {k-1}\) 之后 \(b-c\) 就是答案

下面来分析一波:

首先定义 纵向相邻横向相邻

纵向相邻 指一个初始图案存在一列,满足这一列的第一行和最后一行两个格子都是 #

类似的,横向相邻 指一个初始图案存在一行,满足这一行的第一列和最后一列两个格子都是 #

不难发现,如果一个初始图案既 纵向相邻横向相邻,那么他复制多次之后仍旧联通

如果一个初始图案既不 纵向相邻 也不 横向相邻,那么他复制多次之后的联通块数就是黑色格子数的 \(k-1\) 次,因为复制之后不会有连通块合并的情况

剩下的就是只满足其中一个的情况。不失一般性,我们假设初始图案满足 纵向相邻,不满足 横向相邻

考虑当前在 \(k-1\) 层,我们操作一次之后到第 \(k\) 层,中间每个量的变化

令 \(a_{k}\) 表示第 \(k\) 层的连通块个数,\(b_k\) 表示第 \(k\) 层的黑色格子数量,\(c_k\) 表示第 \(k\) 层的图中有多少个上下相邻且都为黑色的格子对,\(d_k\) 表示第 \(k\) 层的图中有多少满足 纵向相邻 的列

那么我们来考虑 \(k-1\) 到 \(k\) 的变化

\(a_k=b_{k-1}-c_{k-1},b_k=b_{k-1}^2,c_k=b_{k-1} \cdot c_{k-1}+c_{k-1} \cdot d_{k-1},d_k=d_{k-1}^2\)

惊讶的发现,他可以用一个矩阵完美地套进去!

首先 \(a\) 这个玩意根本没有用,不记录

考虑 \(2\times 2\) 的矩阵乘法

\(\left\{\matrix{a & b\\c&d}\right\}^2=\left\{\matrix{a^2+bc & ab+bd\\ac+cd & bc+d^2}\right\}\)

那么

\(\left\{\matrix{b_{k-1} & c_{k-1}\\0&d_{k-1}}\right\}^2=\left\{\matrix{b_{k-1}^2 & b_{k-1} \cdot c_{k-1}+c_{k-1} \cdot d_{k-1}\\0 & d_{k-1}^2}\right\}=\left\{\matrix{b_k & c_k \\ 0 & d_k}\right\}\)

然后就做完了。。。

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
#define Debug(...) fprintf(stderr, __VA_ARGS__) ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
} const int maxn=1010;
const int mod=1e9+7;
int n,m;ll k;
char c[maxn][maxn];
char tc[maxn][maxn]; struct Matrix{
int a[3][3];
Matrix(){memset(a,0,sizeof(a));}
void init_one(){a[1][1]=a[2][2]=1;}
Matrix operator * (const Matrix &b) const{
Matrix ret;
rep(i,1,2) rep(j,1,2) rep(k,1,2){
ret.a[i][j]+=(a[i][k]*1ll*b.a[k][j])%mod;
if(ret.a[i][j]>=mod) ret.a[i][j]-=mod;
}
return ret;
}
void pr(){
cout<<a[1][1]<<' '<<a[1][2]<<endl<<a[2][1]<<' '<<a[2][2]<<endl<<endl;
}
} a; Matrix ksm(Matrix a,ll p){
Matrix ret;ret.init_one();
while(p){
if(p&1) ret=ret*a;
p>>=1;
a=a*a;
}
return ret;
} int ksm(int x,ll p){
int ret=1;
while(p){
if(p&1) ret=ret*1ll*x%mod;
p>>=1;
x=x*1ll*x%mod;
}
return ret;
} void work(){
n=read(),m=read(),k=read();
int cnt=0;bool f1=0,f2=0;
rep(i,1,n) rep(j,1,m){
c[i][j]=getchar();
while(c[i][j]!='.' && c[i][j]!='#') c[i][j]=getchar();
}
rep(i,1,n) if(c[i][1]=='#' && c[i][m]=='#') f1=1;
rep(j,1,m) if(c[1][j]=='#' && c[n][j]=='#') f2=1;
if(f2 && !f1){
rep(i,1,n) rep(j,1,m) tc[j][n+1-i]=c[i][j];
swap(n,m);
rep(i,1,n) rep(j,1,m) c[i][j]=tc[i][j];
}
rep(i,1,n){
if(c[i][1]=='#' && c[i][m]=='#') a.a[2][2]++;
rep(j,1,m){
if(c[i][j]=='#') a.a[1][1]++;
if(c[i][j]=='#' && c[i][j-1]=='#') a.a[1][2]++;
}
}
if(!f1 && !f2){
printf("%d\n",ksm(a.a[1][1],k-1));
return;
}
else if(f1 && f2){
puts("1");
return;
}
a=ksm(a,k-1);
printf("%d\n",(a.a[1][1]-a.a[1][2]+mod)%mod);
} int main(){
#ifdef LZT
freopen("in","r",stdin);
#endif work(); #ifdef LZT
Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
}

√agc004_c

去年南外校赛原题,当时不会。。。o(╥﹏╥)o

直接构造,第一列涂红,最后一列涂蓝,剩下的格子奇数行涂红,偶数行涂蓝,原来是紫色的格子都涂上,一定可以

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
#define Debug(...) fprintf(stderr, __VA_ARGS__) ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
} const int maxn=550;
int n,m;
char c[maxn][maxn]; void work(){
n=read(),m=read();
rep(i,1,n) rep(j,1,m){
c[i][j]=getchar();
while(c[i][j]!='.' && c[i][j]!='#') c[i][j]=getchar();
}
rep(i,1,n){
c[i][1]='R',c[i][m]='B';
if(i&1){rep(j,2,m-1) if(c[i][j]=='.') c[i][j]='R';}
else{rep(j,2,m-1) if(c[i][j]=='.') c[i][j]='B';}
}
rep(i,1,n){
rep(j,1,m) if(c[i][j]!='B') putchar('#'); else putchar('.');
puts("");
}
puts(""); rep(i,1,n){
rep(j,1,m) if(c[i][j]!='R') putchar('#'); else putchar('.');
puts("");
}
} int main(){
#ifdef LZT
freopen("in","r",stdin);
#endif work(); #ifdef LZT
Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
}

√agc004_d

水题,不难发现 \(1\) 一定要连到 \(1\),然后根据题目,输入是一棵树,那么可以 \(\text{dfs}\) 一遍,一旦这个点为根的子树内有一个点的深度 \(\geq k\),那么这个点必须连到 \(1\),统计一下即可

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
#define Debug(...) fprintf(stderr, __VA_ARGS__) ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
} const int maxn=100100;
int n,k,ans,a[maxn];
vector<int> G[maxn]; int dfs(int u){
int nw=0;
for(int i=0;i<G[u].size();i++){
int v=G[u][i];
nw=max(nw,dfs(v));
}
nw++;
if(nw==k && a[u]!=1) ans++,nw=0;
return nw;
} void work(){
n=read(),k=read();
rep(i,1,n){
int x=read();
a[i]=x;
if(i==1){
if(x!=1) ans++;
a[i]=1;
}
else G[x].pb(i);
}
dfs(1);
cout<<ans<<endl;
} int main(){
#ifdef LZT
freopen("in","r",stdin);
#endif work(); #ifdef LZT
Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
}

agc004_e

看题解比较好

如果看完了题解的前一半,知道了 \(E\) 移动的矩形可以推出边界矩形,那么就 \(f[i][j][a][b]\) 表示当前 \(E\) 的矩形是 \((i,j)\) 到 \((p,q)\) 时最大答案(用 \(\text{short}\) 存或者滚动),那么枚举四个方向,判断能否扩展,更新一下即可,具体见程序

#include<stdio.h>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cmath>
#include<iostream>
#include<queue>
#include<string>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<long long,long long> pll;
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define rep(i,j,k) for(register int i=(int)(j);i<=(int)(k);i++)
#define rrep(i,j,k) for(register int i=(int)(j);i>=(int)(k);i--)
#define Debug(...) fprintf(stderr, __VA_ARGS__) ll read(){
ll x=0,f=1;char c=getchar();
while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
} const int maxn=102;
int n,m;
short f[maxn][maxn][maxn][maxn];
char s[maxn][maxn];
short a[maxn][maxn]; short ask(int X1,int Y1,int X2,int Y2){
if(X2<X1 || Y2<Y1) return 0;
return a[X2][Y2]-a[X1-1][Y2]-a[X2][Y1-1]+a[X1-1][Y1-1];
} void work(){
n=read(),m=read();
int x=-1,y=-1;
rep(i,1,n){
scanf("%s",s[i]+1);
rep(j,1,m){
if(s[i][j]=='E'){
x=i;y=j;
}
a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+(s[i][j]=='o');
}
}
rrep(i,x,1) rrep(j,y,1) rep(p,x,n) rep(q,y,m){
short &nw=f[i][j][p][q];
short le=max(p-x+1,i),ri=min(p,n-x+i);
short up=max(q-y+1,j),down=min(q,m-y+j);
if(i>1){
short ad=0;
if(p-x<i-1) ad=ask(i-1,up,i-1,down);
f[i-1][j][p][q]=max(f[i-1][j][p][q],(short)(nw+ad));
}
if(j>1){
short ad=0;
if(q-y<j-1) ad=ask(le,j-1,ri,j-1);
f[i][j-1][p][q]=max(f[i][j-1][p][q],(short)(nw+ad));
}
if(p<n){
short ad=0;
if(x-i+p+1<=n) ad=ask(p+1,up,p+1,down);
f[i][j][p+1][q]=max(f[i][j][p+1][q],(short)(nw+ad));
}
if(q<m){
short ad=0;
if(y-j+q+1<=m) ad=ask(le,q+1,ri,q+1);
f[i][j][p][q+1]=max(f[i][j][p][q+1],(short)(nw+ad));
}
}
printf("%d\n",f[1][1][n][m]);
} int main(){
#ifdef LZT
freopen("in","r",stdin);
#endif work(); #ifdef LZT
Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
}

2017国家集训队作业Atcoder题目试做的更多相关文章

  1. 2017国家集训队作业[agc006f]Blackout

    2017国家集训队作业[agc006f]Blackout 题意: 有一个\(N*N\)的网格,一开始有\(M\)个格子被涂黑,给出这\(M\)个格子,和染色操作:如果有坐标为\((x,y),(y,z) ...

  2. 2017国家集训队作业[agc004f]Namori

    2017国家集训队作业[agc004f]Namori 题意: 给你一颗树或环套树,树上有\(N\)个点,有\(M\)条边.一开始,树上的点都是白色,一次操作可以选择一条端点颜色相同的边,使它的端点颜色 ...

  3. 2017国家集训队作业[agc008f]Black Radius

    2017国家集训队作业[agc008f]Black Radius 时隔4个月,经历了省赛打酱油和中考各种被吊打后,我终于回想起了我博客园的密码= = 题意: ​ 给你一棵树,树上有若干个关键点.选中某 ...

  4. 2017国家集训队作业[agc016b]Color Hats

    2017国家集训队作业[agc016b]Color Hats 题意: 有\(N\)个人,每个人有一顶帽子.帽子有不同的颜色.现在,每个人都告诉你,他看到的所有其它人的帽子共有多少种颜色,问有没有符合所 ...

  5. 2017国家集训队作业[agc016e]Poor Turkey

    2017国家集训队作业[agc016e]Poor Turkey 题意: 一开始有\(N\)只鸡是活着的,有\(M\)个时刻,每个时刻有两个数\(X_i,Y_i\),表示在第\(i\)个时刻在\(X_i ...

  6. 2017国家集训队作业[arc082d]Sandglass

    2017国家集训队作业[arc082d]Sandglass 题意: ​ 有一个沙漏,初始时\(A\)瓶在上方,两个瓶子的最大容量都为\(X\)克,沙子流动的速度为\(1g\)每单位时间.给出\(K\) ...

  7. 2017国家集训队作业[arc076d/f][Exhausted?]

    2017国家集训队作业[arc076d/f][Exhausted?] 题意: ​ 有\(N\)个人,\(M\)把椅子,给出\(...L_i.R_i\)表示第\(i\)个人可以选择编号为\(1\sim ...

  8. 2017国家集训队作业[agc006e]Rotate 3x3

    2017国家集训队作业[agc006e]Rotate 3x3 题意: ​ 给你一个\(3*N\)的网格,每次操作选择一个\(3*3\)的网格,旋转\(180^\circ\).问可不可以使每个位置\(( ...

  9. 2017国家集训队作业[agc014d]Black and White Tree

    2017国家集训队作业[agc014d]Black and White Tree 题意: ​ 有一颗n个点的树,刚开始每个点都没有颜色.Alice和Bob会轮流对这棵树的一个点涂色,Alice涂白,B ...

随机推荐

  1. codeforces 的 Codeforces Round #273 (Div. 2) --C Table Decorations

    C. Table Decorations time limit per test 1 second memory limit per test 256 megabytes input standard ...

  2. POJ3279 Fliptile —— 状态压缩 + 模拟

    题目链接:http://poj.org/problem?id=3279 Fliptile Time Limit: 2000MS   Memory Limit: 65536K Total Submiss ...

  3. VS2010关于调用ffmpeg借口出错

    win7 下开发视频服务器,用到ffmpeg,debug版本运行正常,切换到release时,出现"0x00905a4d 处未处理的异常: 0xC0000005: 读取位置 0x00905a ...

  4. android读取apk中已经存在的数据库信息

    在android数据库编程方面,大家有没有遇到过,我要从指定位置的已经存在的数据库来进行操作的问题.之前我尝试了很多方法都没有成功,后来找到了解决的方法.   下面说明下这段代码的意思,第一步先判断在 ...

  5. 谈谈java中静态变量与静态方法在有继承关系的两个类中调用

    谈谈java中静态变量与静态方法在有继承关系的两个类中调用 学习的中如果遇到不明白或者不清楚的的时候,就是自己做些测试,自己去试试,这次我就做一个关于静态变量和静态方法在有继承关系的两个类中的问题测试 ...

  6. 初始化cache_dir(squid)

    sed -i '/adjustFactor/d' /CNCLog/exactTraffic/conf/localTraffic.cfgecho "adjustFactor = '-0.67 ...

  7. AtCoder Grand Contest 007 E:Shik and Travel

    题目传送门:https://agc007.contest.atcoder.jp/tasks/agc007_e 题目翻译 现在有一个二叉树,除了叶子每个结点都有两个儿子.这个二叉树一共有\(m\)个叶子 ...

  8. Android Studio工程Gradle编译报错

    一.环境的搭建: 首先搭建好AndroidStudio环境.我使用的是Ubuntu 12.04系统(由于此机器还要运行其他程序,为避免兼容性问题,暂未更新到最新,而继续沿用此稳定版),java和jdk ...

  9. UVA11624(bfs最短路)

    Joe works in a maze. Unfortunately, portions of the maze have caught on fire, and the owner of the m ...

  10. PTA 哈希查找 除留取余法

    PTA 电话聊天狂人(25 分) 给定大量手机用户通话记录,找出其中通话次数最多的聊天狂人. 输入格式: 输入首先给出正整数N(≤10​5​​),为通话记录条数.随后N行,每行给出一条通话记录.简单起 ...