注意题目是先提供糖果再返还,所以我们不能直接硬除。
按题意暴力模拟即可,复杂度最高只有约 4 × 1 0 5 4\times 10^5 4×105,可以过。
#include
#include
using namespace std;
int N;
struct Node {
char nam[25];
int cst,k;
int cnt;
}A[70+5];
int main() {
freopen("go.in","r",stdin);
freopen("go.out","w",stdout);
scanf("%d\n",&N);
for(int i=1;i<=N;i++)
scanf("%s %d %d\n",A[i].nam,&A[i].cst,&A[i].k);
int tot=0,maxx=-1;
for(int i=1;i<=N;i++) {
// A[i].cnt=A[i].k/(A[i].cst-2);
// tot+=A[i].cnt;
// maxx=max(maxx,A[i].cnt);
while(A[i].k>=A[i].cst) {
A[i].k-=A[i].cst;
A[i].cnt++;
A[i].k+=2;
}
tot+=A[i].cnt;
maxx=max(maxx,A[i].cnt);
}
printf("%d\n",tot);
for(int i=1;i<=N;i++)
if(maxx==A[i].cnt) {
puts(A[i].nam);
break;
}
return 0;
}
首先想到的应该是暴力回溯法。(做法不解释,自己看注释)
我们画一下解答树可以发现一些规律(以样例一为例,如下图所示):
我们在每层节点放一个编号(红字),答案路径为橙色。
不难发现最下一层编号为 X X X的节点就是答案,只要沿着路径往回就可以了。
这个操作可以简单利用除法和取余来完成,具体详见代码。
#include
#include
#include
#include
using namespace std;
const int Maxn=500;
int N,M,K,X;
char S[Maxn+5];
char s[Maxn+5][30];
/*
vector ans;
int cnt;
void DFS(int num) {
if(num>M) {
cnt++;
if(cnt==X) {
for(int i=0,j=0;i
char ans[Maxn+5];
int main() {
freopen("tavan.in","r",stdin);
freopen("tavan.out","w",stdout);
scanf("%d %d %d %d\n",&N,&M,&K,&X);
scanf("%s\n",S);
for(int i=1;i<=M;i++) {
scanf("%s\n",s[i]);
sort(s[i],s[i]+K);
}
// DFS(1);
for(int i=M;i>=1;i--) {
int t=(X-1)%K;
ans[i]=s[i][t];
if(X%K==0)X/=K;
else X=X/K+1;
}
for(int i=0,j=1;i<N;i++) {
if(S[i]=='#') {
putchar(ans[j]);
j++;
} else putchar(S[i]);
}
return 0;
}
这题我们可以贪心地从两边往中间走,不符回文就直接加就行了。
#include
#include
using namespace std;
typedef long long ll;
const int Maxn=1e6;
int N;
ll A[Maxn+5];
int main() {
freopen("nizin.in","r",stdin);
freopen("nizin.out","w",stdout);
scanf("%d",&N);
for(int i=1;i<=N;i++)
scanf("%d",&A[i]);
int tot=0;
int l=1,r=N;
while(l<=r) {
if(A[l]==A[r]) {
l++,r--;
} else if(A[l]<A[r]) {
A[l+1]+=A[l];
l++,tot++;
} else if(A[l]>A[r]) {
A[r-1]+=A[r];
r--,tot++;
}
}
printf("%d\n",tot);
return 0;
}
这题真的是脑洞大开。。。。
我们分两种情况讨论:
对于奇数的矩阵,只需将 1 , 2 , 3 , … , N 2 1,2,3,\ldots,N^2 1,2,3,…,N2挨个填进去就可以了。
对于偶数的矩阵:
当 N = 2 N=2 N=2时,手推一下就可以发现不存在这样的矩阵。
当 N N N等于其他数时,我们可以采用如下步骤构造一个矩阵:
接下来说明这个方法的合理性:
从第 1 1 1行到第 N − 1 N-1 N−1行,各个数字显然是不相等的,且该行平均数都在于第 N − 1 N-1 N−1列上。
根据构造方法,每一列的平均数也在第 N − 1 N-1 N−1行上。
我们可以知道,第 N − 1 N-1 N−1行最后一个数字是 N ( N − 1 ) 2 2 \frac{N(N-1)^2}{2} 2N(N−1)2,第 N N N行第一个数字是 1 + ( N − 2 ) ( N − 1 ) N ( N + 1 ) 2 1+\frac{(N-2)(N-1)N(N+1)}{2} 1+2(N−2)(N−1)N(N+1),画一下它们的函数图像:(如下图)
可以看出两函数有一个交点 A ( 2 , 1 ) A(2,1) A(2,1),且 g ( x ) g(x) g(x)增长速度明显要比 f ( x ) f(x) f(x)大,所以我们不必担心有重复的数字。
接下来说明最大的数字不会超过 1 0 9 10^9 109:
我们显然可以发现,第 N N N行第 N N N个数字是最大的,手推一下可以发现最后一个数的解析式为 ( N − 1 ) N ( N 2 − N − 1 ) 2 \frac{(N-1)N(N^2-N-1)}{2} 2(N−1)N(N2−N−1),把极限数据 100 100 100带进去得到结果 49000050 49000050 49000050没有超过 1 0 9 10^9 109。
#include
#include
using namespace std;
int a[100+5];
int main() {
freopen("prosjecni.in","r",stdin);
freopen("prosjecni.out","w",stdout);
int N;
scanf("%d",&N);
if(N%2) {
for(int i=1;i<=N;i++) {
for(int j=1;j<=N;j++)
printf("%d ",(i-1)*N+j);
puts("");
}
} else {
if(N==2) {
puts("-1");
return 0;
} else {
int t=(N-1)*N/2;
for(int i=1;i<N;i++)
a[i]=i;
a[N]=t;
for(int i=1;i<N;i++) {
for(int j=1;j<=N;j++) {
printf("%d ",a[j]);
a[j]+=t;
}
puts("");
}
int tmp=1+(N-2)*(N+1)/2*t-a[1];
for(int i=1;i<=N;i++)
printf("%d ",a[i]+tmp);
puts("");
}
}
return 0;
}
附送我考试时手写的一个简单的special judge (不能加到lemon中去):
#include
#include
#include
using namespace std;
const int Maxn=100;
int N;
int T[Maxn+5][Maxn+5];
int main() {
scanf("%d",&N);
for(int i=1;i<=N;i++) {
for(int j=1;j<=N;j++)
scanf("%d",&T[i][j]);
}
for(int i=1;i<=N;i++) {
int t=0;
for(int j=1;j<=N;j++)
t+=T[i][j];
t/=N;
bool is_find=false;
for(int j=1;j<=N;j++)
if(t==T[i][j]) {
is_find=true;
break;
}
if(is_find==false) {
puts("WA");
return 0;
}
}
for(int j=1;j<=N;j++) {
int t=0;
for(int i=1;i<=N;i++)
t+=T[i][j];
t/=N;
bool is_find=false;
for(int i=1;i<=N;i++)
if(t==T[i][j]) {
is_find=true;
break;
}
if(is_find==false) {
puts("WA");
return 0;
}
}
puts("AC");
system("pause");
return 0;
}
这题真的绕。。。。
我们先不管操作1,2。
考虑操作3,这一句话相当于将一堆有关联的东西连在一起并互相替换。这有点像并查集。所以我们就试一下吧。
如何证明排序?我们可以尝试 O ( N 2 ) O(N^2) O(N2)暴力查询,但显然是要超时的。。。
其实我们只需要记录能交换的位置上的数,与排好的对应位置上的数比较,只要有相同数量的数,我们就可以认为这个“云”是好的,即这几个位置可以排好序。
所以我们可以考虑哈希。。。如此对于两个云我们就可以 O ( 1 ) O(1) O(1)比较了。
所以,并查集只是一个载体。。。
对于操作1,我们应先将对应位置上的数从哈希表中删掉,并交换,最后不要忘掉再加回去。
对于操作2,我们在合并时把对应的哈希值加上即可。
对于操作3,我们只需利用查找两组对应哈希值相减是否都为0即可。
对应操作4,我们需要找到一个如下所示的云:
这四个云必须满足 h 1 + h 3 = h 2 + h 4 h_1+h_3=h_2+h_4 h1+h3=h2+h4,直接枚举不太好,我们移项一下:变为 h 1 − h 2 = − ( h 3 − h 4 ) h_1-h_2=-(h_3-h_4) h1−h2=−(h3−h4),这样我们就可以将对应的云的哈希值相减存在map
里,最后数一遍就行了。
具体实现还是看代码吧。。。
#include
#include
#include
using namespace std;
typedef long long ll;
const int Maxn=1e6;
const int Hash=1e7+7;
const int Mod=1e9+7;
int N,P;
int A[Maxn+5];
int Q[Maxn+5];
ll powt[Maxn+5];
ll p[Maxn+5],q[Maxn+4];
map<ll,int> diff;
int num[Maxn+5];
ll totpair;
void adddiff(int dir,ll dif,int n) {
if(dif!=0) {
totpair+=dir*n*diff[-dif];
}
diff[dif]+=dir*n;
}
void add(int dir,int u,int pos,int val) {
p[u]+=dir*powt[pos];
q[u]+=dir*powt[val];
num[u]+=dir;
}
int fa[Maxn+5];
int find(int u) {
if(fa[u]==u)return fa[u];
return fa[u]=find(fa[u]);
}
void merge(int a,int b) {
int u=find(a),v=find(b);
if(u==v)return;
adddiff(-1,p[u]-q[u],num[u]);
adddiff(-1,p[v]-q[v],num[v]);
fa[v]=u;
p[u]+=p[v];
q[u]+=q[v];
num[u]+=num[v];
adddiff(1,p[u]-q[u],num[u]);
}
int main() {
freopen("zamjene.in","r",stdin);
freopen("zamjene.out","w",stdout);
scanf("%d %d",&N,&P);
for(int i=1;i<=N;i++) {
scanf("%d",&A[i]);
fa[i]=i;
Q[i]=A[i];
num[i]=1;
}
sort(Q+1,Q+N+1);
powt[0]=1;
for(int i=1;i<=Maxn;i++)
powt[i]=powt[i-1]*Hash;
for(int i=1;i<=N;i++) {
q[i]=powt[A[i]];
p[i]=powt[Q[i]];
adddiff(1,p[i]-q[i],num[i]);
}
while(P--) {
int op;
scanf("%d",&op);
if(op==1) {
int a,b;
scanf("%d %d",&a,&b);
int u=find(a),v=find(b);
if(u==v)
swap(A[a],A[b]);
else {
adddiff(-1,p[u]-q[u],num[u]);
adddiff(-1,p[v]-q[v],num[v]);
add(-1,find(a),a,A[a]);
add(-1,find(b),b,A[b]);
swap(A[a],A[b]);
add(1,find(a),a,A[a]);
add(1,find(b),b,A[b]);
adddiff(1,p[u]-q[u],num[u]);
adddiff(1,p[v]-q[v],num[v]);
}
} else if(op==2) {
int a,b;
scanf("%d %d",&a,&b);
merge(a,b);
} else if(op==3) {
puts(diff[0]==N?"DA":"NE");
} else if(op==4) {
printf("%lld\n",totpair);
}
}
return 0;
}
对于深度大于 K K K的点,我们都可以舍掉,因为无论玩家怎么放,硬币最多向下 K K K次。
我们称深度为 K K K的结点为叶结点,并从左向右依次编号为 1 , 2 , … , N 1,2,\ldots ,N 1,2,…,N。。.。。(以下省略一万字)
(题解过于玄学,为避免阅读不适就省略,文后附官方题解截图,原比赛讨论链接)
简而言之,我们通过贪心的方法可以发现,当 K 2 ≥ N K^2\ge N K2≥N时,无论如何都是有解的。
当 K 2 < N K^2<N K2<N时,我们可以利用状压DP求出来,这部分可以参考lsk大佬的后半部分题解。
#include
#include
#include
#include
using namespace std;
const int Maxn=400;
vector<int> G[Maxn+5];
void addedge(int u,int v) {
G[u].push_back(v);
G[v].push_back(u);
}
int N,K;
int cnt;
int dep[Maxn+5];
int fa[Maxn+5],l[Maxn+5],r[Maxn+5];
void DFS(int u,int parent,int depth) {
dep[u]=depth,fa[u]=parent;
if(depth==K) {
l[u]=cnt++,r[u]=cnt;
return;
}
l[u]=cnt;
for(int i=0;i<(int)G[u].size();i++) {
int v=G[u][i];
if(v==parent)continue;
DFS(v,u,depth+1);
}
r[u]=cnt;
}
bool f[Maxn+1][(1<<20)+1];
vector<int> h[Maxn+5];
bool DP() {
f[0][0]=true;
for(int i=1;i<=N;i++)
if(dep[i])
h[r[i]].push_back(i);
for(int i=1;i<=cnt;i++)
for(int j=0;j<(int)h[i].size();j++)
for(int s=0;s<(1<<K);s++)
if(s&(1<<(dep[h[i][j]]-1)))
f[i][s]|=f[l[h[i][j]]][s^(1<<(dep[h[i][j]]-1))];
for(int j=0;j<(1<<K);j++)
if(f[cnt][j])return true;
return false;
}
int main() {
freopen("burza.in","r",stdin);
freopen("burza.out","w",stdout);
scanf("%d %d",&N,&K);
for(int i=1;i<N;i++) {
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v);
}
if(K*K>=N) {
puts("DA");
return 0;
}
DFS(1,-1,0);
puts(DP()?"DA":"NE");
return 0;
}